Parameters.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.configuration2.builder.fluent;

  18. import java.lang.reflect.InvocationHandler;
  19. import java.lang.reflect.Method;
  20. import java.lang.reflect.Proxy;

  21. import org.apache.commons.configuration2.builder.BasicBuilderParameters;
  22. import org.apache.commons.configuration2.builder.BuilderParameters;
  23. import org.apache.commons.configuration2.builder.DatabaseBuilderParametersImpl;
  24. import org.apache.commons.configuration2.builder.DefaultParametersHandler;
  25. import org.apache.commons.configuration2.builder.DefaultParametersManager;
  26. import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl;
  27. import org.apache.commons.configuration2.builder.HierarchicalBuilderParametersImpl;
  28. import org.apache.commons.configuration2.builder.INIBuilderParametersImpl;
  29. import org.apache.commons.configuration2.builder.JndiBuilderParametersImpl;
  30. import org.apache.commons.configuration2.builder.PropertiesBuilderParametersImpl;
  31. import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl;
  32. import org.apache.commons.configuration2.builder.combined.CombinedBuilderParametersImpl;
  33. import org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl;

  34. //@formatter:off
  35. /**
  36.  * A convenience class for creating parameter objects for initializing configuration builder objects.
  37.  * <p>
  38.  * For setting initialization properties of new configuration objects, a number of specialized parameter classes exists.
  39.  * These classes use inheritance to organize the properties they support in a logic way. For instance, parameters for
  40.  * file-based configurations also support the basic properties common to all configuration implementations, parameters
  41.  * for XML configurations also include file-based and basic properties, etc.
  42.  * </p>
  43.  * <p>
  44.  * When constructing a configuration builder, an easy-to-use fluent API is desired to define specific properties for the
  45.  * configuration to be created. However, the inheritance structure of the parameter classes makes it surprisingly
  46.  * difficult to provide such an API. This class comes to rescue by defining a set of methods for the creation of
  47.  * interface-based parameter objects offering a truly fluent API. The methods provided can be called directly when
  48.  * setting up a configuration builder as shown in the following example code fragment:
  49.  * </p>
  50.  * <pre>
  51.  * Parameters params = new Parameters();
  52.  * configurationBuilder.configure(params.fileBased()
  53.  *   .setThrowExceptionOnMissing(true)
  54.  *   .setEncoding(&quot;UTF-8&quot;)
  55.  *   .setListDelimiter('#')
  56.  *   .setFileName(&quot;test.xml&quot;));
  57.  * </pre>
  58.  * <p>
  59.  * Using this class it is not only possible to create new parameters objects but also to initialize the newly created
  60.  * objects with default values. This is via the associated {@link DefaultParametersManager} object. Such an object can
  61.  * be passed to the constructor, or a new (uninitialized) instance is created. There are convenience methods for
  62.  * interacting with the associated {@code DefaultParametersManager}, namely to register or remove
  63.  * {@link DefaultParametersHandler} objects. On all newly created parameters objects the handlers registered at the
  64.  * associated {@code DefaultParametersHandler} are automatically applied.
  65.  * </p>
  66.  * <p>
  67.  * Implementation note: This class is thread-safe.
  68.  * </p>
  69.  *
  70.  * @since 2.0
  71.  */
  72. //@formatter:off
  73. public final class Parameters {
  74.     /**
  75.      * A specialized {@code InvocationHandler} implementation which maps the methods of a parameters interface to an
  76.      * implementation of the corresponding property interfaces. The parameters interface is a union of multiple property
  77.      * interfaces. The wrapped object implements all of these, but not the union interface. Therefore, a reflection-based
  78.      * approach is required. A special handling is required for the method of the {@code BuilderParameters} interface
  79.      * because here no fluent return value is used.
  80.      */
  81.     private static final class ParametersIfcInvocationHandler implements InvocationHandler {
  82.         /**
  83.          * Checks whether the specified method belongs to an interface which requires fluent result values.
  84.          *
  85.          * @param method the method to be checked
  86.          * @return a flag whether the method's result should be handled as a fluent result value
  87.          */
  88.         private static boolean isFluentResult(final Method method) {
  89.             final Class<?> declaringClass = method.getDeclaringClass();
  90.             return declaringClass.isInterface() && !declaringClass.equals(BuilderParameters.class);
  91.         }

  92.         /** The target object of reflection calls. */
  93.         private final Object target;

  94.         /**
  95.          * Creates a new instance of {@code ParametersIfcInvocationHandler} and sets the wrapped parameters object.
  96.          *
  97.          * @param targetObj the target object for reflection calls
  98.          */
  99.         public ParametersIfcInvocationHandler(final Object targetObj) {
  100.             target = targetObj;
  101.         }

  102.         /**
  103.          * {@inheritDoc} This implementation delegates method invocations to the target object and handles the return value
  104.          * correctly.
  105.          */
  106.         @Override
  107.         public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
  108.             final Object result = method.invoke(target, args);
  109.             return isFluentResult(method) ? proxy : result;
  110.         }
  111.     }

  112.     /** The manager for default handlers. */
  113.     private final DefaultParametersManager defaultParametersManager;

  114.     /**
  115.      * Creates a new instance of {@code Parameters}. A new, uninitialized {@link DefaultParametersManager} is created.
  116.      */
  117.     public Parameters() {
  118.         this(null);
  119.     }

  120.     /**
  121.      * Creates a new instance of {@code Parameters} and initializes it with the given {@code DefaultParametersManager}.
  122.      * Because {@code DefaultParametersManager} is thread-safe, it makes sense to share a single instance between multiple
  123.      * {@code Parameters} objects; that way the same initialization is performed on newly created parameters objects.
  124.      *
  125.      * @param manager the {@code DefaultParametersHandler} (may be <strong>null</strong>, then a new default instance is created)
  126.      */
  127.     public Parameters(final DefaultParametersManager manager) {
  128.         defaultParametersManager = manager != null ? manager : new DefaultParametersManager();
  129.     }

  130.     /**
  131.      * Creates a new instance of a parameters object for basic configuration properties.
  132.      *
  133.      * @return the new parameters object
  134.      */
  135.     public BasicBuilderParameters basic() {
  136.         return new BasicBuilderParameters();
  137.     }

  138.     /**
  139.      * Creates a new instance of a parameters object for combined configuration builder properties.
  140.      *
  141.      * @return the new parameters object
  142.      */
  143.     public CombinedBuilderParameters combined() {
  144.         return createParametersProxy(new CombinedBuilderParametersImpl(), CombinedBuilderParameters.class);
  145.     }

  146.     /**
  147.      * Creates a proxy object for a given parameters interface based on the given implementation object. The newly created
  148.      * object is initialized with default values if there are matching {@link DefaultParametersHandler} objects.
  149.      *
  150.      * @param <T> the type of the parameters interface
  151.      * @param target the implementing target object
  152.      * @param ifcClass the interface class
  153.      * @param superIfcs an array with additional interface classes to be implemented
  154.      * @return the proxy object
  155.      */
  156.     private <T> T createParametersProxy(final Object target, final Class<T> ifcClass, final Class<?>... superIfcs) {
  157.         final Class<?>[] ifcClasses = new Class<?>[1 + superIfcs.length];
  158.         ifcClasses[0] = ifcClass;
  159.         System.arraycopy(superIfcs, 0, ifcClasses, 1, superIfcs.length);
  160.         final Object obj = Proxy.newProxyInstance(Parameters.class.getClassLoader(), ifcClasses, new ParametersIfcInvocationHandler(target));
  161.         getDefaultParametersManager().initializeParameters((BuilderParameters) obj);
  162.         return ifcClass.cast(obj);
  163.     }

  164.     /**
  165.      * Creates a new instance of a parameters object for database configurations.
  166.      *
  167.      * @return the new parameters object
  168.      */
  169.     public DatabaseBuilderParameters database() {
  170.         return createParametersProxy(new DatabaseBuilderParametersImpl(), DatabaseBuilderParameters.class);
  171.     }

  172.     /**
  173.      * Creates a new instance of a parameters object for file-based configuration properties.
  174.      *
  175.      * @return the new parameters object
  176.      */
  177.     public FileBasedBuilderParameters fileBased() {
  178.         return createParametersProxy(new FileBasedBuilderParametersImpl(), FileBasedBuilderParameters.class);
  179.     }

  180.     /**
  181.      * Gets the {@code DefaultParametersManager} associated with this object.
  182.      *
  183.      * @return the {@code DefaultParametersManager}
  184.      */
  185.     public DefaultParametersManager getDefaultParametersManager() {
  186.         return defaultParametersManager;
  187.     }

  188.     /**
  189.      * Creates a new instance of a parameters object for hierarchical configurations.
  190.      *
  191.      * @return the new parameters object
  192.      */
  193.     public HierarchicalBuilderParameters hierarchical() {
  194.         return createParametersProxy(new HierarchicalBuilderParametersImpl(), HierarchicalBuilderParameters.class, FileBasedBuilderParameters.class);
  195.     }

  196.     /**
  197.      * Creates a new instance of a parameters object for INI configurations.
  198.      *
  199.      * @return the new parameters object
  200.      */
  201.     public INIBuilderParameters ini() {
  202.         return createParametersProxy(new INIBuilderParametersImpl(), INIBuilderParameters.class, FileBasedBuilderParameters.class,
  203.             HierarchicalBuilderParameters.class);
  204.     }

  205.     /**
  206.      * Creates a new instance of a parameters object for JNDI configurations.
  207.      *
  208.      * @return the new parameters object
  209.      */
  210.     public JndiBuilderParameters jndi() {
  211.         return createParametersProxy(new JndiBuilderParametersImpl(), JndiBuilderParameters.class);
  212.     }

  213.     /**
  214.      * Creates a new instance of a parameters object for a builder for multiple file-based configurations.
  215.      *
  216.      * @return the new parameters object
  217.      */
  218.     public MultiFileBuilderParameters multiFile() {
  219.         return createParametersProxy(new MultiFileBuilderParametersImpl(), MultiFileBuilderParameters.class);
  220.     }

  221.     /**
  222.      * Creates a new instance of a parameters object for properties configurations.
  223.      *
  224.      * @return the new parameters object
  225.      */
  226.     public PropertiesBuilderParameters properties() {
  227.         return createParametersProxy(new PropertiesBuilderParametersImpl(), PropertiesBuilderParameters.class, FileBasedBuilderParameters.class);
  228.     }

  229.     /**
  230.      * Registers the specified {@code DefaultParametersHandler} object for the given parameters class. This is a convenience
  231.      * method which just delegates to the associated {@code DefaultParametersManager}.
  232.      *
  233.      * @param <T> the type of the parameters supported by this handler
  234.      * @param paramsClass the parameters class supported by this handler (must not be <strong>null</strong>)
  235.      * @param handler the {@code DefaultParametersHandler} to be registered (must not be <strong>null</strong>)
  236.      * @throws IllegalArgumentException if a required parameter is missing
  237.      * @see DefaultParametersManager
  238.      */
  239.     public <T> void registerDefaultsHandler(final Class<T> paramsClass, final DefaultParametersHandler<? super T> handler) {
  240.         getDefaultParametersManager().registerDefaultsHandler(paramsClass, handler);
  241.     }

  242.     /**
  243.      * Registers the specified {@code DefaultParametersHandler} object for the given parameters class and start class in the
  244.      * inheritance hierarchy. This is a convenience method which just delegates to the associated
  245.      * {@code DefaultParametersManager}.
  246.      *
  247.      * @param <T> the type of the parameters supported by this handler
  248.      * @param paramsClass the parameters class supported by this handler (must not be <strong>null</strong>)
  249.      * @param handler the {@code DefaultParametersHandler} to be registered (must not be <strong>null</strong>)
  250.      * @param startClass an optional start class in the hierarchy of parameter objects for which this handler should be
  251.      *        applied
  252.      * @throws IllegalArgumentException if a required parameter is missing
  253.      */
  254.     public <T> void registerDefaultsHandler(final Class<T> paramsClass, final DefaultParametersHandler<? super T> handler, final Class<?> startClass) {
  255.         getDefaultParametersManager().registerDefaultsHandler(paramsClass, handler, startClass);
  256.     }

  257.     /**
  258.      * Creates a new instance of a parameters object for XML configurations.
  259.      *
  260.      * @return the new parameters object
  261.      */
  262.     public XMLBuilderParameters xml() {
  263.         return createParametersProxy(new XMLBuilderParametersImpl(), XMLBuilderParameters.class, FileBasedBuilderParameters.class,
  264.             HierarchicalBuilderParameters.class);
  265.     }
  266. }