/* *******************************************************************
* Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Common Public License v1.0
* which accompanies this distribution and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* PARC initial implementation
* ******************************************************************/
package org.aspectj.weaver.patterns;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Map;
import org.aspectj.bridge.MessageUtil;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;
/**
* On creation, type pattern only contains WildTypePattern nodes, not BindingType or ExactType.
*
* <p>Then we call resolveBindings() during compilation
* During concretization of enclosing pointcuts, we call remapAdviceFormals
*
* @author Erik Hilsdale
* @author Jim Hugunin
*/
public abstract class TypePattern extends PatternNode {
public static class MatchKind {
private String name;
public MatchKind(String name) { this.name = name; }
public String toString() { return name; }
}
public static final MatchKind STATIC = new MatchKind("STATIC");
public static final MatchKind DYNAMIC = new MatchKind("DYNAMIC");
public static final TypePattern ELLIPSIS = new EllipsisTypePattern();
public static final TypePattern ANY = new AnyTypePattern();
public static final TypePattern NO = new NoTypePattern();
protected boolean includeSubtypes;
protected boolean isVarArgs = false;
protected AnnotationTypePattern annotationPattern = AnnotationTypePattern.ANY;
protected TypePatternList typeParameters = TypePatternList.EMPTY;
protected TypePattern(boolean includeSubtypes,boolean isVarArgs,TypePatternList typeParams) {
this.includeSubtypes = includeSubtypes;
this.isVarArgs = isVarArgs;
this.typeParameters = (typeParams == null ? TypePatternList.EMPTY : typeParams);
}
protected TypePattern(boolean includeSubtypes, boolean isVarArgs) {
this(includeSubtypes,isVarArgs,null);
}
public AnnotationTypePattern getAnnotationPattern() {
return annotationPattern;
}
public boolean isVarArgs() {
return isVarArgs;
}
public boolean isStarAnnotation() {
return annotationPattern == AnnotationTypePattern.ANY;
}
public boolean isArray() {
return false;
}
protected TypePattern(boolean includeSubtypes) {
this(includeSubtypes,false);
}
public void setAnnotationTypePattern(AnnotationTypePattern annPatt) {
this.annotationPattern = annPatt;
}
public void setTypeParameters(TypePatternList typeParams) {
this.typeParameters = typeParams;
}
public TypePatternList getTypeParameters() {
return this.typeParameters;
}
public void setIsVarArgs(boolean isVarArgs) {
this.isVarArgs = isVarArgs;
}
// answer conservatively...
protected boolean couldEverMatchSameTypesAs(TypePattern other) {
if (this.includeSubtypes || other.includeSubtypes) return true;
if (this.annotationPattern != AnnotationTypePattern.ANY) return true;
if (other.annotationPattern != AnnotationTypePattern.ANY) return true;
return false;
}
//XXX non-final for Not, && and ||
public boolean matchesStatically(ResolvedType type) {
if (includeSubtypes) {
return matchesSubtypes(type);
} else {
return matchesExactly(type);
}
}
public abstract FuzzyBoolean matchesInstanceof(ResolvedType type);
public final FuzzyBoolean matches(ResolvedType type, MatchKind kind) {
FuzzyBoolean typeMatch = null;
//??? This is part of gracefully handling missing references
if (type.isMissing()) return FuzzyBoolean.NO;
if (kind == STATIC) {
// typeMatch = FuzzyBoolean.fromBoolean(matchesStatically(type));
// return typeMatch.and(annotationPattern.matches(type));
return FuzzyBoolean.fromBoolean(matchesStatically(type));
} else if (kind == DYNAMIC) {
//System.err.println("matching: " + this + " with " + type);
// typeMatch = matchesInstanceof(type);
//System.err.println(" got: " + ret);
// return typeMatch.and(annotationPattern.matches(type));
return matchesInstanceof(type);
} else {
throw new IllegalArgumentException("kind must be DYNAMIC or STATIC");
}
}
protected abstract boolean matchesExactly(ResolvedType type);
protected abstract boolean matchesExactly(ResolvedType type, ResolvedType annotatedType);
protected boolean matchesSubtypes(ResolvedType type) {
//System.out.println("matching: " + this + " to " + type);
if (matchesExactly(type)) {
//System.out.println(" true");
return true;
}
// FuzzyBoolean ret = FuzzyBoolean.NO; // ??? -eh
for (Iterator i = type.getDirectSupertypes(); i.hasNext(); ) {
ResolvedType superType = (ResolvedType)i.next();
// TODO asc generics, temporary whilst matching isnt aware..
//if (superType.isParameterizedType()) superType = superType.getRawType().resolve(superType.getWorld());
if (matchesSubtypes(superType,type)) return true;
}
return false;
}
protected boolean matchesSubtypes(ResolvedType superType, ResolvedType annotatedType) {
//System.out.println("matching: " + this + " to " + type);
if (matchesExactly(superType,annotatedType)) {
//System.out.println(" true");
return true;
}
// FuzzyBoolean ret = FuzzyBoolean.NO; // ??? -eh
for (Iterator i = superType.getDirectSupertypes(); i.hasNext(); ) {
ResolvedType superSuperType = (ResolvedType)i.next();
if (matchesSubtypes(superSuperType,annotatedType)) return true;
}
return false;
}
public UnresolvedType resolveExactType(IScope scope, Bindings bindings) {
TypePattern p = resolveBindings(scope, bindings, false, true);
if (p == NO) return ResolvedType.MISSING;
return ((ExactTypePattern)p).getType();
}
public UnresolvedType getExactType() {
if (this instanceof ExactTypePattern) return ((ExactTypePattern)this).getType();
else return ResolvedType.MISSING;
}
protected TypePattern notExactType(IScope s) {
s.getMessageHandler().handleMessage(MessageUtil.error(
WeaverMessages.format(WeaverMessages.EXACT_TYPE_PATTERN_REQD), getSourceLocation()));
return NO;
}
// public boolean assertExactType(IMessageHandler m) {
// if (this instanceof ExactTypePattern) return true;
//
// //XXX should try harder to avoid multiple errors for one problem
// m.handleMessage(MessageUtil.error("exact type pattern required", getSourceLocation()));
// return false;
// }
/**
* This can modify in place, or return a new TypePattern if the type changes.
*/
public TypePattern resolveBindings(IScope scope, Bindings bindings,
boolean allowBinding, boolean requireExactType)
{
annotationPattern = annotationPattern.resolveBindings(scope,bindings,allowBinding);
return this;
}
public void resolve(World world) {
annotationPattern.resolve(world);
}
/**
* return a version of this type pattern in which all type variable references have been
* replaced by their corresponding entry in the map.
*/
public abstract TypePattern parameterizeWith(Map typeVariableMap);
public void postRead(ResolvedType enclosingType) {
}
public boolean isStar() {
return false;
}
/**
* This is called during concretization of pointcuts, it is used by BindingTypePattern
* to return a new BindingTypePattern with a formal index appropiate for the advice,
* rather than for the lexical declaration, i.e. this handles transforamtions through
* named pointcuts.
* <pre>
* pointcut foo(String name): args(name);
* --> This makes a BindingTypePattern(0) pointing to the 0th formal
*
* before(Foo f, String n): this(f) && foo(n) { ... }
* --> when resolveReferences is called on the args from the above, it
* will return a BindingTypePattern(1)
*
* before(Foo f): this(f) && foo(*) { ... }
* --> when resolveReferences is called on the args from the above, it
* will return an ExactTypePattern(String)
* </pre>
*/
public TypePattern remapAdviceFormals(IntMap bindings) {
return this;
}
public static final byte WILD = 1;
public static final byte EXACT = 2;
public static final byte BINDING = 3;
public static final byte ELLIPSIS_KEY = 4;
public static final byte ANY_KEY = 5;
public static final byte NOT = 6;
public static final byte OR = 7;
public static final byte AND = 8;
public static final byte NO_KEY = 9;
public static final byte ANY_WITH_ANNO = 10;
public static final byte HAS_MEMBER = 11;
public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException {
byte key = s.readByte();
switch(key) {
case WILD: return WildTypePattern.read(s, context);
case EXACT: return ExactTypePattern.read(s, context);
case BINDING: return BindingTypePattern.read(s, context);
case ELLIPSIS_KEY: return ELLIPSIS;
case ANY_KEY: return ANY;
case NO_KEY: return NO;
case NOT: return NotTypePattern.read(s, context);
case OR: return OrTypePattern.read(s, context);
case AND: return AndTypePattern.read(s, context);
case ANY_WITH_ANNO: return AnyWithAnnotationTypePattern.read(s,context);
case HAS_MEMBER: return HasMemberTypePattern.read(s,context);
}
throw new BCException("unknown TypePattern kind: " + key);
}
public boolean isIncludeSubtypes() {
return includeSubtypes;
}
}
class EllipsisTypePattern extends TypePattern {
/**
* Constructor for EllipsisTypePattern.
* @param includeSubtypes
*/
public EllipsisTypePattern() {
super(false,false,new TypePatternList());
}
/* (non-Javadoc)
* @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern)
*/
protected boolean couldEverMatchSameTypesAs(TypePattern other) {
return true;
}
/**
* @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType)
*/
protected boolean matchesExactly(ResolvedType type) {
return false;
}
protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) {
return false;
}
/**
* @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType)
*/
public FuzzyBoolean matchesInstanceof(ResolvedType type) {
return FuzzyBoolean.NO;
}
/**
* @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream)
*/
public void write(DataOutputStream s) throws IOException {
s.writeByte(ELLIPSIS_KEY);
}
public String toString() { return ".."; }
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
return (obj instanceof EllipsisTypePattern);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return 17 * 37;
}
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this, data);
}
public TypePattern parameterizeWith(Map typeVariableMap) {
return this;
}
}
class AnyTypePattern extends TypePattern {
/**
* Constructor for EllipsisTypePattern.
* @param includeSubtypes
*/
public AnyTypePattern() {
super(false,false,new TypePatternList());
}
/* (non-Javadoc)
* @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern)
*/
protected boolean couldEverMatchSameTypesAs(TypePattern other) {
return true;
}
/**
* @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType)
*/
protected boolean matchesExactly(ResolvedType type) {
return true;
}
protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) {
return true;
}
/**
* @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType)
*/
public FuzzyBoolean matchesInstanceof(ResolvedType type) {
return FuzzyBoolean.YES;
}
/**
* @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream)
*/
public void write(DataOutputStream s) throws IOException {
s.writeByte(ANY_KEY);
}
/**
* @see org.aspectj.weaver.patterns.TypePattern#matches(IType, MatchKind)
*/
// public FuzzyBoolean matches(IType type, MatchKind kind) {
// return FuzzyBoolean.YES;
// }
/**
* @see org.aspectj.weaver.patterns.TypePattern#matchesSubtypes(IType)
*/
protected boolean matchesSubtypes(ResolvedType type) {
return true;
}
public boolean isStar() {
return true;
}
public String toString() { return "*"; }
public boolean equals(Object obj) {
return (obj instanceof AnyTypePattern);
}
public int hashCode() {
return 37;
}
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this, data);
}
public TypePattern parameterizeWith(Map arg0) {
return this;
}
}
/**
* This type represents a type pattern of '*' but with an annotation specified,
* e.g. '@Color *'
*/
class AnyWithAnnotationTypePattern extends TypePattern {
public AnyWithAnnotationTypePattern(AnnotationTypePattern atp) {
super(false,false);
annotationPattern = atp;
}
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this,data);
}
protected boolean couldEverMatchSameTypesAs(TypePattern other) {
return true;
}
protected boolean matchesExactly(ResolvedType type) {
annotationPattern.resolve(type.getWorld());
return annotationPattern.matches(type).alwaysTrue();
}
protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) {
annotationPattern.resolve(type.getWorld());
return annotationPattern.matches(annotatedType).alwaysTrue();
}
public FuzzyBoolean matchesInstanceof(ResolvedType type) {
if (Modifier.isFinal(type.getModifiers())) {
return FuzzyBoolean.fromBoolean(matchesExactly(type));
}
return FuzzyBoolean.MAYBE;
}
public TypePattern parameterizeWith(Map typeVariableMap) {
AnyWithAnnotationTypePattern ret = new AnyWithAnnotationTypePattern(this.annotationPattern.parameterizeWith(typeVariableMap));
ret.copyLocationFrom(this);
return ret;
}
public void write(DataOutputStream s) throws IOException {
s.writeByte(TypePattern.ANY_WITH_ANNO);
annotationPattern.write(s);
writeLocation(s);
}
public static TypePattern read(VersionedDataInputStream s,ISourceContext c) throws IOException {
AnnotationTypePattern annPatt = AnnotationTypePattern.read(s,c);
AnyWithAnnotationTypePattern ret = new AnyWithAnnotationTypePattern(annPatt);
ret.readLocation(c, s);
return ret;
}
// public FuzzyBoolean matches(IType type, MatchKind kind) {
// return FuzzyBoolean.YES;
// }
protected boolean matchesSubtypes(ResolvedType type) {
return true;
}
public boolean isStar() {
return false;
}
public String toString() { return annotationPattern+" *"; }
public boolean equals(Object obj) {
if (!(obj instanceof AnyWithAnnotationTypePattern)) return false;
AnyWithAnnotationTypePattern awatp = (AnyWithAnnotationTypePattern) obj;
return (annotationPattern.equals(awatp.annotationPattern));
}
public int hashCode() {
return annotationPattern.hashCode();
}
}
class NoTypePattern extends TypePattern {
public NoTypePattern() {
super(false,false,new TypePatternList());
}
/* (non-Javadoc)
* @see org.aspectj.weaver.patterns.TypePattern#couldEverMatchSameTypesAs(org.aspectj.weaver.patterns.TypePattern)
*/
protected boolean couldEverMatchSameTypesAs(TypePattern other) {
return false;
}
/**
* @see org.aspectj.weaver.patterns.TypePattern#matchesExactly(IType)
*/
protected boolean matchesExactly(ResolvedType type) {
return false;
}
protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) {
return false;
}
/**
* @see org.aspectj.weaver.patterns.TypePattern#matchesInstanceof(IType)
*/
public FuzzyBoolean matchesInstanceof(ResolvedType type) {
return FuzzyBoolean.NO;
}
/**
* @see org.aspectj.weaver.patterns.PatternNode#write(DataOutputStream)
*/
public void write(DataOutputStream s) throws IOException {
s.writeByte(NO_KEY);
}
/**
* @see org.aspectj.weaver.patterns.TypePattern#matches(IType, MatchKind)
*/
// public FuzzyBoolean matches(IType type, MatchKind kind) {
// return FuzzyBoolean.YES;
// }
/**
* @see org.aspectj.weaver.patterns.TypePattern#matchesSubtypes(IType)
*/
protected boolean matchesSubtypes(ResolvedType type) {
return false;
}
public boolean isStar() {
return false;
}
public String toString() { return "<nothing>"; }//FIXME AV - bad! toString() cannot be parsed back (not idempotent)
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
return (obj instanceof NoTypePattern);
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return 17 * 37 * 37;
}
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this, data);
}
public TypePattern parameterizeWith(Map arg0) {
return this;
}
}