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.Objects; 020 021/** 022 * <p> 023 * A class representing an argument for a constructor invocation to be used by a 024 * {@link BeanDeclaration}. 025 * </p> 026 * <p> 027 * A {@code BeanDeclaration} can provide a list of instances of this class to 028 * define the constructor to be invoked on the bean class. Each constructor 029 * argument can either be a simple value or a nested {@code BeanDeclaration}. In 030 * the latter case, the bean is resolved recursively. 031 * </p> 032 * <p> 033 * The constructor to be invoked on the bean class has to be determined based on 034 * the types of the constructor arguments. To avoid ambiguity, the type name can 035 * be explicitly provided. 036 * </p> 037 * 038 * @since 2.0 039 */ 040public final class ConstructorArg 041{ 042 /** The bean declaration referenced by this constructor argument. */ 043 private final BeanDeclaration beanDeclaration; 044 045 /** The value of this constructor argument. */ 046 private final Object value; 047 048 /** The name of the argument type. */ 049 private final String typeName; 050 051 /** 052 * Creates a new instance of {@code ConstructorArg}. 053 * 054 * @param decl the associated bean declaration 055 * @param val the value of the argument 056 * @param type the type name 057 */ 058 private ConstructorArg(final BeanDeclaration decl, final Object val, final String type) 059 { 060 beanDeclaration = decl; 061 value = val; 062 typeName = type; 063 } 064 065 /** 066 * Creates a new instance of {@code ConstructorArg} for the specified 067 * {@code BeanDeclaration}. The actual value of this argument is the 068 * resolved {@code BeanDeclaration}. 069 * 070 * @param decl the {@code BeanDeclaration} 071 * @return the newly created instance of this class 072 * @throws NullPointerException if the {@code BeanDeclaration} is 073 * <b>null</b> 074 */ 075 public static ConstructorArg forBeanDeclaration(final BeanDeclaration decl) 076 { 077 return forBeanDeclaration(decl, null); 078 } 079 080 /** 081 * Creates a new instance of {@code ConstructorArg} for the specified 082 * {@code BeanDeclaration} and sets the type name explicitly. The type name 083 * is used to match this argument against the parameter type of a 084 * constructor or the bean class. 085 * 086 * @param beanDeclaration the {@code BeanDeclaration} 087 * @param typeName the name of the data type of this argument 088 * @return the newly created instance of this class 089 * @throws NullPointerException if the {@code BeanDeclaration} is 090 * <b>null</b> 091 */ 092 public static ConstructorArg forBeanDeclaration(final BeanDeclaration beanDeclaration, 093 final String typeName) 094 { 095 Objects.requireNonNull(beanDeclaration, "beanDeclaration"); 096 return new ConstructorArg(beanDeclaration, null, typeName); 097 } 098 099 /** 100 * Creates a new instance of {@code ConstructorArg} for the specified simple 101 * value. The value is passed to the constructor invocation. 102 * 103 * @param value the value of this constructor argument (may be <b>null</b>) 104 * @return the newly created instance of this class 105 */ 106 public static ConstructorArg forValue(final Object value) 107 { 108 return forValue(value, null); 109 } 110 111 /** 112 * Creates a new instance of {@code ConstructorArg} for the specified simple 113 * value and sets the type name explicitly. The type name is used to match 114 * this argument against the parameter type of a constructor or the bean 115 * class. 116 * 117 * @param value the value of this constructor argument (may be <b>null</b>) 118 * @param typeName the name of the data type of this argument 119 * @return the newly created instance of this class 120 */ 121 public static ConstructorArg forValue(final Object value, final String typeName) 122 { 123 return new ConstructorArg(null, value, typeName); 124 } 125 126 /** 127 * Returns the {@code BeanDeclaration} referenced by this constructor 128 * argument. A return value of <b>null</b> means that this constructor 129 * argument does not have a bean declaration as value; in this case, the 130 * value can be queried using the {@link #getValue()} method. 131 * 132 * @return the referenced {@code BeanDeclaration} or <b>null</b> 133 */ 134 public BeanDeclaration getBeanDeclaration() 135 { 136 return beanDeclaration; 137 } 138 139 /** 140 * Returns a flag whether this constructor argument represents a 141 * {@code BeanDeclaration}. If this method returns <b>true</b>, the actual 142 * value of this argument can be obtained by resolving the bean declaration 143 * returned by {@link #getBeanDeclaration()}. Otherwise, this argument has a 144 * simple value which can be queried using {@link #getValue()}. 145 * 146 * @return a flag whether this constructor argument references a bean 147 * declaration 148 */ 149 public boolean isNestedBeanDeclaration() 150 { 151 return getBeanDeclaration() != null; 152 } 153 154 /** 155 * Returns the value of this constructor argument. This method can be 156 * queried if {@link #isNestedBeanDeclaration()} returns <b>false</b>. Note 157 * that a return value of <b>null</b> is legal (to pass <b>null</b> to a 158 * constructor argument). 159 * 160 * @return the simple value of this constructor argument 161 */ 162 public Object getValue() 163 { 164 return value; 165 } 166 167 /** 168 * Returns the optional data type name of this constructor argument. The 169 * type name can be specified as a hint to select a specific constructor if 170 * there are ambiguities. Note that it does not necessarily has to match the 171 * data type of this argument's value because a type conversion may be 172 * performed before invoking the constructor. 173 * 174 * @return the data type name of this argument if defined or <b>null</b> 175 * otherwise 176 */ 177 public String getTypeName() 178 { 179 return typeName; 180 } 181 182 /** 183 * Checks whether this constructor argument is compatible with the given 184 * class. This method is called to determine a matching constructor. It 185 * compares the argument's data type with the class name if it is defined. 186 * If no type name has been set, result is <b>true</b> as it is assumed that 187 * a type conversion can be performed when calling the constructor. This 188 * means that per default only the number of constructor arguments is 189 * checked to find a matching constructor. Only if there are multiple 190 * constructors with the same number of arguments, explicit type names have 191 * to be provided to select a specific constructor. 192 * 193 * @param argCls the class of the constructor argument to compare with 194 * @return <b>true</b> if this constructor argument is compatible with this 195 * class, <b>false</b> otherwise 196 */ 197 public boolean matches(final Class<?> argCls) 198 { 199 if (argCls == null) 200 { 201 return false; 202 } 203 204 return getTypeName() == null || getTypeName().equals(argCls.getName()); 205 } 206 207 /** 208 * Returns a string representation of this object. This string contains the 209 * value of this constructor argument and the explicit type if provided. 210 * 211 * @return a string for this object 212 */ 213 @Override 214 public String toString() 215 { 216 final StringBuilder buf = new StringBuilder(); 217 buf.append(getClass().getSimpleName()); 218 buf.append(" [ value = "); 219 buf.append(isNestedBeanDeclaration() ? getBeanDeclaration() 220 : getValue()); 221 if (getTypeName() != null) 222 { 223 buf.append(" (").append(getTypeName()).append(')'); 224 } 225 buf.append(" ]"); 226 return buf.toString(); 227 } 228}