static List<Extension> getApplicableExtensionMethods( EclipseNode typeNode, Annotation ann, TypeBinding receiverType) { List<Extension> extensions = new ArrayList<Extension>(); if ((typeNode != null) && (ann != null) && (receiverType != null)) { BlockScope blockScope = ((TypeDeclaration) typeNode.get()).initializerScope; EclipseNode annotationNode = typeNode.getNodeFor(ann); AnnotationValues<ExtensionMethod> annotation = createAnnotation(ExtensionMethod.class, annotationNode); boolean suppressBaseMethods = false; try { suppressBaseMethods = annotation.getInstance().suppressBaseMethods(); } catch (AnnotationValueDecodeFail fail) { fail.owner.setError(fail.getMessage(), fail.idx); } for (Object extensionMethodProvider : annotation.getActualExpressions("value")) { if (extensionMethodProvider instanceof ClassLiteralAccess) { TypeBinding binding = ((ClassLiteralAccess) extensionMethodProvider).type.resolveType(blockScope); if (binding == null) continue; if (!binding.isClass() && !binding.isEnum()) continue; Extension e = new Extension(); e.extensionMethods = getApplicableExtensionMethodsDefinedInProvider( typeNode, (ReferenceBinding) binding, receiverType); e.suppressBaseMethods = suppressBaseMethods; extensions.add(e); } } } return extensions; }
/** * Convert a JDT annotation value as obtained from ElementValuePair.getValue() (e.g., IntConstant, * FieldBinding, etc.) to the type expected by a reflective method invocation (e.g., int, an enum * constant, etc.). * * @return a value of type {@code expectedType}, or a dummy value of that type if the actual value * cannot be converted. */ private Object convertJDTValueToReflectionType( Object jdtValue, TypeBinding actualType, Class<?> expectedType) { if (expectedType.isPrimitive() || String.class.equals(expectedType)) { if (jdtValue instanceof Constant) { if (boolean.class.equals(expectedType)) { return ((Constant) jdtValue).booleanValue(); } else if (byte.class.equals(expectedType)) { return ((Constant) jdtValue).byteValue(); } else if (char.class.equals(expectedType)) { return ((Constant) jdtValue).charValue(); } else if (double.class.equals(expectedType)) { return ((Constant) jdtValue).doubleValue(); } else if (float.class.equals(expectedType)) { return ((Constant) jdtValue).floatValue(); } else if (int.class.equals(expectedType)) { return ((Constant) jdtValue).intValue(); } else if (long.class.equals(expectedType)) { return ((Constant) jdtValue).longValue(); } else if (short.class.equals(expectedType)) { return ((Constant) jdtValue).shortValue(); } else if (String.class.equals(expectedType)) { return ((Constant) jdtValue).stringValue(); } } // Primitive or string is expected, but our actual value cannot be coerced into one. // TODO: if the actual value is an array of primitives, should we unpack the first one? return Factory.getMatchingDummyValue(expectedType); } else if (expectedType.isEnum()) { Object returnVal = null; if (actualType != null && actualType.isEnum() && jdtValue instanceof FieldBinding) { FieldBinding binding = (FieldBinding) jdtValue; try { Field returnedField = null; returnedField = expectedType.getField(new String(binding.name)); if (null != returnedField) { returnVal = returnedField.get(null); } } catch (NoSuchFieldException nsfe) { // return null } catch (IllegalAccessException iae) { // return null } } return null == returnVal ? Factory.getMatchingDummyValue(expectedType) : returnVal; } else if (expectedType.isAnnotation()) { // member value is expected to be an annotation type. Wrap it in an Annotation proxy. if (actualType.isAnnotationType() && jdtValue instanceof AnnotationBinding) { AnnotationMirrorImpl annoMirror = (AnnotationMirrorImpl) _env.getFactory().newAnnotationMirror((AnnotationBinding) jdtValue); return Proxy.newProxyInstance( expectedType.getClassLoader(), new Class[] {expectedType}, annoMirror); } else { // No way to cast a non-annotation value to an annotation type; return null to caller return null; } } else { return Factory.getMatchingDummyValue(expectedType); } }
/** * Convert an array of JDT types as obtained from ElementValuePair.getValue() (e.g., an Object[] * containing IntConstant elements) to the type expected by a reflective method invocation (e.g., * int[]). * * <p>This does not handle arrays of Class, but it does handle primitives, enum constants, and * types such as String. * * @param jdtValue the actual value returned by ElementValuePair.getValue() or * MethodBinding.getDefault() * @param jdtType the return type of the annotation method binding * @param expectedType the type that the invoker of the method is expecting; must be an array type * @return an Object which is, e.g., an int[]; or null, if an array cannot be created. */ private Object convertJDTArrayToReflectionArray( Object jdtValue, TypeBinding jdtType, Class<?> expectedType) { assert null != expectedType && expectedType.isArray(); if (!jdtType.isArrayType()) { // the compiler says that the type binding isn't an array type; this probably means // that there's some sort of syntax error. return null; } Object[] jdtArray; // See bug 261969: it's legal to pass a solo element for an array-typed value if (jdtValue != null && !(jdtValue instanceof Object[])) { // Create an array of the expected type jdtArray = (Object[]) Array.newInstance(jdtValue.getClass(), 1); jdtArray[0] = jdtValue; } else { jdtArray = (Object[]) jdtValue; } TypeBinding jdtLeafType = jdtType.leafComponentType(); Class<?> expectedLeafType = expectedType.getComponentType(); final int length = jdtArray.length; final Object returnArray = Array.newInstance(expectedLeafType, length); for (int i = 0; i < length; ++i) { Object jdtElementValue = jdtArray[i]; if (expectedLeafType.isPrimitive() || String.class.equals(expectedLeafType)) { if (jdtElementValue instanceof Constant) { if (boolean.class.equals(expectedLeafType)) { Array.setBoolean(returnArray, i, ((Constant) jdtElementValue).booleanValue()); } else if (byte.class.equals(expectedLeafType)) { Array.setByte(returnArray, i, ((Constant) jdtElementValue).byteValue()); } else if (char.class.equals(expectedLeafType)) { Array.setChar(returnArray, i, ((Constant) jdtElementValue).charValue()); } else if (double.class.equals(expectedLeafType)) { Array.setDouble(returnArray, i, ((Constant) jdtElementValue).doubleValue()); } else if (float.class.equals(expectedLeafType)) { Array.setFloat(returnArray, i, ((Constant) jdtElementValue).floatValue()); } else if (int.class.equals(expectedLeafType)) { Array.setInt(returnArray, i, ((Constant) jdtElementValue).intValue()); } else if (long.class.equals(expectedLeafType)) { Array.setLong(returnArray, i, ((Constant) jdtElementValue).longValue()); } else if (short.class.equals(expectedLeafType)) { Array.setShort(returnArray, i, ((Constant) jdtElementValue).shortValue()); } else if (String.class.equals(expectedLeafType)) { Array.set(returnArray, i, ((Constant) jdtElementValue).stringValue()); } } else { // Primitive or string is expected, but our actual value cannot be coerced into one. // TODO: if the actual value is an array of primitives, should we unpack the first one? Factory.setArrayMatchingDummyValue(returnArray, i, expectedLeafType); } } else if (expectedLeafType.isEnum()) { Object returnVal = null; if (jdtLeafType != null && jdtLeafType.isEnum() && jdtElementValue instanceof FieldBinding) { FieldBinding binding = (FieldBinding) jdtElementValue; try { Field returnedField = null; returnedField = expectedLeafType.getField(new String(binding.name)); if (null != returnedField) { returnVal = returnedField.get(null); } } catch (NoSuchFieldException nsfe) { // return null } catch (IllegalAccessException iae) { // return null } } Array.set(returnArray, i, returnVal); } else if (expectedLeafType.isAnnotation()) { // member value is expected to be an annotation type. Wrap it in an Annotation proxy. Object returnVal = null; if (jdtLeafType.isAnnotationType() && jdtElementValue instanceof AnnotationBinding) { AnnotationMirrorImpl annoMirror = (AnnotationMirrorImpl) _env.getFactory().newAnnotationMirror((AnnotationBinding) jdtElementValue); returnVal = Proxy.newProxyInstance( expectedLeafType.getClassLoader(), new Class[] {expectedLeafType}, annoMirror); } Array.set(returnArray, i, returnVal); } else { Array.set(returnArray, i, null); } } return returnArray; }