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 /** 040 * Creates a new instance of {@code ConstructorArg} for the specified {@code BeanDeclaration}. The actual value of this 041 * argument is the resolved {@code BeanDeclaration}. 042 * 043 * @param decl the {@code BeanDeclaration} 044 * @return the newly created instance of this class 045 * @throws NullPointerException if the {@code BeanDeclaration} is <b>null</b> 046 */ 047 public static ConstructorArg forBeanDeclaration(final BeanDeclaration decl) { 048 return forBeanDeclaration(decl, null); 049 } 050 051 /** 052 * Creates a new instance of {@code ConstructorArg} for the specified {@code BeanDeclaration} and sets the type name 053 * explicitly. The type name is used to match this argument against the parameter type of a constructor or the bean 054 * class. 055 * 056 * @param beanDeclaration the {@code BeanDeclaration} 057 * @param typeName the name of the data type of this argument 058 * @return the newly created instance of this class 059 * @throws NullPointerException if the {@code BeanDeclaration} is <b>null</b> 060 */ 061 public static ConstructorArg forBeanDeclaration(final BeanDeclaration beanDeclaration, final String typeName) { 062 Objects.requireNonNull(beanDeclaration, "beanDeclaration"); 063 return new ConstructorArg(beanDeclaration, null, typeName); 064 } 065 066 /** 067 * Creates a new instance of {@code ConstructorArg} for the specified simple value. The value is passed to the 068 * constructor invocation. 069 * 070 * @param value the value of this constructor argument (may be <b>null</b>) 071 * @return the newly created instance of this class 072 */ 073 public static ConstructorArg forValue(final Object value) { 074 return forValue(value, null); 075 } 076 077 /** 078 * Creates a new instance of {@code ConstructorArg} for the specified simple value and sets the type name explicitly. 079 * The type name is used to match this argument against the parameter type of a constructor or the bean class. 080 * 081 * @param value the value of this constructor argument (may be <b>null</b>) 082 * @param typeName the name of the data type of this argument 083 * @return the newly created instance of this class 084 */ 085 public static ConstructorArg forValue(final Object value, final String typeName) { 086 return new ConstructorArg(null, value, typeName); 087 } 088 089 /** The bean declaration referenced by this constructor argument. */ 090 private final BeanDeclaration beanDeclaration; 091 092 /** The value of this constructor argument. */ 093 private final Object value; 094 095 /** The name of the argument type. */ 096 private final String typeName; 097 098 /** 099 * Constructs a new instance of {@code ConstructorArg}. 100 * 101 * @param decl the associated bean declaration 102 * @param val the value of the argument 103 * @param type the type name 104 */ 105 private ConstructorArg(final BeanDeclaration decl, final Object val, final String type) { 106 beanDeclaration = decl; 107 value = val; 108 typeName = type; 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 * Gets the optional data type name of this constructor argument. The type name can be specified as a hint to select 124 * a specific constructor if there are ambiguities. Note that it does not necessarily has to match the data type of this 125 * argument's value because a type conversion may be performed before invoking the constructor. 126 * 127 * @return the data type name of this argument if defined or <b>null</b> otherwise 128 */ 129 public String getTypeName() { 130 return typeName; 131 } 132 133 /** 134 * Gets the value of this constructor argument. This method can be queried if {@link #isNestedBeanDeclaration()} 135 * returns <b>false</b>. Note that a return value of <b>null</b> is legal (to pass <b>null</b> to a constructor 136 * argument). 137 * 138 * @return the simple value of this constructor argument 139 */ 140 public Object getValue() { 141 return value; 142 } 143 144 /** 145 * Tests whether this constructor argument represents a {@code BeanDeclaration}. If this method returns 146 * <b>true</b>, the actual value of this argument can be obtained by resolving the bean declaration returned by 147 * {@link #getBeanDeclaration()}. Otherwise, this argument has a simple value which can be queried using 148 * {@link #getValue()}. 149 * 150 * @return whether this constructor argument references a bean declaration 151 */ 152 public boolean isNestedBeanDeclaration() { 153 return getBeanDeclaration() != null; 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}