/* *******************************************************************
* Copyright (c) 2004 IBM Corporation.
* 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
*
* ******************************************************************/
package org.aspectj.weaver.patterns;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.AnnotatedElement;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.ConcreteTypeMunger;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.NewFieldTypeMunger;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.ShadowMunger;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;
import org.aspectj.weaver.ast.Literal;
import org.aspectj.weaver.ast.Test;
import org.aspectj.weaver.ast.Var;
(at)Annotation((at)Foo) or (at)Annotation(foo)
Matches any join point where the subject of the join point has an annotation matching the annotationTypePattern:
Join Point Kind - Subject
================================
method call - the target method
method execution - the method
constructor call - the constructor
constructor execution - the constructor
get - the target field
set - the target field
adviceexecution - the advice
initialization - the constructor
preinitialization - the constructor
staticinitialization - the type being initialized
handler - the declared type of the handled exception
/**
* (at)Annotation((at)Foo) or (at)Annotation(foo)<br>
* <p>
* Matches any join point where the subject of the join point has an annotation matching the annotationTypePattern:
*
* <br>
* Join Point Kind - Subject <br>
* ================================ <br>
* method call - the target method <br>
* method execution - the method <br>
* constructor call - the constructor <br>
* constructor execution - the constructor <br>
* get - the target field <br>
* set - the target field <br>
* adviceexecution - the advice <br>
* initialization - the constructor <br>
* preinitialization - the constructor <br>
* staticinitialization - the type being initialized <br>
* handler - the declared type of the handled exception <br>
*/
public class AnnotationPointcut extends NameBindingPointcut {
private ExactAnnotationTypePattern annotationTypePattern;
private String declarationText;
public AnnotationPointcut(ExactAnnotationTypePattern type) {
super();
this.annotationTypePattern = type;
this.pointcutKind = Pointcut.ANNOTATION;
buildDeclarationText();
}
public AnnotationPointcut(ExactAnnotationTypePattern type, ShadowMunger munger) {
this(type);
buildDeclarationText();
}
public ExactAnnotationTypePattern getAnnotationTypePattern() {
return annotationTypePattern;
}
@Override
public int couldMatchKinds() {
return Shadow.ALL_SHADOW_KINDS_BITS;
}
@Override
public Pointcut parameterizeWith(Map<String,UnresolvedType> typeVariableMap, World w) {
AnnotationPointcut ret = new AnnotationPointcut((ExactAnnotationTypePattern) annotationTypePattern.parameterizeWith(
typeVariableMap, w));
ret.copyLocationFrom(this);
return ret;
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.Pointcut#fastMatch(org.aspectj.weaver.patterns.FastMatchInfo)
*/
@Override
public FuzzyBoolean fastMatch(FastMatchInfo info) {
if (info.getKind() == Shadow.StaticInitialization) {
return annotationTypePattern.fastMatches(info.getType());
} else {
return FuzzyBoolean.MAYBE;
}
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.Pointcut#match(org.aspectj.weaver.Shadow)
*/
@Override
protected FuzzyBoolean matchInternal(Shadow shadow) {
AnnotatedElement toMatchAgainst = null;
Member member = shadow.getSignature();
ResolvedMember rMember = member.resolve(shadow.getIWorld());
if (rMember == null) {
if (member.getName().startsWith(NameMangler.PREFIX)) {
return FuzzyBoolean.NO;
}
shadow.getIWorld().getLint().unresolvableMember.signal(member.toString(), getSourceLocation());
return FuzzyBoolean.NO;
}
Shadow.Kind kind = shadow.getKind();
if (kind == Shadow.StaticInitialization) {
toMatchAgainst = rMember.getDeclaringType().resolve(shadow.getIWorld());
} else if ((kind == Shadow.ExceptionHandler)) {
toMatchAgainst = rMember.getParameterTypes()[0].resolve(shadow.getIWorld());
} else {
toMatchAgainst = rMember;
// FIXME asc I'd like to get rid of this bit of logic altogether, shame ITD fields don't have an effective sig attribute
// FIXME asc perf cache the result of discovering the member that contains the real annotations
if (rMember.isAnnotatedElsewhere()) {
if (kind == Shadow.FieldGet || kind == Shadow.FieldSet) {
// FIXME asc should include supers with getInterTypeMungersIncludingSupers ?
List mungers = rMember.getDeclaringType().resolve(shadow.getIWorld()).getInterTypeMungers();
for (Iterator iter = mungers.iterator(); iter.hasNext();) {
ConcreteTypeMunger typeMunger = (ConcreteTypeMunger) iter.next();
if (typeMunger.getMunger() instanceof NewFieldTypeMunger) {
ResolvedMember fakerm = typeMunger.getSignature();
if (fakerm.equals(member)) {
ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm, typeMunger.getAspectType());
ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod);
toMatchAgainst = rmm;
}
}
}
}
}
}
annotationTypePattern.resolve(shadow.getIWorld());
return annotationTypePattern.matches(toMatchAgainst);
}
private ResolvedMember findMethod(ResolvedType aspectType, ResolvedMember ajcMethod) {
ResolvedMember decMethods[] = aspectType.getDeclaredMethods();
for (int i = 0; i < decMethods.length; i++) {
ResolvedMember member = decMethods[i];
if (member.equals(ajcMethod)) {
return member;
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.Pointcut#resolveBindings(org.aspectj.weaver.patterns.IScope,
* org.aspectj.weaver.patterns.Bindings)
*/
@Override
protected void resolveBindings(IScope scope, Bindings bindings) {
if (!scope.getWorld().isInJava5Mode()) {
scope.message(MessageUtil.error(WeaverMessages.format(WeaverMessages.ATANNOTATION_ONLY_SUPPORTED_AT_JAVA5_LEVEL),
getSourceLocation()));
return;
}
annotationTypePattern = (ExactAnnotationTypePattern) annotationTypePattern.resolveBindings(scope, bindings, true);
// must be either a Var, or an annotation type pattern
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.Pointcut#concretize1(org.aspectj.weaver.ResolvedType, org.aspectj.weaver.IntMap)
*/
@Override
protected Pointcut concretize1(ResolvedType inAspect, ResolvedType declaringType, IntMap bindings) {
ExactAnnotationTypePattern newType = (ExactAnnotationTypePattern) annotationTypePattern.remapAdviceFormals(bindings);
Pointcut ret = new AnnotationPointcut(newType, bindings.getEnclosingAdvice());
ret.copyLocationFrom(this);
return ret;
}
@Override
protected Test findResidueInternal(Shadow shadow, ExposedState state) {
if (annotationTypePattern instanceof BindingAnnotationFieldTypePattern) {
if (shadow.getKind() != Shadow.MethodExecution) {
shadow.getIWorld()
.getMessageHandler()
.handleMessage(
MessageUtil
.error("Annotation field binding is only supported at method-execution join points (compiler limitation)",
getSourceLocation()));
return Literal.TRUE; // exit quickly, error will prevent weaving
}
BindingAnnotationFieldTypePattern btp = (BindingAnnotationFieldTypePattern) annotationTypePattern;
ResolvedType formalType = btp.getFormalType().resolve(shadow.getIWorld());
UnresolvedType annoType = btp.getAnnotationType();
// TODO 2 need to sort out appropriate creation of the AnnotationAccessFieldVar - what happens for
// reflective (ReflectionShadow) access to types?
Var var = shadow.getKindedAnnotationVar(annoType);
if (var == null) {
throw new BCException("Unexpected problem locating annotation at join point '" + shadow + "'");
}
state.set(btp.getFormalIndex(), var.getAccessorForValue(formalType, btp.formalName));
} else if (annotationTypePattern instanceof BindingAnnotationTypePattern) {
BindingAnnotationTypePattern btp = (BindingAnnotationTypePattern) annotationTypePattern;
UnresolvedType annotationType = btp.getAnnotationType();
Var var = shadow.getKindedAnnotationVar(annotationType);
// At this point, var *could* be null. The only reason this could happen (if we aren't failing...)
// is if another binding annotation designator elsewhere in the pointcut is going to expose the annotation
// eg. (execution(* a*(..)) && @annotation(foo)) || (execution(* b*(..)) && @this(foo))
// where sometimes @annotation will be providing the value, and sometimes
// @this will be providing the value (see pr138223)
// If we are here for other indecipherable reasons (it's not the case above...) then
// you might want to uncomment this next bit of code to collect the diagnostics
// if (var == null) throw new BCException("Impossible! annotation=["+annotationType+
// "] shadow=["+shadow+" at "+shadow.getSourceLocation()+
// "] pointcut is at ["+getSourceLocation()+"]");
if (var == null) {
if (matchInternal(shadow).alwaysTrue()) {
return Literal.TRUE;
} else {
return Literal.FALSE;
}
}
state.set(btp.getFormalIndex(), var);
}
if (matchInternal(shadow).alwaysTrue()) {
return Literal.TRUE;
} else {
return Literal.FALSE;
}
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingAnnotationTypePatterns()
*/
@Override
public List<BindingPattern> getBindingAnnotationTypePatterns() {
if (annotationTypePattern instanceof BindingPattern) { // BindingAnnotationTypePattern) {
List<BindingPattern> l = new ArrayList<BindingPattern>();
l.add((BindingPattern)annotationTypePattern);
return l;
} else {
return Collections.emptyList();
}
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.NameBindingPointcut#getBindingTypePatterns()
*/
@Override
public List<BindingTypePattern> getBindingTypePatterns() {
return Collections.emptyList();
}
/*
* (non-Javadoc)
*
* @see org.aspectj.weaver.patterns.PatternNode#write(java.io.DataOutputStream)
*/
@Override
public void write(CompressingDataOutputStream s) throws IOException {
s.writeByte(Pointcut.ANNOTATION);
annotationTypePattern.write(s);
writeLocation(s);
}
public static Pointcut read(VersionedDataInputStream s, ISourceContext context) throws IOException {
AnnotationTypePattern type = AnnotationTypePattern.read(s, context);
AnnotationPointcut ret = new AnnotationPointcut((ExactAnnotationTypePattern) type);
ret.readLocation(context, s);
return ret;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof AnnotationPointcut)) {
return false;
}
AnnotationPointcut o = (AnnotationPointcut) other;
return o.annotationTypePattern.equals(this.annotationTypePattern);
}
@Override
public int hashCode() {
int result = 17;
result = 37 * result + annotationTypePattern.hashCode();
return result;
}
public void buildDeclarationText() {
StringBuffer buf = new StringBuffer();
buf.append("@annotation(");
String annPatt = annotationTypePattern.toString();
buf.append(annPatt.startsWith("@") ? annPatt.substring(1) : annPatt);
buf.append(")");
this.declarationText = buf.toString();
}
@Override
public String toString() {
return this.declarationText;
}
@Override
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}