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}