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