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 {@link BeanDeclaration}. 024 * </p> 025 * <p> 026 * A {@code BeanDeclaration} can provide a list of instances of this class to define the constructor to be invoked on 027 * the bean class. Each constructor argument can either be a simple value or a nested {@code BeanDeclaration}. In the 028 * 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 the types of the constructor arguments. 032 * To avoid ambiguity, the type name can be explicitly provided. 033 * </p> 034 * 035 * @since 2.0 036 */ 037public final class ConstructorArg { 038 039 /** The bean declaration referenced by this constructor argument. */ 040 private final BeanDeclaration beanDeclaration; 041 042 /** The value of this constructor argument. */ 043 private final Object value; 044 045 /** The name of the argument type. */ 046 private final String typeName; 047 048 /** 049 * Constructs a new instance of {@code ConstructorArg}. 050 * 051 * @param decl the associated bean declaration 052 * @param val the value of the argument 053 * @param type the type name 054 */ 055 private ConstructorArg(final BeanDeclaration decl, final Object val, final String type) { 056 beanDeclaration = decl; 057 value = val; 058 typeName = type; 059 } 060 061 /** 062 * Creates a new instance of {@code ConstructorArg} for the specified {@code BeanDeclaration}. The actual value of this 063 * argument is the resolved {@code BeanDeclaration}. 064 * 065 * @param decl the {@code BeanDeclaration} 066 * @return the newly created instance of this class 067 * @throws NullPointerException if the {@code BeanDeclaration} is <b>null</b> 068 */ 069 public static ConstructorArg forBeanDeclaration(final BeanDeclaration decl) { 070 return forBeanDeclaration(decl, null); 071 } 072 073 /** 074 * Creates a new instance of {@code ConstructorArg} for the specified {@code BeanDeclaration} and sets the type name 075 * explicitly. The type name is used to match this argument against the parameter type of a constructor or the bean 076 * class. 077 * 078 * @param beanDeclaration the {@code BeanDeclaration} 079 * @param typeName the name of the data type of this argument 080 * @return the newly created instance of this class 081 * @throws NullPointerException if the {@code BeanDeclaration} is <b>null</b> 082 */ 083 public static ConstructorArg forBeanDeclaration(final BeanDeclaration beanDeclaration, final String typeName) { 084 Objects.requireNonNull(beanDeclaration, "beanDeclaration"); 085 return new ConstructorArg(beanDeclaration, null, typeName); 086 } 087 088 /** 089 * Creates a new instance of {@code ConstructorArg} for the specified simple value. The value is passed to the 090 * constructor invocation. 091 * 092 * @param value the value of this constructor argument (may be <b>null</b>) 093 * @return the newly created instance of this class 094 */ 095 public static ConstructorArg forValue(final Object value) { 096 return forValue(value, null); 097 } 098 099 /** 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}