001: /*******************************************************************************
002: * Copyright (c) 2005, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.apt.model;
011:
012: import java.lang.reflect.Array;
013: import java.lang.reflect.Field;
014: import java.lang.reflect.InvocationHandler;
015: import java.lang.reflect.Method;
016: import java.lang.reflect.Proxy;
017: import java.util.ArrayList;
018: import java.util.Collections;
019: import java.util.HashMap;
020: import java.util.List;
021: import java.util.Map;
022:
023: import javax.lang.model.element.AnnotationMirror;
024: import javax.lang.model.element.AnnotationValue;
025: import javax.lang.model.element.ExecutableElement;
026: import javax.lang.model.type.DeclaredType;
027: import javax.lang.model.type.MirroredTypeException;
028: import javax.lang.model.type.MirroredTypesException;
029: import javax.lang.model.type.TypeMirror;
030:
031: import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
032: import org.eclipse.jdt.internal.compiler.impl.Constant;
033: import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
034: import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
035: import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair;
036: import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
037: import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
038: import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
039: import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
040: import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
041:
042: public class AnnotationMirrorImpl implements AnnotationMirror,
043: InvocationHandler {
044:
045: public final BaseProcessingEnvImpl _env;
046: public final AnnotationBinding _binding;
047:
048: /* package */AnnotationMirrorImpl(BaseProcessingEnvImpl env,
049: AnnotationBinding binding) {
050: _env = env;
051: _binding = binding;
052: }
053:
054: @Override
055: public boolean equals(Object obj) {
056: if (obj instanceof AnnotationMirrorImpl) {
057: if (this ._binding == null) {
058: return ((AnnotationMirrorImpl) obj)._binding == null;
059: }
060: return equals(this ._binding,
061: ((AnnotationMirrorImpl) obj)._binding);
062: }
063: return false;
064: }
065:
066: private static boolean equals(AnnotationBinding annotationBinding,
067: AnnotationBinding annotationBinding2) {
068: if (annotationBinding.getAnnotationType() != annotationBinding2
069: .getAnnotationType())
070: return false;
071: final ElementValuePair[] elementValuePairs = annotationBinding
072: .getElementValuePairs();
073: final ElementValuePair[] elementValuePairs2 = annotationBinding2
074: .getElementValuePairs();
075: final int length = elementValuePairs.length;
076: if (length != elementValuePairs2.length)
077: return false;
078: loop: for (int i = 0; i < length; i++) {
079: ElementValuePair pair = elementValuePairs[i];
080: // loop on the given pair to make sure one will match
081: for (int j = 0; j < length; j++) {
082: ElementValuePair pair2 = elementValuePairs2[j];
083: if (pair.binding == pair2.binding) {
084: if (pair.value == null) {
085: if (pair2.value == null) {
086: continue loop;
087: }
088: return false;
089: } else {
090: if (pair2.value == null
091: || !pair2.value.equals(pair.value)) {
092: return false;
093: }
094: }
095: continue loop;
096: }
097: }
098: return false;
099: }
100: return true;
101: }
102:
103: public DeclaredType getAnnotationType() {
104: if (this ._binding == null) {
105: return _env.getFactory().getErrorType();
106: }
107: ReferenceBinding annoType = _binding.getAnnotationType();
108: return _env.getFactory().newDeclaredType(annoType);
109: }
110:
111: /**
112: * @return all the members of this annotation mirror that have explicit values.
113: * Default values are not included.
114: */
115: public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues() {
116: if (this ._binding == null) {
117: return Collections.emptyMap();
118: }
119: ElementValuePair[] pairs = _binding.getElementValuePairs();
120: Map<ExecutableElement, AnnotationValue> valueMap = new HashMap<ExecutableElement, AnnotationValue>(
121: pairs.length);
122: for (ElementValuePair pair : pairs) {
123: MethodBinding method = pair.getMethodBinding();
124: if (method == null) {
125: // ideally we should be able to create a fake ExecuableElementImpl
126: continue;
127: }
128: ExecutableElement e = new ExecutableElementImpl(_env,
129: method);
130: AnnotationValue v = new AnnotationValueImpl(_env, pair
131: .getValue(), method.returnType);
132: valueMap.put(e, v);
133: }
134: return Collections.unmodifiableMap(valueMap);
135: }
136:
137: /**
138: * {@see Elements#getElementValuesWithDefaults()}
139: * @return all the members of this annotation mirror that have explicit or default
140: * values.
141: */
142: public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults() {
143: if (this ._binding == null) {
144: return Collections.emptyMap();
145: }
146: ElementValuePair[] pairs = _binding.getElementValuePairs();
147: ReferenceBinding annoType = _binding.getAnnotationType();
148: Map<ExecutableElement, AnnotationValue> valueMap = new HashMap<ExecutableElement, AnnotationValue>();
149: for (MethodBinding method : annoType.methods()) {
150: // if binding is in ElementValuePair list, then get value from there
151: boolean foundExplicitValue = false;
152: for (int i = 0; i < pairs.length; ++i) {
153: MethodBinding explicitBinding = pairs[i]
154: .getMethodBinding();
155: if (method == explicitBinding) {
156: ExecutableElement e = new ExecutableElementImpl(
157: _env, explicitBinding);
158: AnnotationValue v = new AnnotationValueImpl(_env,
159: pairs[i].getValue(),
160: explicitBinding.returnType);
161: valueMap.put(e, v);
162: foundExplicitValue = true;
163: break;
164: }
165: }
166: // else get default value if one exists
167: if (!foundExplicitValue) {
168: Object defaultVal = method.getDefaultValue();
169: if (null != defaultVal) {
170: ExecutableElement e = new ExecutableElementImpl(
171: _env, method);
172: AnnotationValue v = new AnnotationValueImpl(_env,
173: defaultVal, method.returnType);
174: valueMap.put(e, v);
175: }
176: }
177: }
178: return Collections.unmodifiableMap(valueMap);
179: }
180:
181: public int hashCode() {
182: if (this ._binding == null)
183: return this ._env.hashCode();
184: return this ._binding.hashCode();
185: }
186:
187: /*
188: * Used by getAnnotation(), which returns a reflective proxy of the annotation class. When processors then
189: * invoke methods such as value() on the annotation proxy, this method is called.
190: * <p>
191: * A challenge here is that the processor was not necessarily compiled against the same annotation
192: * definition that the compiler is looking at right now, not to mention that the annotation itself
193: * may be defective in source. So the actual type of the value may be quite different than the
194: * type expected by the caller, which will result in a ClassCastException, which is ugly for the
195: * processor to try to catch. So we try to catch and correct this type mismatch where possible.
196: * <p>
197: * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
198: */
199: @Override
200: public Object invoke(Object proxy, Method method, Object[] args)
201: throws Throwable {
202: if (this ._binding == null)
203: return null;
204: final String methodName = method.getName();
205: if (args == null || args.length == 0) {
206: if (methodName.equals("hashCode")) { //$NON-NLS-1$
207: return new Integer(hashCode());
208: } else if (methodName.equals("toString")) { //$NON-NLS-1$
209: return toString();
210: } else if (methodName.equals("annotationType")) { //$NON-NLS-1$
211: return proxy.getClass().getInterfaces()[0];
212: }
213: } else if (args.length == 1 && methodName.equals("equals")) { //$NON-NLS-1$
214: return new Boolean(equals(args[0]));
215: }
216:
217: // If it's not one of the above methods, it must be an annotation member, so it cannot take any arguments
218: if (args != null && args.length != 0) {
219: throw new NoSuchMethodException(
220: "method " + method.getName() + formatArgs(args) + " does not exist on annotation " + toString()); //$NON-NLS-1$ //$NON-NLS-2$
221: }
222: final MethodBinding methodBinding = getMethodBinding(methodName);
223: if (methodBinding == null) {
224: throw new NoSuchMethodException(
225: "method " + method.getName() + "() does not exist on annotation" + toString()); //$NON-NLS-1$ //$NON-NLS-2$
226: }
227:
228: Object actualValue = null;
229: boolean foundMethod = false;
230: ElementValuePair[] pairs = _binding.getElementValuePairs();
231: for (ElementValuePair pair : pairs) {
232: if (methodName.equals(new String(pair.getName()))) {
233: actualValue = pair.getValue();
234: foundMethod = true;
235: break;
236: }
237: }
238: if (!foundMethod) {
239: // couldn't find explicit value; see if there's a default
240: actualValue = methodBinding.getDefaultValue();
241: }
242: Class<?> expectedType = method.getReturnType();
243: TypeBinding actualType = methodBinding.returnType;
244: return getReflectionValue(actualValue, actualType, expectedType);
245: }
246:
247: /*
248: * (non-Javadoc)
249: * Sun implementation shows the values. We avoid that here,
250: * because getting the values is not idempotent.
251: */
252: @Override
253: public String toString() {
254: if (this ._binding == null) {
255: return "@any()"; //$NON-NLS-1$
256: }
257: return "@" + _binding.getAnnotationType().debugName(); //$NON-NLS-1$
258: }
259:
260: /**
261: * Used for constructing exception message text.
262: * @return a string like "(a, b, c)".
263: */
264: private String formatArgs(final Object[] args) {
265: // estimate that each class name (plus the separators) is 10 characters long plus 2 for "()".
266: final StringBuilder builder = new StringBuilder(
267: args.length * 8 + 2);
268: builder.append('(');
269: for (int i = 0; i < args.length; i++) {
270: if (i > 0)
271: builder.append(", "); //$NON-NLS-1$
272: builder.append(args[i].getClass().getName());
273: }
274: builder.append(')');
275: return builder.toString();
276: }
277:
278: /**
279: * Find a particular annotation member by name.
280: * @return a compiler method binding, or null if no member was found.
281: */
282: private MethodBinding getMethodBinding(String name) {
283: ReferenceBinding annoType = _binding.getAnnotationType();
284: MethodBinding[] methods = annoType.getMethods(name
285: .toCharArray());
286: for (MethodBinding method : methods) {
287: // annotation members have no parameters
288: if (method.parameters.length == 0) {
289: return method;
290: }
291: }
292: return null;
293: }
294:
295: /**
296: * Convert an annotation member value from JDT into Reflection, and from whatever its actual type
297: * is into whatever type the reflective invoker of a method is expecting.
298: * <p>
299: * Only certain types are permitted as member values. Specifically, a member must be a constant,
300: * and must be either a primitive type, String, Class, an enum constant, an annotation, or an
301: * array of any of those. Multidimensional arrays are not permitted.
302: *
303: * @param annoValue the value as represented by {@link ElementValuePair#getValue()}
304: * @param actualType the return type of the corresponding {@link MethodBinding}
305: * @param expectedType the type that the reflective method invoker is expecting
306: * @return an object of the expected type representing the annotation member value,
307: * or an appropriate dummy value (such as null) if no value is available
308: */
309: private Object getReflectionValue(Object actualValue,
310: TypeBinding actualType, Class<?> expectedType) {
311: if (null == expectedType) {
312: // With no expected type, we can't even guess at a conversion
313: return null;
314: }
315: if (null == actualValue) {
316: // Return a type-appropriate equivalent of null
317: return Factory.getMatchingDummyValue(expectedType);
318: }
319: if (expectedType.isArray()) {
320: if (Class.class.equals(expectedType.getComponentType())) {
321: // package Class[]-valued return as a MirroredTypesException
322: if (actualType.isArrayType()
323: && actualValue instanceof Object[]
324: && ((ArrayBinding) actualType).leafComponentType
325: .erasure().id == TypeIds.T_JavaLangClass) {
326: Object[] bindings = (Object[]) actualValue;
327: List<TypeMirror> mirrors = new ArrayList<TypeMirror>(
328: bindings.length);
329: for (int i = 0; i < bindings.length; ++i) {
330: if (bindings[i] instanceof TypeBinding) {
331: mirrors.add(_env.getFactory()
332: .newTypeMirror(
333: (TypeBinding) bindings[i]));
334: }
335: }
336: throw new MirroredTypesException(mirrors);
337: }
338: // TODO: actual value is not a TypeBinding[]. Should we return a TypeMirror[] around an ErrorType?
339: return null;
340: }
341: // Handle arrays of types other than Class, e.g., int[], MyEnum[], ...
342: return convertJDTArrayToReflectionArray(actualValue,
343: actualType, expectedType);
344: } else if (Class.class.equals(expectedType)) {
345: // package the Class-valued return as a MirroredTypeException
346: if (actualValue instanceof TypeBinding) {
347: TypeMirror mirror = _env.getFactory().newTypeMirror(
348: (TypeBinding) actualValue);
349: throw new MirroredTypeException(mirror);
350: } else {
351: // TODO: actual value is not a TypeBinding. Should we return a TypeMirror around an ErrorType?
352: return null;
353: }
354: } else {
355: // Handle unitary values of type other than Class, e.g., int, MyEnum, ...
356: return convertJDTValueToReflectionType(actualValue,
357: actualType, expectedType);
358: }
359: }
360:
361: /**
362: * Convert an array of JDT types as obtained from ElementValuePair.getValue()
363: * (e.g., an Object[] containing IntConstant elements) to the type expected by
364: * a reflective method invocation (e.g., int[]).
365: * <p>
366: * This does not handle arrays of Class, but it does handle primitives, enum constants,
367: * and types such as String.
368: * @param jdtValue the actual value returned by ElementValuePair.getValue() or MethodBinding.getDefault()
369: * @param jdtType the return type of the annotation method binding
370: * @param expectedType the type that the invoker of the method is expecting; must be an array type
371: * @return an Object which is, e.g., an int[]; or null, if an array cannot be created.
372: */
373: private Object convertJDTArrayToReflectionArray(Object jdtValue,
374: TypeBinding jdtType, Class<?> expectedType) {
375: assert null != expectedType && expectedType.isArray();
376: if (!jdtType.isArrayType() || !(jdtValue instanceof Object[])) {
377: // TODO: wrap solo element into one-length array
378: return null;
379: }
380: TypeBinding jdtLeafType = jdtType.leafComponentType();
381: Object[] jdtArray = (Object[]) jdtValue;
382: Class<?> expectedLeafType = expectedType.getComponentType();
383: final int length = jdtArray.length;
384: final Object returnArray = Array.newInstance(expectedLeafType,
385: length);
386: for (int i = 0; i < length; ++i) {
387: Object jdtElementValue = jdtArray[i];
388: if (expectedLeafType.isPrimitive()
389: || String.class.equals(expectedLeafType)) {
390: if (jdtElementValue instanceof Constant) {
391: if (boolean.class.equals(expectedLeafType)) {
392: Array.setBoolean(returnArray, i,
393: ((Constant) jdtElementValue)
394: .booleanValue());
395: } else if (byte.class.equals(expectedLeafType)) {
396: Array.setByte(returnArray, i,
397: ((Constant) jdtElementValue)
398: .byteValue());
399: } else if (char.class.equals(expectedLeafType)) {
400: Array.setChar(returnArray, i,
401: ((Constant) jdtElementValue)
402: .charValue());
403: } else if (double.class.equals(expectedLeafType)) {
404: Array.setDouble(returnArray, i,
405: ((Constant) jdtElementValue)
406: .doubleValue());
407: } else if (float.class.equals(expectedLeafType)) {
408: Array.setFloat(returnArray, i,
409: ((Constant) jdtElementValue)
410: .floatValue());
411: } else if (int.class.equals(expectedLeafType)) {
412: Array
413: .setInt(returnArray, i,
414: ((Constant) jdtElementValue)
415: .intValue());
416: } else if (long.class.equals(expectedLeafType)) {
417: Array.setLong(returnArray, i,
418: ((Constant) jdtElementValue)
419: .longValue());
420: } else if (short.class.equals(expectedLeafType)) {
421: Array.setShort(returnArray, i,
422: ((Constant) jdtElementValue)
423: .shortValue());
424: } else if (String.class.equals(expectedLeafType)) {
425: Array.set(returnArray, i,
426: ((Constant) jdtElementValue)
427: .stringValue());
428: }
429: } else {
430: // Primitive or string is expected, but our actual value cannot be coerced into one.
431: // TODO: if the actual value is an array of primitives, should we unpack the first one?
432: Factory.setArrayMatchingDummyValue(returnArray, i,
433: expectedLeafType);
434: }
435: } else if (expectedLeafType.isEnum()) {
436: Object returnVal = null;
437: if (jdtLeafType != null && jdtLeafType.isEnum()
438: && jdtElementValue instanceof FieldBinding) {
439: FieldBinding binding = (FieldBinding) jdtElementValue;
440: try {
441: Field returnedField = null;
442: returnedField = expectedLeafType
443: .getField(new String(binding.name));
444: if (null != returnedField) {
445: returnVal = returnedField.get(null);
446: }
447: } catch (NoSuchFieldException nsfe) {
448: // return null
449: } catch (IllegalAccessException iae) {
450: // return null
451: }
452: }
453: Array.set(returnArray, i, returnVal);
454: } else if (expectedLeafType.isAnnotation()) {
455: // member value is expected to be an annotation type. Wrap it in an Annotation proxy.
456: Object returnVal = null;
457: if (jdtLeafType.isAnnotationType()
458: && jdtElementValue instanceof AnnotationBinding) {
459: AnnotationMirrorImpl annoMirror = (AnnotationMirrorImpl) _env
460: .getFactory()
461: .newAnnotationMirror(
462: (AnnotationBinding) jdtElementValue);
463: returnVal = Proxy.newProxyInstance(expectedLeafType
464: .getClassLoader(),
465: new Class[] { expectedLeafType },
466: annoMirror);
467: }
468: Array.set(returnArray, i, returnVal);
469: } else {
470: Array.set(returnArray, i, null);
471: }
472: }
473: return returnArray;
474: }
475:
476: /**
477: * Convert a JDT annotation value as obtained from ElementValuePair.getValue()
478: * (e.g., IntConstant, FieldBinding, etc.) to the type expected by a reflective
479: * method invocation (e.g., int, an enum constant, etc.).
480: * @return a value of type {@code expectedType}, or a dummy value of that type if
481: * the actual value cannot be converted.
482: */
483: private Object convertJDTValueToReflectionType(Object jdtValue,
484: TypeBinding actualType, Class<?> expectedType) {
485: if (expectedType.isPrimitive()
486: || String.class.equals(expectedType)) {
487: if (jdtValue instanceof Constant) {
488: if (boolean.class.equals(expectedType)) {
489: return ((Constant) jdtValue).booleanValue();
490: } else if (byte.class.equals(expectedType)) {
491: return ((Constant) jdtValue).byteValue();
492: } else if (char.class.equals(expectedType)) {
493: return ((Constant) jdtValue).charValue();
494: } else if (double.class.equals(expectedType)) {
495: return ((Constant) jdtValue).doubleValue();
496: } else if (float.class.equals(expectedType)) {
497: return ((Constant) jdtValue).floatValue();
498: } else if (int.class.equals(expectedType)) {
499: return ((Constant) jdtValue).intValue();
500: } else if (long.class.equals(expectedType)) {
501: return ((Constant) jdtValue).longValue();
502: } else if (short.class.equals(expectedType)) {
503: return ((Constant) jdtValue).shortValue();
504: } else if (String.class.equals(expectedType)) {
505: return ((Constant) jdtValue).stringValue();
506: }
507: }
508: // Primitive or string is expected, but our actual value cannot be coerced into one.
509: // TODO: if the actual value is an array of primitives, should we unpack the first one?
510: return Factory.getMatchingDummyValue(expectedType);
511: } else if (expectedType.isEnum()) {
512: Object returnVal = null;
513: if (actualType != null && actualType.isEnum()
514: && jdtValue instanceof FieldBinding) {
515:
516: FieldBinding binding = (FieldBinding) jdtValue;
517: try {
518: Field returnedField = null;
519: returnedField = expectedType.getField(new String(
520: binding.name));
521: if (null != returnedField) {
522: returnVal = returnedField.get(null);
523: }
524: } catch (NoSuchFieldException nsfe) {
525: // return null
526: } catch (IllegalAccessException iae) {
527: // return null
528: }
529: }
530: return null == returnVal ? Factory
531: .getMatchingDummyValue(expectedType) : returnVal;
532: } else if (expectedType.isAnnotation()) {
533: // member value is expected to be an annotation type. Wrap it in an Annotation proxy.
534: if (actualType.isAnnotationType()
535: && jdtValue instanceof AnnotationBinding) {
536: AnnotationMirrorImpl annoMirror = (AnnotationMirrorImpl) _env
537: .getFactory().newAnnotationMirror(
538: (AnnotationBinding) jdtValue);
539: return Proxy.newProxyInstance(expectedType
540: .getClassLoader(),
541: new Class[] { expectedType }, annoMirror);
542: } else {
543: // No way to cast a non-annotation value to an annotation type; return null to caller
544: return null;
545: }
546: } else {
547: return Factory.getMatchingDummyValue(expectedType);
548: }
549: }
550:
551: }
|