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

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.commons.configuration2.builder.BasicBuilderParameters;
import org.apache.commons.configuration2.builder.BuilderParameters;
import org.apache.commons.configuration2.builder.DatabaseBuilderParametersImpl;
import org.apache.commons.configuration2.builder.DefaultParametersHandler;
import org.apache.commons.configuration2.builder.DefaultParametersManager;
import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl;
import org.apache.commons.configuration2.builder.HierarchicalBuilderParametersImpl;
import org.apache.commons.configuration2.builder.INIBuilderParametersImpl;
import org.apache.commons.configuration2.builder.JndiBuilderParametersImpl;
import org.apache.commons.configuration2.builder.PropertiesBuilderParametersImpl;
import org.apache.commons.configuration2.builder.XMLBuilderParametersImpl;
import org.apache.commons.configuration2.builder.combined.CombinedBuilderParametersImpl;
import org.apache.commons.configuration2.builder.combined.MultiFileBuilderParametersImpl;

A convenience class for creating parameter objects for initializing configuration builder objects.

For setting initialization properties of new configuration objects, a number of specialized parameter classes exists. These classes use inheritance to organize the properties they support in a logic way. For instance, parameters for file-based configurations also support the basic properties common to all configuration implementations, parameters for XML configurations also include file-based and basic properties, etc.

When constructing a configuration builder, an easy-to-use fluent API is desired to define specific properties for the configuration to be created. However, the inheritance structure of the parameter classes makes it surprisingly difficult to provide such an API. This class comes to rescue by defining a set of methods for the creation of interface-based parameter objects offering a truly fluent API. The methods provided can be called directly when setting up a configuration builder as shown in the following example code fragment:

Parameters params = new Parameters();
configurationBuilder.configure(params.fileBased()
        .setThrowExceptionOnMissing(true).setEncoding("UTF-8")
        .setListDelimiter('#').setFileName("test.xml"));

Using this class it is not only possible to create new parameters objects but also to initialize the newly created objects with default values. This is via the associated DefaultParametersManager object. Such an object can be passed to the constructor, or a new (uninitialized) instance is created. There are convenience methods for interacting with the associated DefaultParametersManager, namely to register or remove DefaultParametersHandler objects. On all newly created parameters objects the handlers registered at the associated DefaultParametersHandler are automatically applied.

Implementation note: This class is thread-safe.

