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  package org.opencastproject.assetmanager.api;
22  
23  import static java.lang.String.format;
24  
25  import org.opencastproject.assetmanager.api.fn.Product;
26  
27  import java.util.Date;
28  import java.util.Objects;
29  import java.util.function.Function;
30  
31  import javax.annotation.Nonnull;
32  import javax.annotation.ParametersAreNonnullByDefault;
33  import javax.annotation.concurrent.Immutable;
34  
35  /**
36   * A property value.
37   * <p>
38   * The wrapped type is not exposed as a generic type parameter since {@link Value}s appear in
39   * contexts like lists where this type information cannot be preserved.
40   * To access the wrapped type one can choose between two options.
41   * If the type is known, use {@link #get(ValueType)}.
42   * If the type is not known, safely decompose the value with {@link #decompose(Fn, Fn, Fn, Fn, Fn)}.
43   * </p>
44   * The value type is a sum type made up from
45   * <ul>
46   * <li>{@link StringValue}
47   * <li>{@link DateValue}
48   * <li>{@link LongValue}
49   * <li>{@link BooleanValue}
50   * </ul>
51   * <p>
52   * Use one of the various <code>mk(..)</code> constructors to create a new {@link Value}.
53   *
54   * @see Property
55   */
56  @ParametersAreNonnullByDefault
57  public abstract class Value {
58    public static final StringType STRING = new StringType();
59    public static final DateType DATE = new DateType();
60    public static final LongType LONG = new LongType();
61    public static final BooleanType BOOLEAN = new BooleanType();
62    public static final VersionType VERSION = new VersionType();
63    // TODO: rename to UNKNOWN
64    public static final UntypedType UNTYPED = new UntypedType();
65  //  public static final Class<UntypedValue> UNTYPED = UntypedValue.class;
66  
67    private Value() {
68    }
69  
70    /** Get the wrapped value. */
71    public abstract Object get();
72  
73    /**
74     * Get the wrapped value in a type safe way. Use this method if you are
75     * sure about the contained value type. Otherwise consider the use
76     * of {@link #decompose(Fn, Fn, Fn, Fn, Fn)}.
77     *
78     * @param ev
79     *         Evidence type. The type parameter <code>A</code> of the evidence type
80     *         must match the type of the wrapped value. Any other value will result
81     *         in an exception being thrown.
82     * @throws java.lang.RuntimeException
83     *         if the passed evidence <code>ev</code> does not match the type of the wrapped value
84     */
85    public final <A> A get(ValueType<A> ev) {
86      if (getType().getClass().equals(ev.getClass())) {
87        return (A) get();
88      } else {
89        throw new RuntimeException(this + " is not a " + ev.getClass().getSimpleName());
90      }
91    }
92  
93    public final ValueType<?> getType() {
94      return decompose(new Function<String, ValueType<?>>() {
95        @Override public ValueType<?> apply(String a) {
96          return STRING;
97        }
98      }, new Function<Date, ValueType<?>>() {
99        @Override public ValueType<?> apply(Date a) {
100         return DATE;
101       }
102     }, new Function<Long, ValueType<?>>() {
103       @Override public ValueType<?> apply(Long a) {
104         return LONG;
105       }
106     }, new Function<Boolean, ValueType<?>>() {
107       @Override public ValueType<?> apply(Boolean a) {
108         return BOOLEAN;
109       }
110     }, new Function<Version, ValueType<?>>() {
111       @Override public ValueType<?> apply(Version a) {
112         return VERSION;
113       }
114     });
115   }
116 
117   /**
118    * Decompose (or pattern match) the value instance. Provide a function to handle each possible type.
119    * Use {@link #doNotMatch()} as a placeholder that yields an error.
120    */
121   public final <A> A decompose(
122       Function<? super String, ? extends  A> stringValue,
123       Function<? super Date, ? extends A> dateValue,
124       Function<? super Long, ? extends A> longValue,
125       Function<? super Boolean, ? extends A> booleanValue,
126       Function<? super Version, ? extends A> versionValue
127   ) {
128     if (this instanceof StringValue) {
129       return  stringValue.apply(((StringValue) this).get());
130     } else if (this instanceof DateValue) {
131       return dateValue.apply(((DateValue) this).get());
132     } else if (this instanceof LongValue) {
133       return longValue.apply(((LongValue) this).get());
134     } else if (this instanceof BooleanValue) {
135       return booleanValue.apply(((BooleanValue) this).get());
136     } else if (this instanceof VersionValue) {
137       return versionValue.apply(((VersionValue) this).get());
138     } else {
139       // catch bug
140       throw new Error("Unexhaustive match: " + this);
141     }
142   }
143 
144   //
145 
146   /**
147    * Use as a placeholder that yields an error in
148    * value decomposition.
149    *
150    * @see #decompose(Fn, Fn, Fn, Fn, Fn)
151    */
152   public static <B> Function<Object, B> doNotMatch() {
153     return new Function<Object, B>() {
154       @Override public B apply(Object a) {
155         throw new Error("Unexhaustive match: " + a);
156       }
157     };
158   }
159 
160   /* -------------------------------------------------------------------------------------------------------------- */
161 
162   //
163   // Type evidence and factory classes
164   //
165 
166   /**
167    * ValueType gives evidence that type <code>A</code> is suitable for the creation
168    * of a {@link Value}.
169    * <p>
170    * This is a more advanced version of the usual <code>Class&lt;A&gt;</code> idiom.
171    * A <code>ValueType</code> is also a constructor for {@link TypedValue}s of
172    * the same type <code>A</code>.
173    *
174    * @param <A> the type to give evidence of
175    */
176   public abstract static class ValueType<A> {
177     /** It should not be possible to inherit from outside class {@link Value}. */
178     private ValueType() {
179     }
180 
181     public abstract TypedValue<A> mk(A a);
182 
183     public abstract <B> B match(
184             Product<? extends B> stringType,
185             Product<? extends B> dateType,
186             Product<? extends B> longType,
187             Product<? extends B> booleanType,
188             Product<? extends B> versionType);
189   }
190 
191   public static final class StringType extends ValueType<String> {
192     @Override public TypedValue<String> mk(String a) {
193       return Value.mk(a);
194     }
195 
196     @Override public <B> B match(
197             Product<? extends B> stringType,
198             Product<? extends B> dateType,
199             Product<? extends B> longType,
200             Product<? extends B> booleanType,
201             Product<? extends B> versionType) {
202       return stringType.get1();
203     }
204   }
205 
206   public static final class DateType extends ValueType<Date> {
207     @Override public TypedValue<Date> mk(Date a) {
208       return Value.mk(a);
209     }
210 
211     @Override public <B> B match(
212             Product<? extends B> stringType,
213             Product<? extends B> dateType,
214             Product<? extends B> longType,
215             Product<? extends B> booleanType,
216             Product<? extends B> versionType) {
217       return dateType.get1();
218     }
219   }
220 
221   public static final class LongType extends ValueType<Long> {
222     @Override public TypedValue<Long> mk(Long a) {
223       return Value.mk(a);
224     }
225 
226     @Override public <B> B match(
227             Product<? extends B> stringType,
228             Product<? extends B> dateType,
229             Product<? extends B> longType,
230             Product<? extends B> booleanType,
231             Product<? extends B> versionType) {
232       return longType.get1();
233     }
234   }
235 
236   public static final class BooleanType extends ValueType<Boolean> {
237     @Override public TypedValue<Boolean> mk(Boolean a) {
238       return Value.mk(a);
239     }
240 
241     @Override public <B> B match(
242             Product<? extends B> stringType,
243             Product<? extends B> dateType,
244             Product<? extends B> longType,
245             Product<? extends B> booleanType,
246             Product<? extends B> versionType) {
247       return booleanType.get1();
248     }
249   }
250 
251   public static final class VersionType extends ValueType<Version> {
252     @Override public TypedValue<Version> mk(Version a) {
253       return Value.mk(a);
254     }
255 
256     @Override public <B> B match(
257             Product<? extends B> stringType,
258             Product<? extends B> dateType,
259             Product<? extends B> longType,
260             Product<? extends B> booleanType,
261             Product<? extends B> versionType) {
262       return versionType.get1();
263     }
264   }
265 
266   public static final class UntypedType extends ValueType<Object> {
267     @Override public TypedValue<Object> mk(Object a) {
268       throw new RuntimeException("Cannot create an untyped value");
269     }
270 
271     @Override public <B> B match(
272             Product<? extends B> stringType,
273             Product<? extends B> dateType,
274             Product<? extends B> longType,
275             Product<? extends B> booleanType,
276             Product<? extends B> versionType) {
277       throw new RuntimeException("Cannot match an untyped value type");
278     }
279   }
280 
281   /* -------------------------------------------------------------------------------------------------------------- */
282 
283   //
284   // Value classes
285   //
286 
287   /** Helper type to reduce boilerplate code. */
288   // CHECKSTYLE:OFF -> class shall be public but not the constructor
289   public static class TypedValue<A> extends Value {
290     private final A value;
291 
292     /** It should not be possible to inherit from outside class {@link Value}. */
293     private TypedValue(@Nonnull A value) {
294       this.value = value;
295     }
296 
297     @Override public A get() {
298       return value;
299     }
300 
301     @Override public int hashCode() {
302       return Objects.hash(value);
303     }
304 
305     // generic implementation of equals
306     // since all wrapped types cannot equal each other this is safe
307     @Override public boolean equals(Object that) {
308       return (this == that) || (that instanceof TypedValue && eqFields((TypedValue) that));
309     }
310 
311     private boolean eqFields(TypedValue that) {
312       return Objects.equals(value, that.value);
313     }
314 
315     @Override public String toString() {
316       return format("%s(%s)", getClass().getSimpleName(), value);
317     }
318   }
319   // CHECKSTYLE:ON
320 
321   /**
322    * A value of type {@link String}.
323    */
324   @Immutable
325   public static final class StringValue extends TypedValue<String> {
326     public StringValue(@Nonnull String value) {
327       super(value);
328     }
329   }
330 
331   /**
332    * A value of type {@link java.util.Date}.
333    */
334   public static final class DateValue extends TypedValue<Date> {
335     public DateValue(@Nonnull Date value) {
336       super(value);
337     }
338   }
339 
340   /**
341    * A value of type {@link java.lang.Long}.
342    */
343   @Immutable
344   public static final class LongValue extends TypedValue<Long> {
345     public LongValue(@Nonnull Long value) {
346       super(value);
347     }
348   }
349 
350   /**
351    * A value of type {@link java.lang.Boolean}.
352    */
353   @Immutable
354   public static final class BooleanValue extends TypedValue<Boolean> {
355     public BooleanValue(@Nonnull Boolean value) {
356       super(value);
357     }
358   }
359 
360   /**
361    * A value of type {@link Version}.
362    */
363   @Immutable
364   public static final class VersionValue extends TypedValue<Version> {
365     public VersionValue(@Nonnull Version value) {
366       super(value);
367     }
368   }
369 
370   /* -------------------------------------------------------------------------------------------------------------- */
371 
372   //
373   // constructor methods
374   //
375 
376   /** Create a new value of type {@link String}. */
377   public static StringValue mk(String value) {
378     return new StringValue(value);
379   }
380 
381   /** Create a new value of type {@link java.util.Date}. */
382   public static DateValue mk(Date value) {
383     return new DateValue(value);
384   }
385 
386   /** Create a new value of type {@link java.lang.Long}. */
387   public static LongValue mk(Long value) {
388     return new LongValue(value);
389   }
390 
391   /** Create a new value of type {@link java.lang.Boolean}. */
392   public static BooleanValue mk(Boolean value) {
393     return new BooleanValue(value);
394   }
395 
396   /** Create a new value of type {@link Version}. */
397   public static VersionValue mk(Version value) {
398     return new VersionValue(value);
399   }
400 
401   /** Generic constructor. Creates a value for any existing ValueType. */
402   public static <A> TypedValue<A> mk(ValueType<A> mk, A a) {
403     return mk.mk(a);
404   }
405 }