/* *******************************************************************
* Copyright (c) 2008 Contributors
* 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:
* Andy Clement initial implementation
* ******************************************************************/
package org.aspectj.weaver.bcel;
import java.util.List;
import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
import org.aspectj.apache.bcel.classfile.annotation.ElementValue;
import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue;
import org.aspectj.apache.bcel.classfile.annotation.NameValuePair;
import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.UnresolvedType;
/**
* An AnnotationAccessVar represents access to a particular annotation, whilst an AnnotationAccessFieldVar represents access to a
* specific field of that annotation.
*
* @author Andy Clement
*/
class AnnotationAccessFieldVar extends BcelVar {
private AnnotationAccessVar annoAccessor;
private ResolvedType annoFieldOfInterest;
private String name;
private int elementValueType;
public AnnotationAccessFieldVar(AnnotationAccessVar aav, ResolvedType annoFieldOfInterest, String name) {
super(annoFieldOfInterest, 0);
this.annoAccessor = aav;
this.name = name;
String sig = annoFieldOfInterest.getSignature();
if (sig.length() == 1) {
switch (sig.charAt(0)) {
case 'I':
elementValueType = ElementValue.PRIMITIVE_INT;
break;
default:
throw new IllegalStateException(sig);
}
} else if (sig.equals("Ljava/lang/String;")) {
elementValueType = ElementValue.STRING;
} else if (annoFieldOfInterest.isEnum()) {
elementValueType = ElementValue.ENUM_CONSTANT;
} else {
throw new IllegalStateException(sig);
}
this.annoFieldOfInterest = annoFieldOfInterest;
}
@Override
public void appendLoadAndConvert(InstructionList il, InstructionFactory fact, ResolvedType toType) {
// Only possible to do annotation field value extraction at MethodExecution
if (annoAccessor.getKind() != Shadow.MethodExecution) {
return;
}
String annotationOfInterestSignature = annoAccessor.getType().getSignature();
// So we have an entity that has an annotation on and within it is the value we want
Member holder = annoAccessor.getMember();
AnnotationAJ[] annos = holder.getAnnotations();
for (AnnotationAJ anno : annos) {
AnnotationGen annotation = ((BcelAnnotation) anno).getBcelAnnotation();
boolean foundValueInAnnotationUsage = false;
if (annotation.getTypeSignature().equals(annotationOfInterestSignature)) {
ResolvedMember[] annotationFields = toType.getWorld()
.resolve(UnresolvedType.forSignature(annotation.getTypeSignature())).getDeclaredMethods();
// Check how many fields there are of the type we are looking for. If >1 then we'll need
// to use the name to choose the right one
int countOfType = 0;
for (ResolvedMember annotationField : annotationFields) {
if (annotationField.getType().equals(annoFieldOfInterest)) {
countOfType++;
}
}
// this block deals with an annotation that has actual values (i.e. not falling back to default values)
List<NameValuePair> nvps = annotation.getValues();
for (NameValuePair nvp : nvps) {
// If multiple of the same type, match by name
if (countOfType > 1) {
if (!nvp.getNameString().equals(name)) {
continue;
}
}
ElementValue o = nvp.getValue();
if (o.getElementValueType() != elementValueType) {
continue;
}
if (o instanceof EnumElementValue) {
EnumElementValue v = (EnumElementValue) o;
String s = v.getEnumTypeString();
ResolvedType rt = toType.getWorld().resolve(UnresolvedType.forSignature(s));
if (rt.equals(toType)) {
il.append(fact.createGetStatic(rt.getName(), v.getEnumValueString(), Type.getType(rt.getSignature())));
foundValueInAnnotationUsage = true;
}
} else if (o instanceof SimpleElementValue) {
SimpleElementValue v = (SimpleElementValue) o;
switch (v.getElementValueType()) {
case ElementValue.PRIMITIVE_INT:
il.append(fact.createConstant(v.getValueInt()));
foundValueInAnnotationUsage = true;
break;
case ElementValue.STRING:
il.append(fact.createConstant(v.getValueString()));
foundValueInAnnotationUsage = true;
break;
default:
throw new IllegalStateException("NYI: Unsupported annotation value binding for " + o);
}
}
if (foundValueInAnnotationUsage) {
break;
}
}
// this block deals with default values
if (!foundValueInAnnotationUsage) {
for (ResolvedMember annotationField : annotationFields) {
if (countOfType > 1) {
if (!annotationField.getName().equals(name)) {
continue;
}
}
if (!annotationField.getType().getSignature().equals(annoFieldOfInterest.getSignature())) {
continue;
}
if (annotationField.getType().getSignature().equals("I")) {
int ivalue = Integer.parseInt(annotationField.getAnnotationDefaultValue());
il.append(fact.createConstant(ivalue));
foundValueInAnnotationUsage = true;
break;
} else if (annotationField.getType().getSignature().equals("Ljava/lang/String;")) {
String svalue = annotationField.getAnnotationDefaultValue();
il.append(fact.createConstant(svalue));
foundValueInAnnotationUsage = true;
break;
} else {
String dvalue = annotationField.getAnnotationDefaultValue();
// form will be LBLAHBLAHBLAH;X where X is the field within X
String typename = dvalue.substring(0, dvalue.lastIndexOf(';') + 1);
String field = dvalue.substring(dvalue.lastIndexOf(';') + 1);
ResolvedType rt = toType.getWorld().resolve(UnresolvedType.forSignature(typename));
il.append(fact.createGetStatic(rt.getName(), field, Type.getType(rt.getSignature())));
foundValueInAnnotationUsage = true;
break;
}
}
}
}
if (foundValueInAnnotationUsage) {
break;
}
}
}
@Override
public void insertLoad(InstructionList il, InstructionFactory fact) {
// Only possible to do annotation field value extraction at
// MethodExecution
if (annoAccessor.getKind() != Shadow.MethodExecution) {
return;
}
appendLoadAndConvert(il, fact, annoFieldOfInterest);
}
@Override
public String toString() {
return super.toString();
}
}