/******************************************************************************* * Copyright (c) 2005, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.apt.model; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.impl.DoubleConstant; import org.eclipse.jdt.internal.compiler.impl.FloatConstant; import org.eclipse.jdt.internal.compiler.impl.LongConstant; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement; public class AnnotationValueImpl implements AnnotationValue, TypeIds { /* * Additions to T_* constants in TypeIds. */ private static final int T_AnnotationMirror = -1; private static final int T_EnumConstant = -2; private static final int T_ClassObject = -3; private static final int T_ArrayType = -4; private final BaseProcessingEnvImpl _env; /** * The annotation value, as it would be returned by * {@link #getValue()}. For instance, an Integer (for an int * constant), a VariableElement (for an enum constant), or * a List<AnnotationValueImpl> containing multiple such (for an array type). */ private final Object _value; /** * The type stored in _value, represented as a T_* value from {@link TypeIds} * or one of the additional T_* values defined in this class. */ private final int _kind; /** * @param value * The JDT representation of a compile-time constant. See * {@link ElementValuePair#getValue()} for possible object types: * <ul> * <li>{@link org.eclipse.jdt.internal.compiler.impl.Constant} for member * of primitive type or String</li> * <li>{@link TypeBinding} for a member value of type * {@link java.lang.Class}</li> * <li>{@link FieldBinding} for an enum constant</li> * <li>{@link AnnotationBinding} for an annotation instance</li> * <li><code>Object[]</code> for a member value of array type, where the * array entries are one of the above</li> * </ul> * @param type * The JDT representation of the type of the constant, as determined * by the return type of the element. This is needed because the type * of the value may have been widened (e.g., byte to int) by the compiler * and we need to call the proper visitor. This is used only for base types. * If it is null or not a BaseTypeBinding, it is ignored and the type is * determined from the type of the value. */ public AnnotationValueImpl(BaseProcessingEnvImpl env, Object value, TypeBinding type) { _env = env; int kind[] = new int[1]; if (type == null) { _value = convertToMirrorType(value, type, kind); _kind = kind[0]; } else if (type.isArrayType()) { List<AnnotationValue> convertedValues = null; TypeBinding valueType = ((ArrayBinding)type).elementsType(); if (value instanceof Object[]) { Object[] values = (Object[])value; convertedValues = new ArrayList<AnnotationValue>(values.length); for (Object oneValue : values) { convertedValues.add(new AnnotationValueImpl(_env, oneValue, valueType)); } } else { convertedValues = new ArrayList<AnnotationValue>(1); convertedValues.add(new AnnotationValueImpl(_env, value, valueType)); } _value = Collections.unmodifiableList(convertedValues); _kind = T_ArrayType; } else { _value = convertToMirrorType(value, type, kind); _kind = kind[0]; } } /** * Convert the JDT representation of a single constant into its javax.lang.model * representation. For instance, convert a StringConstant into a String, or * a FieldBinding into a VariableElement. This does not handle the case where * value is an Object[]. * @param value the JDT object * @param type the return type of the annotation member. If null or not a * BaseTypeBinding, this is ignored and the value is inspected to determine type. * @param kind an int array whose first element will be set to the type of the * converted object, represented with T_* values from TypeIds or from this class. * @return */ private Object convertToMirrorType(Object value, TypeBinding type, int kind[]) { if (type == null) { kind[0] = TypeIds.T_JavaLangString; return "<error>"; //$NON-NLS-1$ } else if (type instanceof BaseTypeBinding || type.id == TypeIds.T_JavaLangString) { if (value == null) { if (type instanceof BaseTypeBinding || type.id == TypeIds.T_JavaLangString) { // return a string with error in it to reflect a value that could not be resolved kind[0] = TypeIds.T_JavaLangString; return "<error>"; //$NON-NLS-1$ } else if (type.isAnnotationType()) { kind[0] = T_AnnotationMirror; return _env.getFactory().newAnnotationMirror(null); } } else if (value instanceof Constant) { if (type instanceof BaseTypeBinding) { kind[0] = ((BaseTypeBinding)type).id; } else if (type.id == TypeIds.T_JavaLangString) { kind[0] = ((Constant)value).typeID(); } else { // error case kind[0] = TypeIds.T_JavaLangString; return "<error>"; //$NON-NLS-1$ } switch (kind[0]) { case T_boolean: return ((Constant)value).booleanValue(); case T_byte: return ((Constant)value).byteValue(); case T_char: return ((Constant)value).charValue(); case T_double: return ((Constant)value).doubleValue(); case T_float: return ((Constant)value).floatValue(); case T_int: try { if (value instanceof LongConstant || value instanceof DoubleConstant || value instanceof FloatConstant) { // error case kind[0] = TypeIds.T_JavaLangString; return "<error>"; //$NON-NLS-1$ } return ((Constant)value).intValue(); } catch (ShouldNotImplement e) { kind[0] = TypeIds.T_JavaLangString; return "<error>"; //$NON-NLS-1$ } case T_JavaLangString: return ((Constant)value).stringValue(); case T_long: return ((Constant)value).longValue(); case T_short: return ((Constant)value).shortValue(); } } } else if (type.isEnum()) { if (value instanceof FieldBinding) { kind[0] = T_EnumConstant; return (VariableElement) _env.getFactory().newElement((FieldBinding) value); } else { kind[0] = TypeIds.T_JavaLangString; return "<error>"; //$NON-NLS-1$ } } else if (type.isAnnotationType()) { if (value instanceof AnnotationBinding) { kind[0] = T_AnnotationMirror; return _env.getFactory().newAnnotationMirror((AnnotationBinding) value); } } else if (value instanceof TypeBinding) { kind[0] = T_ClassObject; return _env.getFactory().newTypeMirror((TypeBinding) value); } // error case kind[0] = TypeIds.T_JavaLangString; return "<error>"; //$NON-NLS-1$ } @SuppressWarnings("unchecked") // Need to cast Object _value to a List<AnnotationValue> @Override public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) { switch (_kind) { case TypeIds.T_boolean: return v.visitBoolean((Boolean)_value, p); case TypeIds.T_byte: return v.visitByte((Byte)_value, p); case TypeIds.T_char: return v.visitChar((Character)_value, p); case TypeIds.T_double: return v.visitDouble((Double)_value, p); case TypeIds.T_float: return v.visitFloat((Float)_value, p); case TypeIds.T_int: return v.visitInt((Integer)_value, p); case TypeIds.T_JavaLangString: return v.visitString((String)_value, p); case TypeIds.T_long: return v.visitLong((Long)_value, p); case TypeIds.T_short: return v.visitShort((Short)_value, p); case T_EnumConstant: return v.visitEnumConstant((VariableElement)_value, p); case T_ClassObject: return v.visitType((TypeMirror)_value, p); case T_AnnotationMirror: return v.visitAnnotation((AnnotationMirror)_value, p); case T_ArrayType: return v.visitArray((List<AnnotationValue>)_value, p); default: return null; } } @Override public Object getValue() { return _value; } @Override public boolean equals(Object obj) { if (obj instanceof AnnotationValueImpl) { return this._value.equals(((AnnotationValueImpl) obj)._value); } return false; } @Override public int hashCode() { return this._value.hashCode() + this._kind; } @Override public String toString() { if (null == _value) { return "null"; //$NON-NLS-1$ } return _value.toString(); } }