All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.configuration2.beanutils.DefaultBeanFactory Maven / Gradle / Ivy

Go to download

Tools to assist in the reading of configuration/preferences files in various formats

There is a newer version: 2.10.1
Show newest version
/*
 * 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.beanutils;

import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.configuration2.convert.ConversionHandler;
import org.apache.commons.configuration2.convert.DefaultConversionHandler;
import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;

/**
 * 

* The default implementation of the {@code BeanFactory} interface. *

*

* This class creates beans of arbitrary types using reflection. Each time the * {@code createBean()} method is invoked, a new bean instance is created. A * default bean class is not supported. *

*

* For data type conversions (which may be needed before invoking methods * through reflection to ensure that the current parameters match their declared * types) a {@link ConversionHandler} object is used. An instance of this class * can be passed to the constructor. Alternatively, a default * {@code ConversionHandler} instance is used. *

*

* An instance of this factory class will be set as the default bean factory for * the {@link BeanHelper} class. This means that if not bean factory is * specified in a {@link BeanDeclaration}, this default instance will be used. *

* * @since 1.3 */ public class DefaultBeanFactory implements BeanFactory { /** Stores the default instance of this class. */ public static final DefaultBeanFactory INSTANCE = new DefaultBeanFactory(); /** A format string for generating error messages for constructor matching. */ private static final String FMT_CTOR_ERROR = "%s! Bean class = %s, constructor arguments = %s"; /** The conversion handler used by this instance. */ private final ConversionHandler conversionHandler; /** * Creates a new instance of {@code DefaultBeanFactory} using a default * {@code ConversionHandler}. */ public DefaultBeanFactory() { this(null); } /** * Creates a new instance of {@code DefaultBeanFactory} using the specified * {@code ConversionHandler} for data type conversions. * * @param convHandler the {@code ConversionHandler}; can be null, * then a default handler is used * @since 2.0 */ public DefaultBeanFactory(final ConversionHandler convHandler) { conversionHandler = (convHandler != null) ? convHandler : DefaultConversionHandler.INSTANCE; } /** * Returns the {@code ConversionHandler} used by this object. * * @return the {@code ConversionHandler} * @since 2.0 */ public ConversionHandler getConversionHandler() { return conversionHandler; } /** * Creates a new bean instance. This implementation delegates to the * protected methods {@code createBeanInstance()} and * {@code initBeanInstance()} for creating and initializing the bean. * This makes it easier for derived classes that need to change specific * functionality of the base class. * * @param bcc the context object defining the bean to be created * @return the new bean instance * @throws Exception if an error occurs */ @Override public Object createBean(final BeanCreationContext bcc) throws Exception { final Object result = createBeanInstance(bcc); initBeanInstance(result, bcc); return result; } /** * Returns the default bean class used by this factory. This is always * null for this implementation. * * @return the default bean class */ @Override public Class getDefaultBeanClass() { return null; } /** * Creates the bean instance. This method is called by * {@code createBean()}. It uses reflection to create a new instance * of the specified class. * * @param bcc the context object defining the bean to be created * @return the new bean instance * @throws Exception if an error occurs */ protected Object createBeanInstance(final BeanCreationContext bcc) throws Exception { final Constructor ctor = findMatchingConstructor(bcc.getBeanClass(), bcc.getBeanDeclaration()); final Object[] args = fetchConstructorArgs(ctor, bcc); return ctor.newInstance(args); } /** * Initializes the newly created bean instance. This method is called by * {@code createBean()}. It calls the {@code initBean()} method of the * context object for performing the initialization. * * @param bean the newly created bean instance * @param bcc the context object defining the bean to be created * @throws Exception if an error occurs */ protected void initBeanInstance(final Object bean, final BeanCreationContext bcc) throws Exception { bcc.initBean(bean, bcc.getBeanDeclaration()); } /** * Evaluates constructor arguments in the specified {@code BeanDeclaration} * and tries to find a unique matching constructor. If this is not possible, * an exception is thrown. Note: This method is intended to be used by * concrete {@link BeanFactory} implementations and not by client code. * * @param beanClass the class of the bean to be created * @param data the current {@code BeanDeclaration} * @param the type of the bean to be created * @return the single matching constructor * @throws ConfigurationRuntimeException if no single matching constructor * can be found * @throws NullPointerException if the bean class or bean declaration are * null */ protected static Constructor findMatchingConstructor( final Class beanClass, final BeanDeclaration data) { final List> matchingConstructors = findMatchingConstructors(beanClass, data); checkSingleMatchingConstructor(beanClass, data, matchingConstructors); return matchingConstructors.get(0); } /** * Obtains the arguments for a constructor call to create a bean. This method * resolves nested bean declarations and performs necessary type * conversions. * * @param ctor the constructor to be invoked * @param bcc the context object defining the bean to be created * @return an array with constructor arguments */ private Object[] fetchConstructorArgs(final Constructor ctor, final BeanCreationContext bcc) { final Class[] types = ctor.getParameterTypes(); assert types.length == nullSafeConstructorArgs(bcc.getBeanDeclaration()).size() : "Wrong number of constructor arguments!"; final Object[] args = new Object[types.length]; int idx = 0; for (final ConstructorArg arg : nullSafeConstructorArgs(bcc.getBeanDeclaration())) { final Object val = arg.isNestedBeanDeclaration() ? bcc.createBean(arg .getBeanDeclaration()) : arg.getValue(); args[idx] = getConversionHandler().to(val, types[idx], null); idx++; } return args; } /** * Fetches constructor arguments from the given bean declaration. Handles * null values safely. * * @param data the bean declaration * @return the collection with constructor arguments (never null) */ private static Collection nullSafeConstructorArgs( final BeanDeclaration data) { Collection args = data.getConstructorArgs(); if (args == null) { args = Collections.emptySet(); } return args; } /** * Returns a list with all constructors which are compatible with the * constructor arguments specified by the given {@code BeanDeclaration}. * * @param beanClass the bean class to be instantiated * @param data the current {@code BeanDeclaration} * @return a list with all matching constructors */ private static List> findMatchingConstructors( final Class beanClass, final BeanDeclaration data) { final List> result = new LinkedList<>(); final Collection args = getConstructorArgs(data); for (final Constructor ctor : beanClass.getConstructors()) { if (matchesConstructor(ctor, args)) { // cast should be okay according to the Javadocs of // getConstructors() @SuppressWarnings("unchecked") final Constructor match = (Constructor) ctor; result.add(match); } } return result; } /** * Checks whether the given constructor is compatible with the given list of * arguments. * * @param ctor the constructor to be checked * @param args the collection of constructor arguments * @return a flag whether this constructor is compatible with the given * arguments */ private static boolean matchesConstructor(final Constructor ctor, final Collection args) { final Class[] types = ctor.getParameterTypes(); if (types.length != args.size()) { return false; } int idx = 0; for (final ConstructorArg arg : args) { if (!arg.matches(types[idx++])) { return false; } } return true; } /** * Helper method for extracting constructor arguments from a bean * declaration. Deals with null values. * * @param data the bean declaration * @return the collection with constructor arguments (never null) */ private static Collection getConstructorArgs( final BeanDeclaration data) { Collection args = data.getConstructorArgs(); if (args == null) { args = Collections.emptySet(); } return args; } /** * Helper method for testing whether exactly one matching constructor was * found. Throws a meaningful exception if there is not a single matching * constructor. * * @param beanClass the bean class * @param data the bean declaration * @param matchingConstructors the list with matching constructors * @throws ConfigurationRuntimeException if there is not exactly one match */ private static void checkSingleMatchingConstructor(final Class beanClass, final BeanDeclaration data, final List> matchingConstructors) { if (matchingConstructors.isEmpty()) { throw constructorMatchingException(beanClass, data, "No matching constructor found"); } if (matchingConstructors.size() > 1) { throw constructorMatchingException(beanClass, data, "Multiple matching constructors found"); } } /** * Creates an exception if no single matching constructor was found with a * meaningful error message. * * @param beanClass the affected bean class * @param data the bean declaration * @param msg an error message * @return the exception with the error message */ private static ConfigurationRuntimeException constructorMatchingException( final Class beanClass, final BeanDeclaration data, final String msg) { return new ConfigurationRuntimeException(FMT_CTOR_ERROR, msg, beanClass.getName(), getConstructorArgs(data).toString()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy