blob: b991ac8af0274b52aebe3936d3a555c873dc0b5b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 BEA Systems, Inc.
* 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:
* tyeung@bea.com - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.apt.core.internal.declaration;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.jdt.apt.core.internal.EclipseMirrorImpl;
import org.eclipse.jdt.apt.core.internal.env.ProcessorEnvImpl;
import org.eclipse.jdt.apt.core.internal.util.Factory;
import org.eclipse.jdt.apt.core.internal.util.SourcePositionImpl;
import org.eclipse.jdt.core.dom.*;
import com.sun.mirror.declaration.AnnotationMirror;
import com.sun.mirror.declaration.AnnotationTypeElementDeclaration;
import com.sun.mirror.declaration.AnnotationValue;
import com.sun.mirror.type.AnnotationType;
import com.sun.mirror.util.SourcePosition;
/**
* Annotation instance from source.
*/
public class AnnotationMirrorImpl implements AnnotationMirror, EclipseMirrorImpl
{
/**The ast node that correspond to the annotation.*/
private final IResolvedAnnotation _domAnnotation;
private final ProcessorEnvImpl _env;
/** the declaration that is annotated by this annotation or the annotation element declaration
* if this is (part of) a default value*/
private final DeclarationImpl _annotated;
public AnnotationMirrorImpl(IResolvedAnnotation annotationAstNode, DeclarationImpl decl, ProcessorEnvImpl env)
{
_domAnnotation = annotationAstNode;
_env = env;
_annotated = decl;
assert _domAnnotation != null : "annotation node missing.";
assert _annotated != null : "missing the declaration that is annotated with this annotation.";
}
public AnnotationType getAnnotationType()
{
final ITypeBinding binding = _domAnnotation.getAnnotationType();
return (AnnotationType)Factory.createReferenceType(binding, _env);
}
public Map<AnnotationTypeElementDeclaration, AnnotationValue> getElementValues()
{
final IResolvedMemberValuePair[] pairs = _domAnnotation.getDeclaredMemberValuePairs();
if (pairs.length == 0) {
return Collections.emptyMap();
}
final Map<AnnotationTypeElementDeclaration, AnnotationValue> result =
new LinkedHashMap<AnnotationTypeElementDeclaration, AnnotationValue>(pairs.length * 4 / 3 + 1 );
for( IResolvedMemberValuePair pair : pairs ){
final String name = pair.getName();
if( name == null ) continue;
IMethodBinding elementMethod = pair.getMemberBinding();
if( elementMethod != null ){
final DeclarationImpl mirrorDecl = Factory.createDeclaration(elementMethod, _env);
final AnnotationValue annoValue =
Factory.createAnnotationMemberValue(pair.getValue(), name, this, _env);
if( mirrorDecl.kind() == EclipseMirrorImpl.MirrorKind.ANNOTATION_ELEMENT &&
annoValue != null )
result.put( (AnnotationTypeElementDeclaration)mirrorDecl, annoValue);
}
}
return result;
}
public SourcePosition getPosition()
{
if( isFromSource() ){
final CompilationUnit unit = _annotated.getCompilationUnit();
final org.eclipse.jdt.core.dom.Annotation annotation = getAstNode();
if( annotation == null ) return null;
org.eclipse.jdt.core.dom.ASTNode astNode = annotation.getTypeName();
if( astNode == null )
astNode = annotation;
final int offset = astNode.getStartPosition();
// TODO: waiting on new API Bugzilla #97766
return new SourcePositionImpl(astNode.getStartPosition(),
astNode.getLength(),
unit.lineNumber(offset),
0,//unit.columnNumber(offset),
_annotated);
}
return null;
}
public String toString()
{
return _domAnnotation.toString();
}
/**
* @return the type(s) of the member value named <code>membername</code>.
* If the value is a class literal, then return the type binding corresponding to the type requested.
* Otherwise, return the type of the expression.
* If the value is an array initialization, then the type of each of the initialization expresion will
* be returned. Return null if no match is found.
*/
public ITypeBinding[] getMemberValueTypeBinding(String membername)
{
if( membername == null ) return null;
final IResolvedMemberValuePair[] declaredPairs = _domAnnotation.getDeclaredMemberValuePairs();
for( IResolvedMemberValuePair pair : declaredPairs ){
if( membername.equals(pair.getName()) ){
final Object value = pair.getValue();
return getValueTypeBinding(value, pair.getMemberBinding().getReturnType());
}
}
// didn't find it in the ast, check the default values.
final IMethodBinding binding = getMethodBinding(membername);
if(binding == null ) return null;
final Object defaultValue = binding.getDefaultValue();
if( defaultValue != null )
return getValueTypeBinding(defaultValue, binding.getReturnType() );
else
return null;
}
private ITypeBinding[] getValueTypeBinding(Object value, final ITypeBinding resolvedType)
{
if( value == null ) return null;
if( resolvedType.isPrimitive() || resolvedType.isAnnotation() || value instanceof String )
return new ITypeBinding[]{ resolvedType };
else if( resolvedType.isArray() ){
final Object[] elements = (Object[])value;
final ITypeBinding[] result = new ITypeBinding[elements.length];
final ITypeBinding leafType = resolvedType.getElementType();
for(int i=0, len = elements.length; i<len; i++ ){
final ITypeBinding[] t = getValueTypeBinding(elements[i], leafType);
result[i] = t == null ? null : t[0];
}
return result;
}
else if( value instanceof IVariableBinding )
return new ITypeBinding[]{ ( (IVariableBinding)value ).getDeclaringClass() };
else if( value instanceof ITypeBinding )
return new ITypeBinding[]{ (ITypeBinding)value };
else
throw new IllegalStateException("value = " + value + " resolvedType = " + resolvedType );
}
private ITypeBinding[] getExpressionTypeBindings(Expression expr)
{
if(expr == null) return null;
switch(expr.getNodeType())
{
case ASTNode.ARRAY_INITIALIZER:
final ArrayInitializer arrayInit = (ArrayInitializer)expr;
final List<Expression> exprs = arrayInit.expressions();
if( exprs == null || exprs.size() == 0 )
return new ITypeBinding[0];
final ITypeBinding[] bindings = new ITypeBinding[exprs.size()];
for( int i=0, size = exprs.size(); i<size; i++ ){
final Expression initExpr = exprs.get(i);
bindings[i] = getExpressionTypeBinding(initExpr);
}
return bindings;
default:
return new ITypeBinding[]{ getExpressionTypeBinding(expr) };
}
}
private ITypeBinding getExpressionTypeBinding(Expression expr)
{
if( expr.getNodeType() == ASTNode.TYPE_LITERAL )
return ((TypeLiteral)expr).getType().resolveBinding();
else
return expr.resolveTypeBinding();
}
/**
* @param memberName the name of the member
* @return the value of the given member
*/
private Object getValue(final String memberName)
{
if( memberName == null ) return null;
final IResolvedMemberValuePair[] declaredPairs = _domAnnotation.getDeclaredMemberValuePairs();
for( IResolvedMemberValuePair pair : declaredPairs ){
if( memberName.equals(pair.getName()) ){
return pair.getValue();
}
}
// didn't find it in the ast, check the default values.
final IMethodBinding binding = getMethodBinding(memberName);
if(binding == null ) return null;
return binding.getDefaultValue();
}
/**
* @return the method binding that matches the given name from the annotation type
* referenced by this annotation.
*/
public IMethodBinding getMethodBinding(final String memberName)
{
if( memberName == null ) return null;
final ITypeBinding typeBinding = _domAnnotation.getAnnotationType();
if( typeBinding == null ) return null;
final IMethodBinding[] methods = typeBinding.getDeclaredMethods();
for( IMethodBinding method : methods ){
if( memberName.equals(method.getName()) )
return method;
}
return null;
}
public Object getReflectionValue(String memberName, Method method)
throws Throwable
{
if(memberName == null || memberName.length() == 0 ) return null;
final Class targetType = method.getReturnType();
final Object value = getValue(memberName);
return getReflectionValue(value, targetType);
}
private Object getReflectionValue(final Object value, final Class targetType)
throws Throwable
{
if( value == null ) return null;
else if(value instanceof Boolean ||
value instanceof Byte ||
value instanceof Character ||
value instanceof Double ||
value instanceof Float ||
value instanceof Integer ||
value instanceof Long ||
value instanceof Short ||
value instanceof String )
return value;
else if( value instanceof IVariableBinding )
{
final IVariableBinding varBinding = (IVariableBinding)value;
final ITypeBinding declaringClass = varBinding.getDeclaringClass();
if( declaringClass != null ){
final String className = new String( declaringClass.getBinaryName() );
final Class clazz = value.getClass().getClassLoader().loadClass( className );
final Field returnedField = clazz.getField( varBinding.getName() );
if( returnedField.getType() != targetType )
throw new ClassCastException( targetType.getName() );
return returnedField.get(null);
}
}
else if (value instanceof Object[])
{
final Object[] elements = (Object[])value;
assert targetType.isArray();
final Class componentType = targetType.getComponentType();
final char componentTypeName = componentType.getName().charAt(0);
final int length = elements.length;;
final Object array = Array.newInstance(componentType, length);
if( length == 0) return array;
for( int i=0; i<length; i++ ){
final Object returnObj = getReflectionValue( elements[i], componentType );
// fill in the array.
// If it is an array of some primitive type, we will need to unwrap it.
if( componentType.isPrimitive() ){
if( componentType == boolean.class ){
final Boolean bool = (Boolean)returnObj;
Array.setBoolean( array, i, bool.booleanValue());
}
else if( componentType == byte.class ){
final Byte b = (Byte)returnObj;
Array.setByte( array, i, b.byteValue() );
}
else if( componentType == char.class ){
final Character c = (Character)returnObj;
Array.setChar( array, i, c.charValue() );
}
else if( componentType == double.class ){
final Double d = (Double)returnObj;
Array.setDouble( array, i, d.doubleValue() );
}
else if( componentType == float.class ){
final Float f = (Float)returnObj;
Array.setFloat( array, i, f.floatValue() );
}
else if( componentType == int.class ){
final Integer integer = (Integer)returnObj;
Array.setInt( array, i, integer.intValue() );
}
else if( componentType == long.class ){
final Long l = (Long)returnObj;
Array.setLong( array, i, l.longValue() );
}
else if( componentType == short.class ){
final Short s = (Short)returnObj;
Array.setShort( array, i, s.shortValue() );
}
else {
throw new IllegalStateException("unrecognized primitive type: " + componentType );
}
}
else{
Array.set( array, i, returnObj );
}
}
}
// caller should have caught this case.
else if( value instanceof ITypeBinding )
throw new IllegalStateException();
else if( value instanceof IResolvedAnnotation )
{
return Factory.createAnnotationMirror((IResolvedAnnotation)value, _annotated, _env);
}
return null;
}
public MirrorKind kind(){ return MirrorKind.ANNOTATION_MIRROR; }
boolean isFromSource()
{
return _annotated.isFromSource();
}
org.eclipse.jdt.core.dom.Annotation getAstNode()
{
if( isFromSource() ){
final CompilationUnit unit = _annotated.getCompilationUnit();
final ASTNode node = unit.findDeclaringNode(_domAnnotation);
if( node instanceof org.eclipse.jdt.core.dom.Annotation )
return (org.eclipse.jdt.core.dom.Annotation)node;
}
return null;
}
ASTNode getASTNodeForElement(String name)
{
if( name == null ) return null;
final org.eclipse.jdt.core.dom.Annotation anno = getAstNode();
if( anno != null ){
if( anno.isSingleMemberAnnotation() ){
if( "value".equals(name) )
return ((SingleMemberAnnotation)anno).getValue();
}
else if( anno.isNormalAnnotation() ){
final List<MemberValuePair> pairs = ((NormalAnnotation)anno).values();
for( MemberValuePair pair : pairs )
{
final String pairName = pair.getName() == null ? null : pair.getName().toString();
if( name.equals(pairName) )
return pair.getValue();
}
}
}
// marker annotation or no match.
return null;
}
CompilationUnit getCompilationUnit() { return _annotated.getCompilationUnit(); }
public ProcessorEnvImpl getEnvironment(){ return _env; }
public IFile getResouce()
{ return _annotated.getResource(); }
public DeclarationImpl getAnnotatedDeclaration(){ return _annotated; }
public boolean equals(Object obj){
if( obj instanceof AnnotationMirrorImpl ){
return ((AnnotationMirrorImpl)obj)._domAnnotation == _domAnnotation;
}
return false;
}
public int hashCode(){
return _domAnnotation.hashCode();
}
}