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

The 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"; /** * Checks 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"); } } /** * Constructs 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()); } /** * 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); } /** * 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; } /** * Gets 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; } /** * 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; } /** * 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; } /** The conversion handler used by this instance. */ private final ConversionHandler conversionHandler; /** * Constructs a new instance of {@code DefaultBeanFactory} using a default {@code ConversionHandler}. */ public DefaultBeanFactory() { this(null); } /** * Constructs 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; } /** * 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; } /** * 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); } /** * 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; } /** * Gets the {@code ConversionHandler} used by this object. * * @return the {@code ConversionHandler} * @since 2.0 */ public ConversionHandler getConversionHandler() { return conversionHandler; } /** * Gets 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; } /** * 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()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy