1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.configuration2.beanutils; 18 19 import java.util.Objects; 20 21 /** 22 * <p> 23 * A class representing an argument for a constructor invocation to be used by a {@link BeanDeclaration}. 24 * </p> 25 * <p> 26 * A {@code BeanDeclaration} can provide a list of instances of this class to define the constructor to be invoked on 27 * the bean class. Each constructor argument can either be a simple value or a nested {@code BeanDeclaration}. In the 28 * latter case, the bean is resolved recursively. 29 * </p> 30 * <p> 31 * The constructor to be invoked on the bean class has to be determined based on the types of the constructor arguments. 32 * To avoid ambiguity, the type name can be explicitly provided. 33 * </p> 34 * 35 * @since 2.0 36 */ 37 public final class ConstructorArg { 38 39 /** The bean declaration referenced by this constructor argument. */ 40 private final BeanDeclaration beanDeclaration; 41 42 /** The value of this constructor argument. */ 43 private final Object value; 44 45 /** The name of the argument type. */ 46 private final String typeName; 47 48 /** 49 * Constructs a new instance of {@code ConstructorArg}. 50 * 51 * @param decl the associated bean declaration 52 * @param val the value of the argument 53 * @param type the type name 54 */ 55 private ConstructorArg(final BeanDeclaration decl, final Object val, final String type) { 56 beanDeclaration = decl; 57 value = val; 58 typeName = type; 59 } 60 61 /** 62 * Creates a new instance of {@code ConstructorArg} for the specified {@code BeanDeclaration}. The actual value of this 63 * argument is the resolved {@code BeanDeclaration}. 64 * 65 * @param decl the {@code BeanDeclaration} 66 * @return the newly created instance of this class 67 * @throws NullPointerException if the {@code BeanDeclaration} is <b>null</b> 68 */ 69 public static ConstructorArg forBeanDeclaration(final BeanDeclaration decl) { 70 return forBeanDeclaration(decl, null); 71 } 72 73 /** 74 * Creates a new instance of {@code ConstructorArg} for the specified {@code BeanDeclaration} and sets the type name 75 * explicitly. The type name is used to match this argument against the parameter type of a constructor or the bean 76 * class. 77 * 78 * @param beanDeclaration the {@code BeanDeclaration} 79 * @param typeName the name of the data type of this argument 80 * @return the newly created instance of this class 81 * @throws NullPointerException if the {@code BeanDeclaration} is <b>null</b> 82 */ 83 public static ConstructorArg forBeanDeclaration(final BeanDeclaration beanDeclaration, final String typeName) { 84 Objects.requireNonNull(beanDeclaration, "beanDeclaration"); 85 return new ConstructorArg(beanDeclaration, null, typeName); 86 } 87 88 /** 89 * Creates a new instance of {@code ConstructorArg} for the specified simple value. The value is passed to the 90 * constructor invocation. 91 * 92 * @param value the value of this constructor argument (may be <b>null</b>) 93 * @return the newly created instance of this class 94 */ 95 public static ConstructorArg forValue(final Object value) { 96 return forValue(value, null); 97 } 98 99 /** 100 * Creates a new instance of {@code ConstructorArg} for the specified simple value and sets the type name explicitly. 101 * The type name is used to match this argument against the parameter type of a constructor or the bean class. 102 * 103 * @param value the value of this constructor argument (may be <b>null</b>) 104 * @param typeName the name of the data type of this argument 105 * @return the newly created instance of this class 106 */ 107 public static ConstructorArg forValue(final Object value, final String typeName) { 108 return new ConstructorArg(null, value, typeName); 109 } 110 111 /** 112 * Gets the {@code BeanDeclaration} referenced by this constructor argument. A return value of <b>null</b> means that 113 * this constructor argument does not have a bean declaration as value; in this case, the value can be queried using the 114 * {@link #getValue()} method. 115 * 116 * @return the referenced {@code BeanDeclaration} or <b>null</b> 117 */ 118 public BeanDeclaration getBeanDeclaration() { 119 return beanDeclaration; 120 } 121 122 /** 123 * Tests whether this constructor argument represents a {@code BeanDeclaration}. If this method returns 124 * <b>true</b>, the actual value of this argument can be obtained by resolving the bean declaration returned by 125 * {@link #getBeanDeclaration()}. Otherwise, this argument has a simple value which can be queried using 126 * {@link #getValue()}. 127 * 128 * @return whether this constructor argument references a bean declaration 129 */ 130 public boolean isNestedBeanDeclaration() { 131 return getBeanDeclaration() != null; 132 } 133 134 /** 135 * Gets the value of this constructor argument. This method can be queried if {@link #isNestedBeanDeclaration()} 136 * returns <b>false</b>. Note that a return value of <b>null</b> is legal (to pass <b>null</b> to a constructor 137 * argument). 138 * 139 * @return the simple value of this constructor argument 140 */ 141 public Object getValue() { 142 return value; 143 } 144 145 /** 146 * Gets the optional data type name of this constructor argument. The type name can be specified as a hint to select 147 * a specific constructor if there are ambiguities. Note that it does not necessarily has to match the data type of this 148 * argument's value because a type conversion may be performed before invoking the constructor. 149 * 150 * @return the data type name of this argument if defined or <b>null</b> otherwise 151 */ 152 public String getTypeName() { 153 return typeName; 154 } 155 156 /** 157 * Checks whether this constructor argument is compatible with the given class. This method is called to determine a 158 * matching constructor. It compares the argument's data type with the class name if it is defined. If no type name has 159 * been set, result is <b>true</b> as it is assumed that a type conversion can be performed when calling the 160 * constructor. This means that per default only the number of constructor arguments is checked to find a matching 161 * constructor. Only if there are multiple constructors with the same number of arguments, explicit type names have to 162 * be provided to select a specific constructor. 163 * 164 * @param argCls the class of the constructor argument to compare with 165 * @return <b>true</b> if this constructor argument is compatible with this class, <b>false</b> otherwise 166 */ 167 public boolean matches(final Class<?> argCls) { 168 if (argCls == null) { 169 return false; 170 } 171 172 return getTypeName() == null || getTypeName().equals(argCls.getName()); 173 } 174 175 /** 176 * Gets a string representation of this object. This string contains the value of this constructor argument and the 177 * explicit type if provided. 178 * 179 * @return a string for this object 180 */ 181 @Override 182 public String toString() { 183 final StringBuilder buf = new StringBuilder(); 184 buf.append(getClass().getSimpleName()); 185 buf.append(" [ value = "); 186 buf.append(isNestedBeanDeclaration() ? getBeanDeclaration() : getValue()); 187 if (getTypeName() != null) { 188 buf.append(" (").append(getTypeName()).append(')'); 189 } 190 buf.append(" ]"); 191 return buf.toString(); 192 } 193 }