/**
  * Convert an annotation member value from JDT into Reflection, and from whatever its actual type
  * is into whatever type the reflective invoker of a method is expecting.
  *
  * <p>Only certain types are permitted as member values. Specifically, a member must be a
  * constant, and must be either a primitive type, String, Class, an enum constant, an annotation,
  * or an array of any of those. Multidimensional arrays are not permitted.
  *
  * @param actualValue the value as represented by {@link ElementValuePair#getValue()}
  * @param actualType the return type of the corresponding {@link MethodBinding}
  * @param expectedType the type that the reflective method invoker is expecting
  * @return an object of the expected type representing the annotation member value, or an
  *     appropriate dummy value (such as null) if no value is available
  */
 private Object getReflectionValue(
     Object actualValue, TypeBinding actualType, Class<?> expectedType) {
   if (null == expectedType) {
     // With no expected type, we can't even guess at a conversion
     return null;
   }
   if (null == actualValue) {
     // Return a type-appropriate equivalent of null
     return Factory.getMatchingDummyValue(expectedType);
   }
   if (expectedType.isArray()) {
     if (Class.class.equals(expectedType.getComponentType())) {
       // package Class[]-valued return as a MirroredTypesException
       if (actualType.isArrayType()
           && actualValue instanceof Object[]
           && ((ArrayBinding) actualType).leafComponentType.erasure().id
               == TypeIds.T_JavaLangClass) {
         Object[] bindings = (Object[]) actualValue;
         List<TypeMirror> mirrors = new ArrayList<>(bindings.length);
         for (int i = 0; i < bindings.length; ++i) {
           if (bindings[i] instanceof TypeBinding) {
             mirrors.add(_env.getFactory().newTypeMirror((TypeBinding) bindings[i]));
           }
         }
         throw new MirroredTypesException(mirrors);
       }
       // TODO: actual value is not a TypeBinding[].  Should we return a TypeMirror[] around an
       // ErrorType?
       return null;
     }
     // Handle arrays of types other than Class, e.g., int[], MyEnum[], ...
     return convertJDTArrayToReflectionArray(actualValue, actualType, expectedType);
   } else if (Class.class.equals(expectedType)) {
     // package the Class-valued return as a MirroredTypeException
     if (actualValue instanceof TypeBinding) {
       TypeMirror mirror = _env.getFactory().newTypeMirror((TypeBinding) actualValue);
       throw new MirroredTypeException(mirror);
     } else {
       // TODO: actual value is not a TypeBinding.  Should we return a TypeMirror around an
       // ErrorType?
       return null;
     }
   } else {
     // Handle unitary values of type other than Class, e.g., int, MyEnum, ...
     return convertJDTValueToReflectionType(actualValue, actualType, expectedType);
   }
 }
  /**
   * 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;
 }
 @Override
 public DeclaredType getAnnotationType() {
   return (DeclaredType) _env.getFactory().newTypeMirror(_binding.getAnnotationType());
 }