|
| 1 | +/** |
| 2 | + * Helpers for implementing optional parameters in a builder-like class. |
| 3 | + * <p> |
| 4 | + * The scenario is that certain methods take optional parameters, subsets of which can overlap. |
| 5 | + * For example, {@code FactoryA} should take {@code FactoryAOptions} and |
| 6 | + * {@code FactoryB} should take {@code FactoryBOptions}, where both {@code FactoryAOptions} and {@code FactoryBOptions} |
| 7 | + * expose an optional parameter "{@code int a}" with the same meaning and default values. |
| 8 | + * <p> |
| 9 | + * To maintain convenience and type-safety, both {@code FactoryAOptions} and {@code FactoryBOptions} should expose |
| 10 | + * a method {@code a(int)} to set the optional parameter. But {@code FactoryAOptions::a} should return an {@code FactoryAOptions}, |
| 11 | + * and {@code FactoryBOptions::a} should return an {@code FactoryBOptions} to allow chaining more parameters of |
| 12 | + * {@code FactoryAOptions} and {@code FactoryBOptions} respectively, while retaining the type of the builder. |
| 13 | + * <p> |
| 14 | + * Using this package, this can be achieved as follows: |
| 15 | + * <p> |
| 16 | + * Each subset of optional parameters ("{@code int a}" in the above example) is implemented as two interfaces, |
| 17 | + * one exposing methods to set the parameters, one exposing methods to retrieve parameter values. |
| 18 | + * <p> |
| 19 | + * For setting: |
| 20 | + * <pre>{@code |
| 21 | + * interface OptionA< T extends OptionA< T > > extends Options< T > { |
| 22 | + * default T a( int a ) { |
| 23 | + * return add( "a", a ); |
| 24 | + * } |
| 25 | + * }}</pre> where the {@code a()} method records the parameter value (with key {@code "a"}) via the |
| 26 | + * {@code add()} method of the {@code Options} super-interface. |
| 27 | + * <p> |
| 28 | + * For getting: |
| 29 | + * <pre>{@code |
| 30 | + * interface ValueA extends Values { |
| 31 | + * ... |
| 32 | + * default int a() {} |
| 33 | + * return value( "a", 0 ); |
| 34 | + * } |
| 35 | + * }}</pre> where the {@code a()} method returns the parameter value (with key {@code "a"} and default value {@code 0}) |
| 36 | + * via the {@code value()} method of the {@code Values} super-interface. |
| 37 | + * <p> |
| 38 | + * Finally, the implementation of {@code FactoryAOptions} derives from {@code AbstractOptions} and all desired |
| 39 | + * subsets of options |
| 40 | + * <pre>{@code public class FactoryAOptions |
| 41 | + * extends AbstractOptions< FactoryAOptions > |
| 42 | + * implements OptionA< FactoryAOptions >, ... |
| 43 | + * { |
| 44 | + * static class FactoryAValues |
| 45 | + * extends AbstractValues |
| 46 | + * implements ValueA, ... |
| 47 | + * { |
| 48 | + * ... |
| 49 | + * public FactoryAValues( final FactoryAOptions options ) { |
| 50 | + * super( options ); |
| 51 | + * } |
| 52 | + * } |
| 53 | + * |
| 54 | + * public final FactoryAValues values = new FactoryAValues( this ); |
| 55 | + * |
| 56 | + * // ======================================================================= |
| 57 | + * |
| 58 | + * // If in-place modification of the options builder is desired, |
| 59 | + * // the following methods should be left out. |
| 60 | + * |
| 61 | + * public FactoryAOptions() { |
| 62 | + * super(); |
| 63 | + * } |
| 64 | + * |
| 65 | + * private FactoryAOptions(FactoryAOptions that) { |
| 66 | + * super(that); |
| 67 | + * } |
| 68 | + * |
| 69 | + * \@Override |
| 70 | + * public FactoryAOptions copyOrThis() { |
| 71 | + * return new FactoryAOptions(this); |
| 72 | + * } |
| 73 | + * |
| 74 | + * // ======================================================================= |
| 75 | + * |
| 76 | + * // The following is not necessary, but can be overridden like this |
| 77 | + * // to make it show up more nicely in IDE auto-complete |
| 78 | + * |
| 79 | + * \@Override |
| 80 | + * public FactoryAOptions a(int a) { |
| 81 | + * return OptionA.super.a(a); |
| 82 | + * } |
| 83 | + * }}</pre> The parameter values are exposed through inner class {@code FactoryAValues} that derives from {@code AbstractValues} |
| 84 | + * and all desired subsets of option values. |
| 85 | + * <p> |
| 86 | + * The only thing that has been omitted from the above example is the parts that provide a nice {@code toString} implementation |
| 87 | + * for the values. This is be achieved by |
| 88 | + * <pre>{@code |
| 89 | + * interface ValueA extends Values { |
| 90 | + * default void buildToString( AbstractValues.ValuesToString sb ) { |
| 91 | + * sb.append( "a", a() ); |
| 92 | + * } |
| 93 | + * |
| 94 | + * default int a() { |
| 95 | + * return value( "a", 0 ); |
| 96 | + * } |
| 97 | + * }}</pre> and <pre>{@code |
| 98 | + * static class FactoryAValues |
| 99 | + * extends AbstractValues |
| 100 | + * implements ValueA, ... |
| 101 | + * { |
| 102 | + * \@Override |
| 103 | + * public String toString() { |
| 104 | + * final ValuesToString sb = new ValuesToString(); |
| 105 | + * ValueA.super.buildToString( sb ); |
| 106 | + * return sb.toString(); |
| 107 | + * } |
| 108 | + * |
| 109 | + * public FactoryAValues( final FactoryAOptions options ) { |
| 110 | + * super( options ); |
| 111 | + * } |
| 112 | + * }}</pre> |
| 113 | + */ |
| 114 | +package org.scijava.optional; |
0 commit comments