| /* |
| * 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; |
| |
| /** |
| * <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("UTF-8") |
| * .setListDelimiter('#').setFileName("test.xml")); |
| * </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. */ |
| private final DefaultParametersManager defaultParametersManager; |
| |
| /** |
| * Creates a new instance of {@code Parameters}. A new, uninitialized |
| * {@link DefaultParametersManager} is created. |
| */ |
| public Parameters() |
| { |
| this(null); |
| } |
| |
| /** |
| * 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 {@code DefaultParametersManager} associated with this object. |
| * |
| * @return the {@code DefaultParametersManager} |
| */ |
| public DefaultParametersManager getDefaultParametersManager() |
| { |
| return defaultParametersManager; |
| } |
| |
| /** |
| * 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 {@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. |
| * |
| * @return the new parameters object |
| */ |
| public BasicBuilderParameters basic() |
| { |
| return new BasicBuilderParameters(); |
| } |
| |
| /** |
| * 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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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 {@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 {@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. */ |
| private final Object target; |
| |
| /** |
| * 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. |
| */ |
| @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. |
| * |
| * @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); |
| } |
| } |
| } |