View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.util.data;
23  
24  import static org.opencastproject.util.data.Tuple.tuple;
25  
26  import com.entwinemedia.fn.data.Opt;
27  
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.Iterator;
31  import java.util.List;
32  
33  /**
34   * The option type encapsulates on optional value. It contains either some value or is empty. Please make sure to NEVER
35   * wrap null into a some. Instead use none.
36   */
37  // todo clean up the mix of abstract methods and concrete implementations based on the isSome() decision
38  public abstract class Option<A> implements Iterable<A> {
39    private Option() {
40    }
41  
42    /** Safe decomposition of the option type. */
43    public abstract <B> B fold(Match<A, B> visitor);
44  
45    public abstract Option<A> foreach(Function<? super A, Void> f);
46  
47    public abstract <B> Option<B> fmap(Function<? super A, ? extends B> f);
48  
49    public <B> Option<B> map(Function<? super A, ? extends B> f) {
50      return fmap(f);
51    }
52  
53    /** Monadic bind operation <code>m a -&gt; (a -&gt; m b) -&gt; m b</code>. */
54    public abstract <B> Option<B> bind(Function<A, Option<B>> f);
55  
56    public <B> Option<B> flatMap(Function<A, Option<B>> f) {
57      return bind(f);
58    }
59  
60    public abstract boolean isSome();
61  
62    public boolean isNone() {
63      return !isSome();
64    }
65  
66    /** If this is none return <code>none</code> else this. */
67    public Option<A> orElse(Option<A> none) {
68      return isSome() ? this : none;
69    }
70  
71    /** Lazy version of {@link #orElse(Option)}. */
72    public Option<A> orElse(Function0<Option<A>> none) {
73      return isSome() ? this : none.apply();
74    }
75  
76    /** Throw <code>none</code> if none. */
77    public <T extends Throwable> Option<A> orError(T none) throws T {
78      if (isSome())
79        return this;
80      else
81        throw none;
82    }
83  
84    public <B> Option<Tuple<A, B>> and(Option<B> b) {
85      if (isSome() && b.isSome()) {
86        return some(tuple(get(), b.get()));
87      } else {
88        return none();
89      }
90    }
91  
92    /** Get the contained value or throw an exception. */
93    public abstract A get();
94  
95    /** Get the contained value in case of being "some" or return parameter <code>none</code> otherwise. */
96    public abstract A getOrElse(A none);
97  
98    /** Get the contained value in case of being "some" or return the result of evaluating <code>none</code> otherwise. */
99    public abstract A getOrElse(Function0<A> none);
100 
101   /** To interface with legacy applications or frameworks that still use <code>null</code> values. */
102   public abstract A getOrElseNull();
103 
104   /** Transform an option into a list, either with a single element or an empty list. */
105   public abstract List<A> list();
106 
107   public abstract Opt<A> toOpt();
108 
109   @Override
110   public abstract int hashCode();
111 
112   @Override
113   public abstract boolean equals(Object o);
114 
115   // -- constructor functions
116 
117   /** Create a new some. */
118   public static <A> Option<A> some(final A a) {
119     if (a == null)
120       throw new Error("null must not be wrapped in a some");
121     return new Option<A>() {
122       @Override
123       public <B> B fold(Match<A, B> visitor) {
124         return visitor.some(a);
125       }
126 
127       @Override
128       public Option<A> foreach(Function<? super A, Void> f) {
129         f.apply(a);
130         return this;
131       }
132 
133       @Override
134       public <B> Option<B> fmap(Function<? super A, ? extends B> f) {
135         B b = f.apply(a);
136         return some(b);
137       }
138 
139       @Override
140       public <B> Option<B> bind(Function<A, Option<B>> f) {
141         return f.apply(a);
142       }
143 
144       @Override
145       public boolean isSome() {
146         return true;
147       }
148 
149       @Override
150       public A get() {
151         return a;
152       }
153 
154       @Override
155       public A getOrElse(A none) {
156         return a;
157       }
158 
159       @Override
160       public A getOrElse(Function0<A> none) {
161         return a;
162       }
163 
164       @Override
165       public A getOrElseNull() {
166         return a;
167       }
168 
169       @Override
170       public Iterator<A> iterator() {
171         return Collections.singletonList(a).iterator();
172       }
173 
174       @Override
175       public List<A> list() {
176         return Collections.singletonList(a);
177       }
178 
179       @Override
180       public Opt<A> toOpt() {
181         return Opt.some(a);
182       }
183 
184       @Override
185       public int hashCode() {
186         // since an Option should NEVER contain any null this is safe
187         return a.hashCode();
188       }
189 
190       @Override
191       public boolean equals(Object o) {
192         if (o instanceof Option) {
193           Option<?> opt = (Option<?>) o;
194           // since an Option should NEVER contain any null this is safe
195           return opt.isSome() && a.equals(opt.get());
196         } else {
197           return false;
198         }
199       }
200 
201       @Override
202       public String toString() {
203         return "Some(" + a + ")";
204       }
205     };
206   }
207 
208   /** Create a new none. */
209   public static <A> Option<A> none() {
210     return new Option<A>() {
211       @Override
212       public <B> B fold(Match<A, B> visitor) {
213         return visitor.none();
214       }
215 
216       @Override
217       public Option<A> foreach(Function<? super A, Void> f) {
218         return this;
219       }
220 
221       @Override
222       public <B> Option<B> fmap(Function<? super A, ? extends B> f) {
223         return none();
224       }
225 
226       @Override
227       public <B> Option<B> bind(Function<A, Option<B>> f) {
228         return none();
229       }
230 
231       @Override
232       public boolean isSome() {
233         return false;
234       }
235 
236       @Override
237       public A get() {
238         throw new IllegalStateException("a none does not contain a value");
239       }
240 
241       @Override
242       public A getOrElse(A none) {
243         return none;
244       }
245 
246       @Override
247       public A getOrElse(Function0<A> none) {
248         return none.apply();
249       }
250 
251       @Override
252       public A getOrElseNull() {
253         return null;
254       }
255 
256       @Override
257       public Iterator<A> iterator() {
258         return new ArrayList<A>().iterator();
259       }
260 
261       @Override
262       public List<A> list() {
263         return Collections.emptyList();
264       }
265 
266       @Override
267       public Opt<A> toOpt() {
268         return Opt.none();
269       }
270 
271       @Override
272       public int hashCode() {
273         return -1;
274       }
275 
276       @Override
277       public boolean equals(Object o) {
278         return o instanceof Option && ((Option) o).isNone();
279       }
280 
281       @Override
282       public String toString() {
283         return "None";
284       }
285     };
286   }
287 
288   /**
289    * Create a none with the type of <code>example</code>. This saves some nasty typing, e.g.
290    * <code>Option.&lt;String&gt;none()</code> vs. <code>none("")</code>.
291    * <p>
292    * Please note that this constructor is only due to Java's insufficient type inference.
293    */
294   public static <A> Option<A> none(A example) {
295     return none();
296   }
297 
298   /** Create a none with the given type. */
299   public static <A> Option<A> none(Class<A> clazz) {
300     return none();
301   }
302 
303   /** Wrap an arbitrary object into an option with <code>null</code> being mapped to none. */
304   public static <A> Option<A> option(A a) {
305     if (a != null)
306       return some(a);
307     else
308       return none();
309   }
310 
311   /** Convert an <code>Opt</code> into an <code>Option</code>. */
312   public static <A> Option<A> fromOpt(Opt<A> a) {
313     for (A x : a) {
314       return some(x);
315     }
316     return none();
317   }
318 
319   /**
320    * Use this function in <code>getOrElse</code> if it is an error being none.
321    *
322    * @deprecated use {@link #orError(Throwable)} or {@link #orElse(Function0)} instead since it saves the need for
323    *             creating new objects just for the sake of type soundness. Java unfortunately lacks a bottom type.
324    */
325   @Deprecated
326   public static <A> Function0<A> error(final String message) {
327     return new Function0<A>() {
328       @Override
329       public A apply() {
330         throw new Error(message);
331       }
332     };
333   }
334 
335   /**
336    * Create an equals function.
337    *
338    * <pre>
339    *   some("abc").map(eq("bcd")).getOrElse(false) // false
340    *   some("abc").map(eq("abc")).getOrElse(false) // true
341    * </pre>
342    */
343   public static Function<String, Boolean> eq(final String compare) {
344     return new Function<String, Boolean>() {
345       @Override
346       public Boolean apply(String s) {
347         return compare.equals(s);
348       }
349     };
350   }
351 
352   public interface Match<A, B> {
353     B some(A a);
354 
355     B none();
356   }
357 
358   /** Effect match. */
359   public abstract static class EMatch<A> implements Match<A, Void> {
360     @Override
361     public final Void some(A a) {
362       esome(a);
363       return null;
364     }
365 
366     @Override
367     public final Void none() {
368       enone();
369       return null;
370     }
371 
372     protected abstract void esome(A a);
373 
374     protected abstract void enone();
375   }
376 }