    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 * @version $Id: CombinedBeanDeclaration.java 1842194 2018-09-27 22:24:23Z ggregory $
    049 * @since 2.0
    050 */
    051public class CombinedBeanDeclaration implements BeanDeclaration
    052{
    053    /** A list with the child declarations. */
    054    private final List<BeanDeclaration> childDeclarations;
    055
    056    /**
    057     * Creates a new instance of {@code CombinedBeanDeclaration} and initializes
    058     * it with the given child declarations.
    059     *
    060     * @param decl the child declarations
    061     * @throws NullPointerException if the array with child declarations is
    062     *         <b>null</b>
    063     */
    064    public CombinedBeanDeclaration(final BeanDeclaration... decl)
    065    {
    066        childDeclarations = new ArrayList<>(Arrays.asList(decl));
    067    }
    068
    069    /**
    070     * {@inheritDoc} This implementation iterates over the list of child
    071     * declarations and asks them for a bean factory name. The first
    072     * non-<b>null</b> value is returned. If none of the child declarations have
    073     * a defined bean factory name, result is <b>null</b>.
    074     */
    075    @Override
    076    public String getBeanFactoryName()
    077    {
    078        for (final BeanDeclaration d : childDeclarations)
    079        {
    080            final String factoryName = d.getBeanFactoryName();
    081            if (factoryName != null)
    082            {
    083                return factoryName;
    084            }
    085        }
    086        return null;
    087    }
    088
    089    /**
    090     * {@inheritDoc} This implementation iterates over the list of child
    091     * declarations and asks them for a bean factory parameter. The first
    092     * non-<b>null</b> value is returned. If none of the child declarations have
    093     * a defined bean factory parameter, result is <b>null</b>.
    094     */
    095    @Override
    096    public Object getBeanFactoryParameter()
    097    {
    098        for (final BeanDeclaration d : childDeclarations)
    099        {
    100            final Object factoryParam = d.getBeanFactoryParameter();
    101            if (factoryParam != null)
    102            {
    103                return factoryParam;
    104            }
    105        }
    106        return null;
    107    }
    108
    109    /**
    110     * {@inheritDoc} This implementation iterates over the list of child
    111     * declarations and asks them for the bean class name. The first
    112     * non-<b>null</b> value is returned. If none of the child declarations have
    113     * a defined bean class, result is <b>null</b>.
    114     */
    115    @Override
    116    public String getBeanClassName()
    117    {
    118        for (final BeanDeclaration d : childDeclarations)
    119        {
    120            final String beanClassName = d.getBeanClassName();
    121            if (beanClassName != null)
    122            {
    123                return beanClassName;
    124            }
    125        }
    126        return null;
    127    }
    128
    129    /**
    130     * {@inheritDoc} This implementation creates a union of the properties
    131     * returned by all child declarations. If a property is defined in multiple
    132     * child declarations, the declaration that comes before in the list of
    133     * children takes precedence.
    134     */
    135    @Override
    136    public Map<String, Object> getBeanProperties()
    137    {
    138        final Map<String, Object> result = new HashMap<>();
    139        for (int i = childDeclarations.size() - 1; i >= 0; i--)
    140        {
    141            final Map<String, Object> props =
    142                    childDeclarations.get(i).getBeanProperties();
    143            if (props != null)
    144            {
    145                result.putAll(props);
    146            }
    147        }
    148        return result;
    149    }
    150
    151    /**
    152     * {@inheritDoc} This implementation creates a union of the nested bean
    153     * declarations returned by all child declarations. If a complex property is
    154     * defined in multiple child declarations, the declaration that comes before
    155     * in the list of children takes precedence.
    156     */
    157    @Override
    158    public Map<String, Object> getNestedBeanDeclarations()
    159    {
    160        final Map<String, Object> result = new HashMap<>();
    161        for (int i = childDeclarations.size() - 1; i >= 0; i--)
    162        {
    163            final Map<String, Object> decls =
    164                    childDeclarations.get(i).getNestedBeanDeclarations();
    165            if (decls != null)
    166            {
    167                result.putAll(decls);
    168            }
    169        }
    170        return result;
    171    }
    172
    173    /**
    174     * {@inheritDoc} This implementation iterates over the list of child
    175     * declarations and asks them for constructor arguments. The first
    176     * non-<b>null</b> and non empty collection is returned. If none of the
    177     * child declarations provide constructor arguments, result is an empty
    178     * collection.
    179     */
    180    @Override
    181    public Collection<ConstructorArg> getConstructorArgs()
    182    {
    183        for (final BeanDeclaration d : childDeclarations)
    184        {
    185            final Collection<ConstructorArg> args = d.getConstructorArgs();
    186            if (args != null && !args.isEmpty())
    187            {
    188                return args;
    189            }
    190        }
    191        return Collections.emptyList();
    192    }
    193}