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 allows combining multiple 030 * {@code BeanDeclaration} objects. 031 * </p> 032 * <p> 033 * An instance of this class can be used if a bean is defined using multiple sources. For instance, there can be one 034 * definition with default values and one with actual values; if actual values are provided, they are used; otherwise, 035 * the default values apply. 036 * </p> 037 * <p> 038 * When constructing an instance an arbitrary number of child {@code BeanDeclaration} objects can be specified. The 039 * implementations of the {@code BeanDeclaration} methods implement a logical combination of the data returned by these 040 * child declarations. The order in which child declarations are added is relevant; first entries take precedence over 041 * later ones. The comments of the single methods explain in which way a combination of the child declarations is built. 042 * </p> 043 * 044 * @since 2.0 045 */ 046public class CombinedBeanDeclaration implements BeanDeclaration { 047 048 /** A list with the child declarations. */ 049 private final List<BeanDeclaration> childDeclarations; 050 051 /** 052 * Constructs a new instance of {@code CombinedBeanDeclaration} and initializes it with the given child declarations. 053 * 054 * @param decl the child declarations 055 * @throws NullPointerException if the array with child declarations is <b>null</b> 056 */ 057 public CombinedBeanDeclaration(final BeanDeclaration... decl) { 058 childDeclarations = new ArrayList<>(Arrays.asList(decl)); 059 } 060 061 /** 062 * {@inheritDoc} This implementation iterates over the list of child declarations and asks them for a bean factory name. 063 * The first non-<b>null</b> value is returned. If none of the child declarations have a defined bean factory name, 064 * result is <b>null</b>. 065 */ 066 @Override 067 public String getBeanFactoryName() { 068 for (final BeanDeclaration d : childDeclarations) { 069 final String factoryName = d.getBeanFactoryName(); 070 if (factoryName != null) { 071 return factoryName; 072 } 073 } 074 return null; 075 } 076 077 /** 078 * {@inheritDoc} This implementation iterates over the list of child declarations and asks them for a bean factory 079 * parameter. The first non-<b>null</b> value is returned. If none of the child declarations have a defined bean factory 080 * parameter, result is <b>null</b>. 081 */ 082 @Override 083 public Object getBeanFactoryParameter() { 084 for (final BeanDeclaration d : childDeclarations) { 085 final Object factoryParam = d.getBeanFactoryParameter(); 086 if (factoryParam != null) { 087 return factoryParam; 088 } 089 } 090 return null; 091 } 092 093 /** 094 * {@inheritDoc} This implementation iterates over the list of child declarations and asks them for the bean class name. 095 * The first non-<b>null</b> value is returned. If none of the child declarations have a defined bean class, result is 096 * <b>null</b>. 097 */ 098 @Override 099 public String getBeanClassName() { 100 for (final BeanDeclaration d : childDeclarations) { 101 final String beanClassName = d.getBeanClassName(); 102 if (beanClassName != null) { 103 return beanClassName; 104 } 105 } 106 return null; 107 } 108 109 /** 110 * {@inheritDoc} This implementation creates a union of the properties returned by all child declarations. If a property 111 * is defined in multiple child declarations, the declaration that comes before in the list of children takes 112 * precedence. 113 */ 114 @Override 115 public Map<String, Object> getBeanProperties() { 116 final Map<String, Object> result = new HashMap<>(); 117 for (int i = childDeclarations.size() - 1; i >= 0; i--) { 118 final Map<String, Object> props = childDeclarations.get(i).getBeanProperties(); 119 if (props != null) { 120 result.putAll(props); 121 } 122 } 123 return result; 124 } 125 126 /** 127 * {@inheritDoc} This implementation creates a union of the nested bean declarations returned by all child declarations. 128 * If a complex property is defined in multiple child declarations, the declaration that comes before in the list of 129 * children takes precedence. 130 */ 131 @Override 132 public Map<String, Object> getNestedBeanDeclarations() { 133 final Map<String, Object> result = new HashMap<>(); 134 for (int i = childDeclarations.size() - 1; i >= 0; i--) { 135 final Map<String, Object> decls = childDeclarations.get(i).getNestedBeanDeclarations(); 136 if (decls != null) { 137 result.putAll(decls); 138 } 139 } 140 return result; 141 } 142 143 /** 144 * {@inheritDoc} This implementation iterates over the list of child declarations and asks them for constructor 145 * arguments. The first non-<b>null</b> and non empty collection is returned. If none of the child declarations provide 146 * constructor arguments, result is an empty collection. 147 */ 148 @Override 149 public Collection<ConstructorArg> getConstructorArgs() { 150 for (final BeanDeclaration d : childDeclarations) { 151 final Collection<ConstructorArg> args = d.getConstructorArgs(); 152 if (args != null && !args.isEmpty()) { 153 return args; 154 } 155 } 156 return Collections.emptyList(); 157 } 158}