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 1790899 2017-04-10 21:56:46Z 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(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 (BeanDeclaration d : childDeclarations)
079        {
080            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 (BeanDeclaration d : childDeclarations)
099        {
100            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 (BeanDeclaration d : childDeclarations)
119        {
120            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        Map<String, Object> result = new HashMap<>();
139        for (int i = childDeclarations.size() - 1; i >= 0; i--)
140        {
141            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        Map<String, Object> result = new HashMap<>();
161        for (int i = childDeclarations.size() - 1; i >= 0; i--)
162        {
163            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 (BeanDeclaration d : childDeclarations)
184        {
185            Collection<ConstructorArg> args = d.getConstructorArgs();
186            if (args != null && !args.isEmpty())
187            {
188                return args;
189            }
190        }
191        return Collections.emptyList();
192    }
193}