Since:2.0
/** * <p> * A convenience class for creating parameter objects for initializing * configuration builder objects. * </p> * <p> * For setting initialization properties of new configuration objects, a number * of specialized parameter classes exists. These classes use inheritance to * organize the properties they support in a logic way. For instance, parameters * for file-based configurations also support the basic properties common to all * configuration implementations, parameters for XML configurations also include * file-based and basic properties, etc. * </p> * <p> * When constructing a configuration builder, an easy-to-use fluent API is * desired to define specific properties for the configuration to be created. * However, the inheritance structure of the parameter classes makes it * surprisingly difficult to provide such an API. This class comes to rescue by * defining a set of methods for the creation of interface-based parameter * objects offering a truly fluent API. The methods provided can be called * directly when setting up a configuration builder as shown in the following * example code fragment: * </p> * * <pre> * Parameters params = new Parameters(); * configurationBuilder.configure(params.fileBased() * .setThrowExceptionOnMissing(true).setEncoding(&quot;UTF-8&quot;) * .setListDelimiter('#').setFileName(&quot;test.xml&quot;)); * </pre> * * <p> * Using this class it is not only possible to create new parameters objects but * also to initialize the newly created objects with default values. This is * via the associated {@link DefaultParametersManager} object. Such an object * can be passed to the constructor, or a new (uninitialized) instance is * created. There are convenience methods for interacting with the associated * {@code DefaultParametersManager}, namely to register or remove * {@link DefaultParametersHandler} objects. On all newly created parameters * objects the handlers registered at the associated {@code DefaultParametersHandler} * are automatically applied. * </p> * <p> * Implementation note: This class is thread-safe. * </p> * * @since 2.0 */
public final class Parameters {
The manager for default handlers.
/** The manager for default handlers. */
private final DefaultParametersManager defaultParametersManager;
Creates a new instance of Parameters. A new, uninitialized DefaultParametersManager is created.
/** * Creates a new instance of {@code Parameters}. A new, uninitialized * {@link DefaultParametersManager} is created. */
public Parameters() { this(null); }
Creates a new instance of Parameters and initializes it with the given DefaultParametersManager. Because DefaultParametersManager is thread-safe, it makes sense to share a single instance between multiple Parameters objects; that way the same initialization is performed on newly created parameters objects.
Params:
  • manager – the DefaultParametersHandler (may be null, then a new default instance is created)
/** * Creates a new instance of {@code Parameters} and initializes it with the * given {@code DefaultParametersManager}. Because * {@code DefaultParametersManager} is thread-safe, it makes sense to share * a single instance between multiple {@code Parameters} objects; that way * the same initialization is performed on newly created parameters objects. * * @param manager the {@code DefaultParametersHandler} (may be <b>null</b>, * then a new default instance is created) */
public Parameters(final DefaultParametersManager manager) { defaultParametersManager = manager != null ? manager : new DefaultParametersManager(); }
Returns the DefaultParametersManager associated with this object.
Returns:the DefaultParametersManager
/** * Returns the {@code DefaultParametersManager} associated with this object. * * @return the {@code DefaultParametersManager} */
public DefaultParametersManager getDefaultParametersManager() { return defaultParametersManager; }
Registers the specified DefaultParametersHandler object for the given parameters class. This is a convenience method which just delegates to the associated DefaultParametersManager.
Params:
  • paramsClass – the parameters class supported by this handler (must not be null)
  • handler – the DefaultParametersHandler to be registered (must not be null)
Type parameters:
  • <T> – the type of the parameters supported by this handler
Throws:
See Also:
/** * Registers the specified {@code DefaultParametersHandler} object for the * given parameters class. This is a convenience method which just delegates * to the associated {@code DefaultParametersManager}. * * @param <T> the type of the parameters supported by this handler * @param paramsClass the parameters class supported by this handler (must * not be <b>null</b>) * @param handler the {@code DefaultParametersHandler} to be registered * (must not be <b>null</b>) * @throws IllegalArgumentException if a required parameter is missing * @see DefaultParametersManager */
public <T> void registerDefaultsHandler(final Class<T> paramsClass, final DefaultParametersHandler<? super T> handler) { getDefaultParametersManager().registerDefaultsHandler(paramsClass, handler); }
Registers the specified DefaultParametersHandler object for the given parameters class and start class in the inheritance hierarchy. This is a convenience method which just delegates to the associated DefaultParametersManager.
Params:
  • paramsClass – the parameters class supported by this handler (must not be null)
  • handler – the DefaultParametersHandler to be registered (must not be null)
  • startClass – an optional start class in the hierarchy of parameter objects for which this handler should be applied
Type parameters:
  • <T> – the type of the parameters supported by this handler
Throws:
/** * Registers the specified {@code DefaultParametersHandler} object for the * given parameters class and start class in the inheritance hierarchy. This * is a convenience method which just delegates to the associated * {@code DefaultParametersManager}. * * @param <T> the type of the parameters supported by this handler * @param paramsClass the parameters class supported by this handler (must * not be <b>null</b>) * @param handler the {@code DefaultParametersHandler} to be registered * (must not be <b>null</b>) * @param startClass an optional start class in the hierarchy of parameter * objects for which this handler should be applied * @throws IllegalArgumentException if a required parameter is missing */
public <T> void registerDefaultsHandler(final Class<T> paramsClass, final DefaultParametersHandler<? super T> handler, final Class<?> startClass) { getDefaultParametersManager().registerDefaultsHandler(paramsClass, handler, startClass); }
Creates a new instance of a parameters object for basic configuration properties.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for basic configuration * properties. * * @return the new parameters object */
public BasicBuilderParameters basic() { return new BasicBuilderParameters(); }
Creates a new instance of a parameters object for file-based configuration properties.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for file-based * configuration properties. * * @return the new parameters object */
public FileBasedBuilderParameters fileBased() { return createParametersProxy(new FileBasedBuilderParametersImpl(), FileBasedBuilderParameters.class); }
Creates a new instance of a parameters object for combined configuration builder properties.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for combined configuration * builder properties. * * @return the new parameters object */
public CombinedBuilderParameters combined() { return createParametersProxy(new CombinedBuilderParametersImpl(), CombinedBuilderParameters.class); }
Creates a new instance of a parameters object for JNDI configurations.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for JNDI configurations. * * @return the new parameters object */
public JndiBuilderParameters jndi() { return createParametersProxy(new JndiBuilderParametersImpl(), JndiBuilderParameters.class); }
Creates a new instance of a parameters object for hierarchical configurations.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for hierarchical * configurations. * * @return the new parameters object */
public HierarchicalBuilderParameters hierarchical() { return createParametersProxy(new HierarchicalBuilderParametersImpl(), HierarchicalBuilderParameters.class, FileBasedBuilderParameters.class); }
Creates a new instance of a parameters object for XML configurations.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for XML configurations. * * @return the new parameters object */
public XMLBuilderParameters xml() { return createParametersProxy(new XMLBuilderParametersImpl(), XMLBuilderParameters.class, FileBasedBuilderParameters.class, HierarchicalBuilderParameters.class); }
Creates a new instance of a parameters object for properties configurations.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for properties * configurations. * * @return the new parameters object */
public PropertiesBuilderParameters properties() { return createParametersProxy(new PropertiesBuilderParametersImpl(), PropertiesBuilderParameters.class, FileBasedBuilderParameters.class); }
Creates a new instance of a parameters object for a builder for multiple file-based configurations.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for a builder for multiple * file-based configurations. * * @return the new parameters object */
public MultiFileBuilderParameters multiFile() { return createParametersProxy(new MultiFileBuilderParametersImpl(), MultiFileBuilderParameters.class); }
Creates a new instance of a parameters object for database configurations.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for database * configurations. * * @return the new parameters object */
public DatabaseBuilderParameters database() { return createParametersProxy(new DatabaseBuilderParametersImpl(), DatabaseBuilderParameters.class); }
Creates a new instance of a parameters object for INI configurations.
Returns:the new parameters object
/** * Creates a new instance of a parameters object for INI configurations. * * @return the new parameters object */
public INIBuilderParameters ini() { return createParametersProxy(new INIBuilderParametersImpl(), INIBuilderParameters.class, FileBasedBuilderParameters.class, HierarchicalBuilderParameters.class); }
Creates a proxy object for a given parameters interface based on the given implementation object. The newly created object is initialized with default values if there are matching DefaultParametersHandler objects.
Params:
  • target – the implementing target object
  • ifcClass – the interface class
  • superIfcs – an array with additional interface classes to be implemented
Type parameters:
  • <T> – the type of the parameters interface
Returns:the proxy object
/** * Creates a proxy object for a given parameters interface based on the * given implementation object. The newly created object is initialized * with default values if there are matching {@link DefaultParametersHandler} * objects. * * @param <T> the type of the parameters interface * @param target the implementing target object * @param ifcClass the interface class * @param superIfcs an array with additional interface classes to be * implemented * @return the proxy object */
private <T> T createParametersProxy(final Object target, final Class<T> ifcClass, final Class<?>... superIfcs) { final Class<?>[] ifcClasses = new Class<?>[1 + superIfcs.length]; ifcClasses[0] = ifcClass; System.arraycopy(superIfcs, 0, ifcClasses, 1, superIfcs.length); final Object obj = Proxy.newProxyInstance(Parameters.class.getClassLoader(), ifcClasses, new ParametersIfcInvocationHandler(target)); getDefaultParametersManager().initializeParameters( (BuilderParameters) obj); return ifcClass.cast(obj); }
A specialized InvocationHandler implementation which maps the methods of a parameters interface to an implementation of the corresponding property interfaces. The parameters interface is a union of multiple property interfaces. The wrapped object implements all of these, but not the union interface. Therefore, a reflection-based approach is required. A special handling is required for the method of the BuilderParameters interface because here no fluent return value is used.
/** * A specialized {@code InvocationHandler} implementation which maps the * methods of a parameters interface to an implementation of the * corresponding property interfaces. The parameters interface is a union of * multiple property interfaces. The wrapped object implements all of these, * but not the union interface. Therefore, a reflection-based approach is * required. A special handling is required for the method of the * {@code BuilderParameters} interface because here no fluent return value * is used. */
private static class ParametersIfcInvocationHandler implements InvocationHandler {
The target object of reflection calls.
/** The target object of reflection calls. */
private final Object target;
Creates a new instance of ParametersIfcInvocationHandler and sets the wrapped parameters object.
Params:
  • targetObj – the target object for reflection calls
/** * Creates a new instance of {@code ParametersIfcInvocationHandler} and * sets the wrapped parameters object. * * @param targetObj the target object for reflection calls */
public ParametersIfcInvocationHandler(final Object targetObj) { target = targetObj; }
{@inheritDoc} This implementation delegates method invocations to the target object and handles the return value correctly.
/** * {@inheritDoc} This implementation delegates method invocations to the * target object and handles the return value correctly. */
@Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { final Object result = method.invoke(target, args); return isFluentResult(method) ? proxy : result; }
Checks whether the specified method belongs to an interface which requires fluent result values.
Params:
  • method – the method to be checked
Returns:a flag whether the method's result should be handled as a fluent result value
/** * Checks whether the specified method belongs to an interface which * requires fluent result values. * * @param method the method to be checked * @return a flag whether the method's result should be handled as a * fluent result value */
private static boolean isFluentResult(final Method method) { final Class<?> declaringClass = method.getDeclaringClass(); return declaringClass.isInterface() && !declaringClass.equals(BuilderParameters.class); } } }