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 * https://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 /**
40 * Creates a new instance of {@code ConstructorArg} for the specified {@code BeanDeclaration}. The actual value of this
41 * argument is the resolved {@code BeanDeclaration}.
42 *
43 * @param decl the {@code BeanDeclaration}
44 * @return the newly created instance of this class
45 * @throws NullPointerException if the {@code BeanDeclaration} is <strong>null</strong>
46 */
47 public static ConstructorArg forBeanDeclaration(final BeanDeclaration decl) {
48 return forBeanDeclaration(decl, null);
49 }
50
51 /**
52 * Creates a new instance of {@code ConstructorArg} for the specified {@code BeanDeclaration} and sets the type name
53 * explicitly. The type name is used to match this argument against the parameter type of a constructor or the bean
54 * class.
55 *
56 * @param beanDeclaration the {@code BeanDeclaration}
57 * @param typeName the name of the data type of this argument
58 * @return the newly created instance of this class
59 * @throws NullPointerException if the {@code BeanDeclaration} is <strong>null</strong>
60 */
61 public static ConstructorArg forBeanDeclaration(final BeanDeclaration beanDeclaration, final String typeName) {
62 Objects.requireNonNull(beanDeclaration, "beanDeclaration");
63 return new ConstructorArg(beanDeclaration, null, typeName);
64 }
65
66 /**
67 * Creates a new instance of {@code ConstructorArg} for the specified simple value. The value is passed to the
68 * constructor invocation.
69 *
70 * @param value the value of this constructor argument (may be <strong>null</strong>)
71 * @return the newly created instance of this class
72 */
73 public static ConstructorArg forValue(final Object value) {
74 return forValue(value, null);
75 }
76
77 /**
78 * Creates a new instance of {@code ConstructorArg} for the specified simple value and sets the type name explicitly.
79 * The type name is used to match this argument against the parameter type of a constructor or the bean class.
80 *
81 * @param value the value of this constructor argument (may be <strong>null</strong>)
82 * @param typeName the name of the data type of this argument
83 * @return the newly created instance of this class
84 */
85 public static ConstructorArg forValue(final Object value, final String typeName) {
86 return new ConstructorArg(null, value, typeName);
87 }
88
89 /** The bean declaration referenced by this constructor argument. */
90 private final BeanDeclaration beanDeclaration;
91
92 /** The value of this constructor argument. */
93 private final Object value;
94
95 /** The name of the argument type. */
96 private final String typeName;
97
98 /**
99 * 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 <strong>null</strong> 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 <strong>null</strong>
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 <strong>null</strong> 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 <strong>false</strong>. Note that a return value of <strong>null</strong> is legal (to pass <strong>null</strong> 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 * <strong>true</strong>, 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 <strong>true</strong> 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 <strong>true</strong> if this constructor argument is compatible with this class, <strong>false</strong> 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 }