Java tutorial
/* * 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> * * @version $Id$ * @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); } } }