@@ -464,4 +464,8 @@ public abstract class AbstractPatternNodeVisitor implements PatternNodeVisitor { | |||
public Object visit(HasMemberTypePattern node, Object data) { | |||
return node; | |||
} | |||
public Object visit(TypeCategoryTypePattern node, Object data) { | |||
return node; | |||
} | |||
} |
@@ -44,6 +44,7 @@ public interface PatternNodeVisitor { | |||
Object visit(WildTypePattern node, Object data); | |||
Object visit(TypePatternList node, Object data); | |||
Object visit(HasMemberTypePattern node, Object data); | |||
Object visit(TypeCategoryTypePattern node, Object data); | |||
// Pointcuts | |||
Object visit(AndPointcut node, Object data); |
@@ -868,6 +868,42 @@ public class PatternParser { | |||
} | |||
} | |||
// // Check for a type category | |||
// IToken token = tokenSource.peek(); | |||
// if (token.isIdentifier()) { | |||
// String category = token.getString(); | |||
// TypeCategoryTypePattern typeIsPattern = null; | |||
// if (category.equals("isClass")) { | |||
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.CLASS); | |||
// } else if (category.equals("isAspect")) { | |||
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ASPECT); | |||
// } else if (category.equals("isInterface")) { | |||
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INTERFACE); | |||
// } else if (category.equals("isInner")) { | |||
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INNER); | |||
// } else if (category.equals("isAnonymous")) { | |||
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANONYMOUS); | |||
// } else if (category.equals("isEnum")) { | |||
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ENUM); | |||
// } else if (category.equals("isAnnotation")) { | |||
// typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION); | |||
// } | |||
// if (typeIsPattern != null) { | |||
// tokenSource.next(); | |||
// typeIsPattern.setLocation(tokenSource.getSourceContext(), token.getStart(), token.getEnd()); | |||
// return typeIsPattern; | |||
// } | |||
// } | |||
if (maybeEatIdentifier("is")) { | |||
int pos = tokenSource.getIndex() - 1; | |||
TypePattern typeIsPattern = parseIsTypePattern(); | |||
if (typeIsPattern != null) { | |||
return typeIsPattern; | |||
} | |||
// rewind as if we never tried to parse it as a typeIs | |||
tokenSource.setIndex(pos); | |||
} | |||
List<NamePattern> names = parseDottedNamePattern(); | |||
int dim = 0; | |||
@@ -914,6 +950,75 @@ public class PatternParser { | |||
return ret; | |||
} | |||
/** | |||
* Attempt to parse a typeIs(<category>) construct. If it cannot be parsed we just return null and that should cause the caller | |||
* to reset their position and attempt to consume it in another way. This means we won't have problems here: execution(* | |||
* typeIs(..)) because someone has decided to call a method the same as our construct. | |||
* | |||
* @return a TypeIsTypePattern or null if could not be parsed | |||
*/ | |||
public TypePattern parseIsTypePattern() { | |||
int startPos = tokenSource.peek(-1).getStart(); // that will be the start of the 'typeIs' | |||
if (!maybeEatAdjacent("(")) { | |||
return null; | |||
} | |||
IToken token = tokenSource.next(); | |||
TypeCategoryTypePattern typeIsPattern = null; | |||
if (token.isIdentifier()) { | |||
String category = token.getString(); | |||
if (category.equals("ClassType")) { | |||
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.CLASS); | |||
} else if (category.equals("AspectType")) { | |||
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ASPECT); | |||
} else if (category.equals("InterfaceType")) { | |||
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INTERFACE); | |||
} else if (category.equals("InnerType")) { | |||
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.INNER); | |||
} else if (category.equals("AnonymousType")) { | |||
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANONYMOUS); | |||
} else if (category.equals("EnumType")) { | |||
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ENUM); | |||
} else if (category.equals("AnnotationType")) { | |||
typeIsPattern = new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION); | |||
} | |||
} | |||
if (typeIsPattern == null) { | |||
throw new ParserException("ClassType/AspectType/InterfaceType/InnerType/EnumType/AnnotationType/AnonymousType", token); | |||
} | |||
if (!maybeEat(")")) { | |||
throw new ParserException(")", tokenSource.peek()); | |||
} | |||
int endPos = tokenSource.peek(-1).getEnd(); | |||
typeIsPattern.setLocation(tokenSource.getSourceContext(), startPos, endPos); | |||
return typeIsPattern; | |||
} | |||
// if (names.size() == 1 && !names.get(0).isAny()) { | |||
// if (maybeEatAdjacent("(")) { | |||
// if (maybeEat(")")) { | |||
// // likely to be one of isClass()/isInterface()/isInner()/isAnonymous()/isAspect() | |||
// if (names.size() == 1) { | |||
// NamePattern np = names.get(0); | |||
// String simpleName = np.maybeGetSimpleName(); | |||
// if (simpleName != null) { | |||
// return new TypeCategoryTypePattern(TypeCategoryTypePattern.ANNOTATION, np); | |||
// } else { | |||
// throw new ParserException( | |||
// "not a supported type category, supported are isClass/isInterface/isEnum/isAnnotation/isInner/isAnonymous", | |||
// tokenSource.peek(-3)); | |||
// } | |||
// } | |||
// int stop = 1; | |||
// // return new WildTypePattern(names, includeSubtypes, dim + (isVarArgs ? 1 : 0), endPos, isVarArgs, | |||
// // typeParameters); | |||
// } | |||
// } else { | |||
// throw new ParserException("category type pattern is missing closing parentheses", tokenSource.peek(-2)); | |||
// } | |||
// } | |||
// } | |||
public TypePattern parseHasFieldTypePattern() { | |||
int startPos = tokenSource.peek(-1).getStart(); | |||
eat("("); |
@@ -0,0 +1,129 @@ | |||
/* ******************************************************************* | |||
* Copyright (c) 2010 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 | |||
* ******************************************************************/ | |||
package org.aspectj.weaver.patterns; | |||
import java.io.DataOutputStream; | |||
import java.io.IOException; | |||
import java.util.Map; | |||
import org.aspectj.util.FuzzyBoolean; | |||
import org.aspectj.weaver.ISourceContext; | |||
import org.aspectj.weaver.ResolvedType; | |||
import org.aspectj.weaver.VersionedDataInputStream; | |||
import org.aspectj.weaver.World; | |||
/** | |||
* A TypeCategoryTypePattern matches on the category of a type, one of class/interface/aspect/inner/anonymous/enum/annotation, and | |||
* these are specified in the pointcut via isClass() isInterface() isAspect() isInner() isAnonymous() isEnum() isAnnotation(). | |||
* | |||
* @author Andy Clement | |||
* @since 1.6.9 | |||
*/ | |||
public class TypeCategoryTypePattern extends TypePattern { | |||
public static final int CLASS = 1; | |||
public static final int INTERFACE = 2; | |||
public static final int ASPECT = 3; | |||
public static final int INNER = 4; | |||
public static final int ANONYMOUS = 5; | |||
public static final int ENUM = 6; | |||
public static final int ANNOTATION = 7; | |||
private int category; | |||
private int VERSION = 1; | |||
public TypeCategoryTypePattern(int category) { | |||
super(false); | |||
this.category = category; | |||
} | |||
@Override | |||
protected boolean matchesExactly(ResolvedType type) { | |||
return isRightCategory(type); | |||
} | |||
@Override | |||
protected boolean matchesExactly(ResolvedType type, ResolvedType annotatedType) { | |||
return isRightCategory(type); | |||
} | |||
@Override | |||
public FuzzyBoolean matchesInstanceof(ResolvedType type) { | |||
return FuzzyBoolean.fromBoolean(isRightCategory(type)); | |||
} | |||
@Override | |||
public TypePattern parameterizeWith(Map typeVariableMap, World w) { | |||
return this; | |||
} | |||
@Override | |||
public Object accept(PatternNodeVisitor visitor, Object data) { | |||
return visitor.visit(this, data); | |||
} | |||
@Override | |||
public boolean equals(Object other) { | |||
if (!(other instanceof TypeCategoryTypePattern)) { | |||
return false; | |||
} | |||
TypeCategoryTypePattern o = (TypeCategoryTypePattern) other; | |||
return o.category == category; | |||
} | |||
// TODO is sourcelocation part of the identity or just a 'nice to have' - if important it should be in hashcode/equals | |||
// TODO but if that is the case it needs addressing for all type patterns | |||
@Override | |||
public int hashCode() { | |||
return category * 37; | |||
} | |||
@Override | |||
public void write(DataOutputStream s) throws IOException { | |||
s.writeByte(TypePattern.TYPE_CATEGORY); | |||
s.writeInt(VERSION); | |||
s.writeInt(category); | |||
writeLocation(s); | |||
} | |||
public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { | |||
int version = s.readInt(); | |||
int category = s.readInt(); | |||
TypeCategoryTypePattern tp = new TypeCategoryTypePattern(category); | |||
tp.readLocation(context, s); | |||
return tp; | |||
} | |||
/** | |||
* @return true if the supplied type is of the category specified for this type pattern | |||
*/ | |||
private boolean isRightCategory(ResolvedType type) { | |||
switch (category) { | |||
case CLASS: | |||
return type.isClass(); | |||
case INTERFACE: | |||
return type.isInterface(); | |||
case ASPECT: | |||
return type.isAspect(); | |||
case ANONYMOUS: | |||
return type.isAnonymous(); | |||
case INNER: | |||
return type.isNested(); | |||
case ENUM: | |||
return type.isEnum(); | |||
case ANNOTATION: | |||
return type.isAnnotation(); | |||
} | |||
return false; | |||
} | |||
} |
@@ -300,6 +300,7 @@ public abstract class TypePattern extends PatternNode { | |||
public static final byte NO_KEY = 9; | |||
public static final byte ANY_WITH_ANNO = 10; | |||
public static final byte HAS_MEMBER = 11; | |||
public static final byte TYPE_CATEGORY = 12; | |||
public static TypePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException { | |||
byte key = s.readByte(); | |||
@@ -326,6 +327,8 @@ public abstract class TypePattern extends PatternNode { | |||
return AnyWithAnnotationTypePattern.read(s, context); | |||
case HAS_MEMBER: | |||
return HasMemberTypePattern.read(s, context); | |||
case TYPE_CATEGORY: | |||
return TypeCategoryTypePattern.read(s, context); | |||
} | |||
throw new BCException("unknown TypePattern kind: " + key); | |||
} | |||
@@ -335,15 +338,15 @@ public abstract class TypePattern extends PatternNode { | |||
} | |||
/** | |||
* For quickly recognizing the pattern '!void' | |||
*/ | |||
* For quickly recognizing the pattern '!void' | |||
*/ | |||
public boolean isBangVoid() { | |||
return false; | |||
} | |||
/** | |||
* for quickly recognizing the pattern 'void' | |||
*/ | |||
/** | |||
* for quickly recognizing the pattern 'void' | |||
*/ | |||
public boolean isVoid() { | |||
return false; | |||
} |
@@ -512,6 +512,11 @@ public class DumpPointcutVisitor implements PatternNodeVisitor { | |||
return null; | |||
} | |||
public Object visit(TypeCategoryTypePattern node, Object data) { | |||
append(node); | |||
return null; | |||
} | |||
public static void check(String s) { | |||
check(Pointcut.fromString(s), false); | |||
} |