    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.configuration2.beanutils;
    018
    019import java.util.ArrayList;
    020import java.util.Arrays;
    021import java.util.Collection;
    022import java.util.Collections;
    023import java.util.HashMap;
    024import java.util.List;
    025import java.util.Map;
    026
    027/**
    028 * <p>
    029 * A special implementation of the {@code BeanDeclaration} interface which
    030 * allows combining multiple {@code BeanDeclaration} objects.
    031 * </p>
    032 * <p>
    033 * An instance of this class can be used if a bean is defined using multiple
    034 * sources. For instance, there can be one definition with default values and
    035 * one with actual values; if actual values are provided, they are used;
    036 * otherwise, the default values apply.
    037 * </p>
    038 * <p>
    039 * When constructing an instance an arbitrary number of child
    040 * {@code BeanDeclaration} objects can be specified. The implementations of the
    041 * {@code BeanDeclaration} methods implement a logical combination of the data
    042 * returned by these child declarations. The order in which child declarations
    043 * are added is relevant; first entries take precedence over later ones. The
    044 * comments of the single methods explain in which way a combination of the
    045 * child declarations is built.
    046 * </p>
    047 *
    048 * @since 2.0
    049 */
    050public class CombinedBeanDeclaration implements BeanDeclaration
    051{
    052    /** A list with the child declarations. */
    053    private final List<BeanDeclaration> childDeclarations;
    054
    055    /**
    056     * Creates a new instance of {@code CombinedBeanDeclaration} and initializes
    057     * it with the given child declarations.
    058     *
    059     * @param decl the child declarations
    060     * @throws NullPointerException if the array with child declarations is
    061     *         <b>null</b>
    062     */
    063    public CombinedBeanDeclaration(final BeanDeclaration... decl)
    064    {
    065        childDeclarations = new ArrayList<>(Arrays.asList(decl));
    066    }
    067
    068    /**
    069     * {@inheritDoc} This implementation iterates over the list of child
    070     * declarations and asks them for a bean factory name. The first
    071     * non-<b>null</b> value is returned. If none of the child declarations have
    072     * a defined bean factory name, result is <b>null</b>.
    073     */
    074    @Override
    075    public String getBeanFactoryName()
    076    {
    077        for (final BeanDeclaration d : childDeclarations)
    078        {
    079            final String factoryName = d.getBeanFactoryName();
    080            if (factoryName != null)
    081            {
    082                return factoryName;
    083            }
    084        }
    085        return null;
    086    }
    087
    088    /**
    089     * {@inheritDoc} This implementation iterates over the list of child
    090     * declarations and asks them for a bean factory parameter. The first
    091     * non-<b>null</b> value is returned. If none of the child declarations have
    092     * a defined bean factory parameter, result is <b>null</b>.
    093     */
    094    @Override
    095    public Object getBeanFactoryParameter()
    096    {
    097        for (final BeanDeclaration d : childDeclarations)
    098        {
    099            final Object factoryParam = d.getBeanFactoryParameter();
    100            if (factoryParam != null)
    101            {
    102                return factoryParam;
    103            }
    104        }
    105        return null;
    106    }
    107
    108    /**
    109     * {@inheritDoc} This implementation iterates over the list of child
    110     * declarations and asks them for the bean class name. The first
    111     * non-<b>null</b> value is returned. If none of the child declarations have
    112     * a defined bean class, result is <b>null</b>.
    113     */
    114    @Override
    115    public String getBeanClassName()
    116    {
    117        for (final BeanDeclaration d : childDeclarations)
    118        {
    119            final String beanClassName = d.getBeanClassName();
    120            if (beanClassName != null)
    121            {
    122                return beanClassName;
    123            }
    124        }
    125        return null;
    126    }
    127
    128    /**
    129     * {@inheritDoc} This implementation creates a union of the properties
    130     * returned by all child declarations. If a property is defined in multiple
    131     * child declarations, the declaration that comes before in the list of
    132     * children takes precedence.
    133     */
    134    @Override
    135    public Map<String, Object> getBeanProperties()
    136    {
    137        final Map<String, Object> result = new HashMap<>();
    138        for (int i = childDeclarations.size() - 1; i >= 0; i--)
    139        {
    140            final Map<String, Object> props =
    141                    childDeclarations.get(i).getBeanProperties();
    142            if (props != null)
    143            {
    144                result.putAll(props);
    145            }
    146        }
    147        return result;
    148    }
    149
    150    /**
    151     * {@inheritDoc} This implementation creates a union of the nested bean
    152     * declarations returned by all child declarations. If a complex property is
    153     * defined in multiple child declarations, the declaration that comes before
    154     * in the list of children takes precedence.
    155     */
    156    @Override
    157    public Map<String, Object> getNestedBeanDeclarations()
    158    {
    159        final Map<String, Object> result = new HashMap<>();
    160        for (int i = childDeclarations.size() - 1; i >= 0; i--)
    161        {
    162            final Map<String, Object> decls =
    163                    childDeclarations.get(i).getNestedBeanDeclarations();
    164            if (decls != null)
    165            {
    166                result.putAll(decls);
    167            }
    168        }
    169        return result;
    170    }
    171
    172    /**
    173     * {@inheritDoc} This implementation iterates over the list of child
    174     * declarations and asks them for constructor arguments. The first
    175     * non-<b>null</b> and non empty collection is returned. If none of the
    176     * child declarations provide constructor arguments, result is an empty
    177     * collection.
    178     */
    179    @Override
    180    public Collection<ConstructorArg> getConstructorArgs()
    181    {
    182        for (final BeanDeclaration d : childDeclarations)
    183        {
    184            final Collection<ConstructorArg> args = d.getConstructorArgs();
    185            if (args != null && !args.isEmpty())
    186            {
    187                return args;
    188            }
    189        }
    190        return Collections.emptyList();
    191    }
    192}