| /******************************************************************************* |
| * Copyright (c) 2005, 2017 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 |
| * het@google.com - Bug 441790 |
| *******************************************************************************/ |
| package org.aspectj.org.eclipse.jdt.internal.compiler.apt.model; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| 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.aspectj.org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.DoubleConstant; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.FloatConstant; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.impl.LongConstant; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeIds; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.util.Util; |
| |
| 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 org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ElementValuePair#getValue()} for possible object types: |
| * <ul> |
| * <li>{@link org.aspectj.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<>(values.length); |
| for (Object oneValue : values) { |
| convertedValues.add(new AnnotationValueImpl(_env, oneValue, valueType)); |
| } |
| } else { |
| convertedValues = new ArrayList<>(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 _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 (_value == null) { |
| return "null"; //$NON-NLS-1$ |
| } else if (_value instanceof String) { |
| String value = (String) _value; |
| StringBuffer sb = new StringBuffer(); |
| sb.append('"'); |
| for (int i = 0; i < value.length(); i++) { |
| Util.appendEscapedChar(sb, value.charAt(i), true); |
| } |
| sb.append('"'); |
| return sb.toString(); |
| } else if (_value instanceof Character) { |
| StringBuffer sb = new StringBuffer(); |
| sb.append('\''); |
| Util.appendEscapedChar(sb, ((Character) _value).charValue(), false); |
| sb.append('\''); |
| return sb.toString(); |
| } else if (_value instanceof VariableElement) { |
| VariableElement enumDecl = (VariableElement) _value; |
| return enumDecl.asType().toString() + "." + enumDecl.getSimpleName(); //$NON-NLS-1$ |
| } else if (_value instanceof Collection) { |
| // It must be Collection<AnnotationValue> |
| @SuppressWarnings("unchecked") |
| Collection<AnnotationValue> values = (Collection<AnnotationValue>) _value; |
| StringBuilder sb = new StringBuilder(); |
| sb.append('{'); |
| boolean first = true; |
| for (AnnotationValue annoValue : values) { |
| if (!first) { |
| sb.append(", "); //$NON-NLS-1$ |
| } |
| first = false; |
| sb.append(annoValue.toString()); |
| } |
| sb.append('}'); |
| return sb.toString(); |
| } else if (_value instanceof TypeMirror) { |
| return _value.toString() + ".class"; //$NON-NLS-1$ |
| } else { |
| return _value.toString(); |
| } |
| } |
| } |