001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.core;
018:
019: import java.lang.reflect.Constructor;
020: import java.lang.reflect.Method;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: import org.springframework.util.Assert;
025:
026: /**
027: * Helper class that encapsulates the specification of a method parameter, i.e.
028: * a Method or Constructor plus a parameter index and a nested type index for
029: * a declared generic type. Useful as a specification object to pass along.
030: *
031: * <p>Used by {@link GenericCollectionTypeResolver},
032: * {@link org.springframework.beans.BeanWrapperImpl} and
033: * {@link org.springframework.beans.factory.support.AbstractBeanFactory}.
034: *
035: * <p>Note that this class does not depend on JDK 1.5 API artifacts, in order
036: * to remain compatible with JDK 1.4. Concrete generic type resolution
037: * via JDK 1.5 API happens in {@link GenericCollectionTypeResolver} only.
038: *
039: * @author Juergen Hoeller
040: * @author Rob Harrop
041: * @since 2.0
042: * @see GenericCollectionTypeResolver
043: */
044: public class MethodParameter {
045:
046: private Method method;
047:
048: private Constructor constructor;
049:
050: private final int parameterIndex;
051:
052: private Class parameterType;
053:
054: private Object[] parameterAnnotations;
055:
056: private int nestingLevel;
057:
058: /** Map from Integer level to Integer type index */
059: private Map typeIndexesPerLevel;
060:
061: /**
062: * Create a new MethodParameter for the given method, with nesting level 1.
063: * @param method the Method to specify a parameter for
064: * @param parameterIndex the index of the parameter
065: */
066: public MethodParameter(Method method, int parameterIndex) {
067: this (method, parameterIndex, 1);
068: }
069:
070: /**
071: * Create a new MethodParameter for the given method.
072: * @param method the Method to specify a parameter for
073: * @param parameterIndex the index of the parameter
074: * (-1 for the method return type; 0 for the first method parameter,
075: * 1 for the second method parameter, etc)
076: * @param nestingLevel the nesting level of the target type
077: * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
078: * nested List, whereas 2 would indicate the element of the nested List)
079: */
080: public MethodParameter(Method method, int parameterIndex,
081: int nestingLevel) {
082: Assert.notNull(method, "Method must not be null");
083: this .method = method;
084: this .parameterIndex = parameterIndex;
085: this .nestingLevel = nestingLevel;
086: }
087:
088: /**
089: * Create a new MethodParameter for the given constructor, with nesting level 1.
090: * @param constructor the Constructor to specify a parameter for
091: * @param parameterIndex the index of the parameter
092: */
093: public MethodParameter(Constructor constructor, int parameterIndex) {
094: this (constructor, parameterIndex, 1);
095: }
096:
097: /**
098: * Create a new MethodParameter for the given constructor.
099: * @param constructor the Constructor to specify a parameter for
100: * @param parameterIndex the index of the parameter
101: * @param nestingLevel the nesting level of the target type
102: * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
103: * nested List, whereas 2 would indicate the element of the nested List)
104: */
105: public MethodParameter(Constructor constructor, int parameterIndex,
106: int nestingLevel) {
107: Assert.notNull(constructor, "Constructor must not be null");
108: this .constructor = constructor;
109: this .parameterIndex = parameterIndex;
110: this .nestingLevel = nestingLevel;
111: }
112:
113: /**
114: * Return the wrapped Method, if any.
115: * <p>Note: Either Method or Constructor is available.
116: * @return the Method, or <code>null</code> if none
117: */
118: public Method getMethod() {
119: return this .method;
120: }
121:
122: /**
123: * Return the wrapped Constructor, if any.
124: * <p>Note: Either Method or Constructor is available.
125: * @return the Constructor, or <code>null</code> if none
126: */
127: public Constructor getConstructor() {
128: return this .constructor;
129: }
130:
131: /**
132: * Return the index of the method/constructor parameter.
133: * @return the parameter index (never negative)
134: */
135: public int getParameterIndex() {
136: return this .parameterIndex;
137: }
138:
139: /**
140: * Return the type of the method/constructor parameter.
141: * @return the parameter type (never <code>null</code>)
142: */
143: public Class getParameterType() {
144: if (this .parameterType == null) {
145: this .parameterType = (this .method != null ? this .method
146: .getParameterTypes()[this .parameterIndex]
147: : this .constructor.getParameterTypes()[this .parameterIndex]);
148: }
149: return this .parameterType;
150: }
151:
152: /**
153: * Return the annotations associated with the method/constructor parameter.
154: * @return the parameter annotations, or <code>null</code> if there is
155: * no annotation support (on JDK < 1.5). The return value is an Object array
156: * instead of an Annotation array simply for compatibility with older JDKs;
157: * feel free to cast it to <code>Annotation[]</code> on JDK 1.5 or higher.
158: */
159: public Object[] getParameterAnnotations() {
160: if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_15) {
161: return null;
162: }
163: if (this .parameterAnnotations == null) {
164: this .parameterAnnotations = (this .method != null ? this .method
165: .getParameterAnnotations()[this .parameterIndex]
166: : this .constructor.getParameterAnnotations()[this .parameterIndex]);
167: }
168: return this .parameterAnnotations;
169: }
170:
171: /**
172: * Increase this parameter's nesting level.
173: * @see #getNestingLevel()
174: */
175: public void increaseNestingLevel() {
176: this .nestingLevel++;
177: }
178:
179: /**
180: * Decrease this parameter's nesting level.
181: * @see #getNestingLevel()
182: */
183: public void decreaseNestingLevel() {
184: getTypeIndexesPerLevel().remove(new Integer(this .nestingLevel));
185: this .nestingLevel--;
186: }
187:
188: /**
189: * Return the nesting level of the target type
190: * (typically 1; e.g. in case of a List of Lists, 1 would indicate the
191: * nested List, whereas 2 would indicate the element of the nested List).
192: */
193: public int getNestingLevel() {
194: return this .nestingLevel;
195: }
196:
197: /**
198: * Set the type index for the current nesting level.
199: * @param typeIndex the corresponding type index
200: * (or <code>null</code> for the default type index)
201: * @see #getNestingLevel()
202: */
203: public void setTypeIndexForCurrentLevel(int typeIndex) {
204: getTypeIndexesPerLevel().put(new Integer(this .nestingLevel),
205: new Integer(typeIndex));
206: }
207:
208: /**
209: * Return the type index for the current nesting level.
210: * @return the corresponding type index, or <code>null</code>
211: * if none specified (indicating the default type index)
212: * @see #getNestingLevel()
213: */
214: public Integer getTypeIndexForCurrentLevel() {
215: return getTypeIndexForLevel(this .nestingLevel);
216: }
217:
218: /**
219: * Return the type index for the specified nesting level.
220: * @param nestingLevel the nesting level to check
221: * @return the corresponding type index, or <code>null</code>
222: * if none specified (indicating the default type index)
223: */
224: public Integer getTypeIndexForLevel(int nestingLevel) {
225: return (Integer) getTypeIndexesPerLevel().get(
226: new Integer(nestingLevel));
227: }
228:
229: /**
230: * Obtain the (lazily constructed) type-indexes-per-level Map.
231: */
232: private Map getTypeIndexesPerLevel() {
233: if (this .typeIndexesPerLevel == null) {
234: this .typeIndexesPerLevel = new HashMap(4);
235: }
236: return this .typeIndexesPerLevel;
237: }
238:
239: /**
240: * Create a new MethodParameter for the given method or constructor.
241: * <p>This is a convenience constructor for scenarios where a
242: * Method or Constructor reference is treated in a generic fashion.
243: * @param methodOrConstructor the Method or Constructor to specify a parameter for
244: * @param parameterIndex the index of the parameter
245: * @return the corresponding MethodParameter instance
246: */
247: public static MethodParameter forMethodOrConstructor(
248: Object methodOrConstructor, int parameterIndex) {
249: if (methodOrConstructor instanceof Method) {
250: return new MethodParameter((Method) methodOrConstructor,
251: parameterIndex);
252: } else if (methodOrConstructor instanceof Constructor) {
253: return new MethodParameter(
254: (Constructor) methodOrConstructor, parameterIndex);
255: } else {
256: throw new IllegalArgumentException("Given object ["
257: + methodOrConstructor
258: + "] is neither a Method nor a Constructor");
259: }
260: }
261:
262: }
|