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}