org.aspectj.weaver.patterns.SignaturePattern.java Source code

Java tutorial

Introduction

Here is the source code for org.aspectj.weaver.patterns.SignaturePattern.java

Source

/* *******************************************************************
 * 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 Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *     PARC     initial implementation 
 * ******************************************************************/

package org.aspectj.weaver.patterns;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.aspectj.bridge.ISourceLocation;
import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.AnnotationTargetKind;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.ConcreteTypeMunger;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.JoinPointSignature;
import org.aspectj.weaver.JoinPointSignatureIterator;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.MemberKind;
import org.aspectj.weaver.NewFieldTypeMunger;
import org.aspectj.weaver.ResolvableTypeList;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.World;

public class SignaturePattern extends PatternNode implements ISignaturePattern {
    private MemberKind kind;
    private ModifiersPattern modifiers;
    private TypePattern returnType;
    private TypePattern declaringType;
    private NamePattern name;
    private TypePatternList parameterTypes;
    private int bits = 0x0000;
    private static final int PARAMETER_ANNOTATION_MATCHING = 0x0001;
    private static final int CHECKED_FOR_PARAMETER_ANNOTATION_MATCHING = 0x0002;

    private ThrowsPattern throwsPattern;
    private AnnotationTypePattern annotationPattern;
    private transient int hashcode = -1;

    private transient boolean isExactDeclaringTypePattern = false;

    public SignaturePattern(MemberKind kind, ModifiersPattern modifiers, TypePattern returnType,
            TypePattern declaringType, NamePattern name, TypePatternList parameterTypes,
            ThrowsPattern throwsPattern, AnnotationTypePattern annotationPattern) {
        this.kind = kind;
        this.modifiers = modifiers;
        this.returnType = returnType;
        this.name = name;
        this.declaringType = declaringType;
        this.parameterTypes = parameterTypes;
        this.throwsPattern = throwsPattern;
        this.annotationPattern = annotationPattern;
        this.isExactDeclaringTypePattern = (declaringType instanceof ExactTypePattern);
    }

    @Override
    public SignaturePattern resolveBindings(IScope scope, Bindings bindings) {
        if (returnType != null) {
            returnType = returnType.resolveBindings(scope, bindings, false, false);
            checkForIncorrectTargetKind(returnType, scope, false);
        }
        if (declaringType != null) {
            declaringType = declaringType.resolveBindings(scope, bindings, false, false);
            checkForIncorrectTargetKind(declaringType, scope, false);
            isExactDeclaringTypePattern = (declaringType instanceof ExactTypePattern);
        }
        if (parameterTypes != null) {
            parameterTypes = parameterTypes.resolveBindings(scope, bindings, false, false);
            checkForIncorrectTargetKind(parameterTypes, scope, false, true);
        }
        if (throwsPattern != null) {
            throwsPattern = throwsPattern.resolveBindings(scope, bindings);
            if (throwsPattern.getForbidden().getTypePatterns().length > 0
                    || throwsPattern.getRequired().getTypePatterns().length > 0) {
                checkForIncorrectTargetKind(throwsPattern, scope, false);
            }
        }
        if (annotationPattern != null) {
            annotationPattern = annotationPattern.resolveBindings(scope, bindings, false);
            checkForIncorrectTargetKind(annotationPattern, scope, true);
        }
        hashcode = -1;
        return this;
    }

    private void checkForIncorrectTargetKind(PatternNode patternNode, IScope scope,
            boolean targetsOtherThanTypeAllowed) {
        checkForIncorrectTargetKind(patternNode, scope, targetsOtherThanTypeAllowed, false);

    }

    // bug 115252 - adding an xlint warning if the annnotation target type is
    // wrong. This logic, or similar, may have to be applied elsewhere in the case
    // of pointcuts which don't go through SignaturePattern.resolveBindings(..)
    private void checkForIncorrectTargetKind(PatternNode patternNode, IScope scope,
            boolean targetsOtherThanTypeAllowed, boolean parameterTargettingAnnotationsAllowed) {
        // return if we're not in java5 mode, if the unmatchedTargetKind Xlint
        // warning has been turned off, or if the patternNode is *
        if (!scope.getWorld().isInJava5Mode() || scope.getWorld().getLint().unmatchedTargetKind == null
                || (patternNode instanceof AnyTypePattern)) {
            return;
        }
        if (patternNode instanceof ExactAnnotationTypePattern) {
            ResolvedType resolvedType = ((ExactAnnotationTypePattern) patternNode).getAnnotationType()
                    .resolve(scope.getWorld());
            if (targetsOtherThanTypeAllowed) {
                AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds();
                if (targetKinds == null) {
                    return;
                }
                reportUnmatchedTargetKindMessage(targetKinds, patternNode, scope, true);
            } else if (!targetsOtherThanTypeAllowed && !resolvedType.canAnnotationTargetType()) {
                // everything is incorrect since we've already checked whether we have the TYPE target annotation
                AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds();
                if (targetKinds == null) {
                    return;
                }
                reportUnmatchedTargetKindMessage(targetKinds, patternNode, scope, false);
            }
        } else {
            TypePatternVisitor visitor = new TypePatternVisitor(scope, targetsOtherThanTypeAllowed,
                    parameterTargettingAnnotationsAllowed);
            patternNode.traverse(visitor, null);
            if (visitor.containedIncorrectTargetKind()) {
                Set<ExactAnnotationTypePattern> keys = visitor.getIncorrectTargetKinds().keySet();
                for (Iterator<ExactAnnotationTypePattern> iter = keys.iterator(); iter.hasNext();) {
                    PatternNode node = iter.next();
                    AnnotationTargetKind[] targetKinds = visitor.getIncorrectTargetKinds().get(node);
                    reportUnmatchedTargetKindMessage(targetKinds, node, scope, false);
                }
            }
        }
    }

    private void reportUnmatchedTargetKindMessage(AnnotationTargetKind[] annotationTargetKinds, PatternNode node,
            IScope scope, boolean checkMatchesMemberKindName) {
        StringBuffer targetNames = new StringBuffer("{");
        for (int i = 0; i < annotationTargetKinds.length; i++) {
            AnnotationTargetKind targetKind = annotationTargetKinds[i];
            if (checkMatchesMemberKindName && kind.getName().equals(targetKind.getName())) {
                return;
            }
            if (i < (annotationTargetKinds.length - 1)) {
                targetNames.append("ElementType." + targetKind.getName() + ",");
            } else {
                targetNames.append("ElementType." + targetKind.getName() + "}");
            }
        }
        scope.getWorld().getLint().unmatchedTargetKind.signal(
                new String[] { node.toString(), targetNames.toString() }, getSourceLocation(),
                new ISourceLocation[0]);
    }

    /**
     * Class which visits the nodes in the TypePattern tree until an ExactTypePattern is found. Once this is found it creates a new
     * ExactAnnotationTypePattern and checks whether the targetKind (created via the @Target annotation) matches ElementType.TYPE if
     * this is the only target kind which is allowed, or matches the signature pattern kind if there is no restriction.
     */
    private class TypePatternVisitor extends AbstractPatternNodeVisitor {

        private IScope scope;
        private Map<ExactAnnotationTypePattern, AnnotationTargetKind[]> incorrectTargetKinds = new HashMap<ExactAnnotationTypePattern, AnnotationTargetKind[]>();
        private boolean targetsOtherThanTypeAllowed;
        private boolean parameterTargettingAnnotationsAllowed;

        /**
         * @param requiredTarget - the signature pattern Kind
         * @param scope
         * @param parameterTargettingAnnotationsAllowed
         */
        public TypePatternVisitor(IScope scope, boolean targetsOtherThanTypeAllowed,
                boolean parameterTargettingAnnotationsAllowed) {
            this.scope = scope;
            this.targetsOtherThanTypeAllowed = targetsOtherThanTypeAllowed;
            this.parameterTargettingAnnotationsAllowed = parameterTargettingAnnotationsAllowed;
        }

        @Override
        public Object visit(WildAnnotationTypePattern node, Object data) {
            node.getTypePattern().accept(this, data);
            return node;
        }

        /**
         * Do the ExactAnnotationTypePatterns have the incorrect target?
         */
        @Override
        public Object visit(ExactAnnotationTypePattern node, Object data) {
            ResolvedType resolvedType = node.getAnnotationType().resolve(scope.getWorld());
            if (targetsOtherThanTypeAllowed) {
                AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds();
                if (targetKinds == null) {
                    return data;
                }
                List<AnnotationTargetKind> incorrectTargets = new ArrayList<AnnotationTargetKind>();
                for (int i = 0; i < targetKinds.length; i++) {
                    if (targetKinds[i].getName().equals(kind.getName())
                            || (targetKinds[i].getName().equals("PARAMETER")
                                    && node.isForParameterAnnotationMatch())) {
                        return data;
                    }
                    incorrectTargets.add(targetKinds[i]);
                }
                if (incorrectTargets.isEmpty()) {
                    return data;
                }
                AnnotationTargetKind[] kinds = new AnnotationTargetKind[incorrectTargets.size()];
                incorrectTargetKinds.put(node, incorrectTargets.toArray(kinds));
            } else if (!targetsOtherThanTypeAllowed && !resolvedType.canAnnotationTargetType()) {
                AnnotationTargetKind[] targetKinds = resolvedType.getAnnotationTargetKinds();
                if (targetKinds == null) {
                    return data;
                }
                // exception here is if parameter annotations are allowed
                if (parameterTargettingAnnotationsAllowed) {
                    for (int i = 0; i < targetKinds.length; i++) {
                        AnnotationTargetKind annotationTargetKind = targetKinds[i];
                        if (annotationTargetKind.getName().equals("PARAMETER")
                                && node.isForParameterAnnotationMatch()) {
                            return data;
                        }
                    }
                }
                incorrectTargetKinds.put(node, targetKinds);
            }
            return data;
        }

        @Override
        public Object visit(ExactTypePattern node, Object data) {
            ExactAnnotationTypePattern eatp = new ExactAnnotationTypePattern(
                    node.getExactType().resolve(scope.getWorld()), null);
            eatp.accept(this, data);
            return data;
        }

        @Override
        public Object visit(AndTypePattern node, Object data) {
            node.getLeft().accept(this, data);
            node.getRight().accept(this, data);
            return node;
        }

        @Override
        public Object visit(OrTypePattern node, Object data) {
            node.getLeft().accept(this, data);
            node.getRight().accept(this, data);
            return node;
        }

        @Override
        public Object visit(AnyWithAnnotationTypePattern node, Object data) {
            node.getAnnotationPattern().accept(this, data);
            return node;
        }

        public boolean containedIncorrectTargetKind() {
            return (incorrectTargetKinds.size() != 0);
        }

        public Map<ExactAnnotationTypePattern, AnnotationTargetKind[]> getIncorrectTargetKinds() {
            return incorrectTargetKinds;
        }
    }

    public void postRead(ResolvedType enclosingType) {
        if (returnType != null) {
            returnType.postRead(enclosingType);
        }
        if (declaringType != null) {
            declaringType.postRead(enclosingType);
        }
        if (parameterTypes != null) {
            parameterTypes.postRead(enclosingType);
        }
    }

    /**
     * return a copy of this signature pattern in which every type variable reference is replaced by the corresponding entry in the
     * map.
     */
    @Override
    public SignaturePattern parameterizeWith(Map<String, UnresolvedType> typeVariableMap, World w) {
        SignaturePattern ret = new SignaturePattern(kind, modifiers,
                returnType.parameterizeWith(typeVariableMap, w), declaringType.parameterizeWith(typeVariableMap, w),
                name, parameterTypes.parameterizeWith(typeVariableMap, w),
                throwsPattern.parameterizeWith(typeVariableMap, w),
                annotationPattern.parameterizeWith(typeVariableMap, w));
        ret.copyLocationFrom(this);
        return ret;
    }

    @Override
    public boolean matches(Member joinPointSignature, World world, boolean allowBridgeMethods) {
        // fail (or succeed!) fast tests...
        if (joinPointSignature == null) {
            return false;
        }
        if (kind != joinPointSignature.getKind()) {
            return false;
        }
        if (kind == Member.ADVICE) {
            return true;
        }

        // do the hard work then...
        boolean subjectMatch = true;
        boolean wantsAnnotationMatch = wantToMatchAnnotationPattern();
        JoinPointSignatureIterator candidateMatches = joinPointSignature.getJoinPointSignatures(world);
        while (candidateMatches.hasNext()) {
            JoinPointSignature aSig = candidateMatches.next();
            // System.out.println(aSig);
            FuzzyBoolean matchResult = matchesExactly(aSig, world, allowBridgeMethods, subjectMatch);
            if (matchResult.alwaysTrue()) {
                return true;
            } else if (matchResult.alwaysFalse()) {
                return false;
            }
            // if we got a "MAYBE" it's worth looking at the other signatures
            // The first signature is the subject signature - and against it we must match modifiers/annotations/throws
            // see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html
            subjectMatch = false;
            // Early exit
            if (wantsAnnotationMatch) {
                return false;
            }
        }
        return false;
    }

    // Does this pattern match this exact signature (no declaring type mucking about
    // or chasing up the hierarchy)
    // return YES if it does, NO if it doesn't and no ancester member could match either,
    // and MAYBE if it doesn't but an ancester member could.
    private FuzzyBoolean matchesExactly(JoinPointSignature aMember, World inAWorld, boolean allowBridgeMethods,
            boolean subjectMatch) {
        // Java5 introduces bridge methods, we match a call to them but nothing else...
        if (aMember.isBridgeMethod() && !allowBridgeMethods) {
            return FuzzyBoolean.MAYBE;
        }

        // Only the subject is checked for modifiers
        // see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html
        if (subjectMatch && !modifiers.matches(aMember.getModifiers())) {
            return FuzzyBoolean.NO;
        }

        FuzzyBoolean matchesIgnoringAnnotations = FuzzyBoolean.YES;
        if (kind == Member.STATIC_INITIALIZATION) {
            matchesIgnoringAnnotations = matchesExactlyStaticInitialization(aMember, inAWorld);
        } else if (kind == Member.FIELD) {
            matchesIgnoringAnnotations = matchesExactlyField(aMember, inAWorld);
        } else if (kind == Member.METHOD) {
            matchesIgnoringAnnotations = matchesExactlyMethod(aMember, inAWorld, subjectMatch);
        } else if (kind == Member.CONSTRUCTOR) {
            matchesIgnoringAnnotations = matchesExactlyConstructor(aMember, inAWorld);
        }
        if (matchesIgnoringAnnotations.alwaysFalse()) {
            return FuzzyBoolean.NO;
        }

        // Only the subject is checked for annotations (239441/119749)
        // see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html
        if (subjectMatch) {
            // The annotations must match if specified
            if (!matchesAnnotations(aMember, inAWorld).alwaysTrue()) {
                return FuzzyBoolean.NO;
            } else {
                return matchesIgnoringAnnotations;
            }
        } else {
            // Unless they specified any annotation then it is a failure
            if (annotationPattern instanceof AnyAnnotationTypePattern) {
                return matchesIgnoringAnnotations;
            } else {
                return FuzzyBoolean.NO;
            }
        }

        // if (subjectMatch && !matchesAnnotations(aMember, inAWorld).alwaysTrue()) {
        // return FuzzyBoolean.NO;
        // } else {
        //         
        // return matchesIgnoringAnnotations;
        // }

    }

    private boolean wantToMatchAnnotationPattern() {
        return !(annotationPattern instanceof AnyAnnotationTypePattern);
    }

    /**
     * Matches on declaring type
     */
    private FuzzyBoolean matchesExactlyStaticInitialization(JoinPointSignature aMember, World world) {
        return FuzzyBoolean.fromBoolean(declaringType.matchesStatically(aMember.getDeclaringType().resolve(world)));
    }

    /**
     * Matches on name, declaring type, field type
     */
    private FuzzyBoolean matchesExactlyField(JoinPointSignature aField, World world) {
        if (!name.matches(aField.getName())) {
            return FuzzyBoolean.NO;
        }
        ResolvedType fieldDeclaringType = aField.getDeclaringType().resolve(world);
        if (!declaringType.matchesStatically(fieldDeclaringType)) {
            return FuzzyBoolean.MAYBE;
        }
        if (!returnType.matchesStatically(aField.getReturnType().resolve(world))) {
            // looking bad, but there might be parameterization to consider...
            if (!returnType.matchesStatically(aField.getGenericReturnType().resolve(world))) {
                // ok, it's bad.
                return FuzzyBoolean.MAYBE;
            }
        }
        // passed all the guards...
        return FuzzyBoolean.YES;
    }

    /**
     * Quickly detect if the joinpoint absolutely cannot match becaused the method parameters at the joinpoint cannot match against
     * this signature pattern.
     * 
     * @param methodJoinpoint the joinpoint to quickly match against
     * @return true if it is impossible for the joinpoint to match this signature
     */
    private boolean parametersCannotMatch(JoinPointSignature methodJoinpoint) {
        if (methodJoinpoint.isVarargsMethod()) {
            // just give up early (for now)
            return false;
        }

        int patternParameterCount = parameterTypes.size();

        if (patternParameterCount == 0 || parameterTypes.ellipsisCount == 0) {
            boolean equalCount = patternParameterCount == methodJoinpoint.getParameterTypes().length;

            // Quick rule: pattern specifies zero parameters, and joinpoint has parameters *OR*
            if (patternParameterCount == 0 && !equalCount) {
                return true;
            }

            // Quick rule: pattern doesn't specify ellipsis and there are a different number of parameters on the
            // method join point as compared with the pattern
            if (parameterTypes.ellipsisCount == 0 && !equalCount) {
                if (patternParameterCount > 0 && parameterTypes.get(patternParameterCount - 1).isVarArgs()) {
                    return false;
                }
                return true;
            }
        }

        return false;
    }

    /**
     * Matches on name, declaring type, return type, parameter types, throws types
     */
    private FuzzyBoolean matchesExactlyMethod(JoinPointSignature aMethod, World world, boolean subjectMatch) {
        if (parametersCannotMatch(aMethod)) {
            // System.err.println("Parameter types pattern " + parameterTypes + " pcount: " + aMethod.getParameterTypes().length);
            return FuzzyBoolean.NO;
        }
        // OPTIMIZE only for exact match do the pattern match now? Otherwise defer it until other fast checks complete?
        if (!name.matches(aMethod.getName())) {
            return FuzzyBoolean.NO;
        }
        // Check the throws pattern
        if (subjectMatch && !throwsPattern.matches(aMethod.getExceptions(), world)) {
            return FuzzyBoolean.NO;
        }

        // '*' trivially matches everything, no need to check further
        if (!declaringType.isStar()) {
            if (!declaringType.matchesStatically(aMethod.getDeclaringType().resolve(world))) {
                return FuzzyBoolean.MAYBE;
            }
        }

        // '*' would match any return value
        if (!returnType.isStar()) {
            boolean b = returnType.isBangVoid();
            if (b) {
                String s = aMethod.getReturnType().getSignature();
                if (s.length() == 1 && s.charAt(0) == 'V') {
                    // it is void, so not a match
                    return FuzzyBoolean.NO;
                }
            } else {
                if (returnType.isVoid()) {
                    String s = aMethod.getReturnType().getSignature();
                    if (s.length() != 1 || s.charAt(0) != 'V') {
                        // it is not void, so not a match
                        return FuzzyBoolean.NO;
                    }
                } else {
                    if (!returnType.matchesStatically(aMethod.getReturnType().resolve(world))) {
                        // looking bad, but there might be parameterization to consider...
                        if (!returnType.matchesStatically(aMethod.getGenericReturnType().resolve(world))) {
                            // ok, it's bad.
                            return FuzzyBoolean.MAYBE;
                        }
                    }
                }
            }
        }

        // The most simple case: pattern is (..) will match anything
        if (parameterTypes.size() == 1 && parameterTypes.get(0).isEllipsis()) {
            return FuzzyBoolean.YES;
        }

        if (!parameterTypes.canMatchSignatureWithNParameters(aMethod.getParameterTypes().length)) {
            return FuzzyBoolean.NO;
        }

        // OPTIMIZE both resolution of these types and their annotations should be deferred - just pass down a world and do it lower
        // down
        // ResolvedType[] resolvedParameters = world.resolve(aMethod.getParameterTypes());

        ResolvableTypeList rtl = new ResolvableTypeList(world, aMethod.getParameterTypes());
        // Only fetch the parameter annotations if the pointcut is going to be matching on them
        ResolvedType[][] parameterAnnotationTypes = null;
        if (isMatchingParameterAnnotations()) {
            parameterAnnotationTypes = aMethod.getParameterAnnotationTypes();
            if (parameterAnnotationTypes != null && parameterAnnotationTypes.length == 0) {
                parameterAnnotationTypes = null;
            }
        }

        if (!parameterTypes.matches(rtl, TypePattern.STATIC, parameterAnnotationTypes).alwaysTrue()) {
            // It could still be a match based on the generic sig parameter types of a parameterized type
            if (!parameterTypes.matches(new ResolvableTypeList(world, aMethod.getGenericParameterTypes()),
                    TypePattern.STATIC, parameterAnnotationTypes).alwaysTrue()) {
                return FuzzyBoolean.MAYBE;
                // It could STILL be a match based on the erasure of the parameter types??
                // to be determined via test cases...
            }
        }

        // check that varargs specifications match
        if (!matchesVarArgs(aMethod, world)) {
            return FuzzyBoolean.MAYBE;
        }

        // passed all the guards..
        return FuzzyBoolean.YES;
    }

    /**
     * Determine if any pattern in the parameter type pattern list is attempting to match on parameter annotations.
     * 
     * @return true if a parameter type pattern wants to match on a parameter annotation
     */
    private boolean isMatchingParameterAnnotations() {
        if ((bits & CHECKED_FOR_PARAMETER_ANNOTATION_MATCHING) == 0) {
            bits |= CHECKED_FOR_PARAMETER_ANNOTATION_MATCHING;
            for (int tp = 0, max = parameterTypes.size(); tp < max; tp++) {
                TypePattern typePattern = parameterTypes.get(tp);
                if (isParameterAnnotationMatching(typePattern)) {
                    bits |= PARAMETER_ANNOTATION_MATCHING;
                }
            }
        }
        return (bits & PARAMETER_ANNOTATION_MATCHING) != 0;
    }

    /**
     * Walk the simple structure of a type pattern and determine if any leaf node is involved in parameter annotation matching.
     */
    private boolean isParameterAnnotationMatching(TypePattern tp) {
        if (tp instanceof OrTypePattern) {
            OrTypePattern orAtp = (OrTypePattern) tp;
            return (isParameterAnnotationMatching(orAtp.getLeft())
                    || isParameterAnnotationMatching(orAtp.getRight()));
        } else if (tp instanceof AndTypePattern) {
            AndTypePattern andAtp = (AndTypePattern) tp;
            return (isParameterAnnotationMatching(andAtp.getLeft())
                    || isParameterAnnotationMatching(andAtp.getRight()));
        } else if (tp instanceof NotTypePattern) {
            NotTypePattern notAtp = (NotTypePattern) tp;
            return (isParameterAnnotationMatching(notAtp.getNegatedPattern()));
        } else {
            AnnotationTypePattern atp = tp.getAnnotationPattern();
            return isParameterAnnotationMatching(atp);
        }
    }

    private boolean isParameterAnnotationMatching(AnnotationTypePattern tp) {
        if (tp instanceof OrAnnotationTypePattern) {
            OrAnnotationTypePattern orAtp = (OrAnnotationTypePattern) tp;
            return (isParameterAnnotationMatching(orAtp.getLeft())
                    || isParameterAnnotationMatching(orAtp.getRight()));
        } else if (tp instanceof AndAnnotationTypePattern) {
            AndAnnotationTypePattern andAtp = (AndAnnotationTypePattern) tp;
            return (isParameterAnnotationMatching(andAtp.getLeft())
                    || isParameterAnnotationMatching(andAtp.getRight()));
        } else if (tp instanceof NotAnnotationTypePattern) {
            NotAnnotationTypePattern notAtp = (NotAnnotationTypePattern) tp;
            return (isParameterAnnotationMatching(notAtp.negatedPattern));
        } else {
            return tp.isForParameterAnnotationMatch();
        }
    }

    /**
     * match on declaring type, parameter types, throws types
     */
    private FuzzyBoolean matchesExactlyConstructor(JoinPointSignature aConstructor, World world) {
        if (!declaringType.matchesStatically(aConstructor.getDeclaringType().resolve(world))) {
            return FuzzyBoolean.NO;
        }

        if (!parameterTypes.canMatchSignatureWithNParameters(aConstructor.getParameterTypes().length)) {
            return FuzzyBoolean.NO;
        }
        ResolvedType[] resolvedParameters = world.resolve(aConstructor.getParameterTypes());

        ResolvedType[][] parameterAnnotationTypes = aConstructor.getParameterAnnotationTypes();

        if (parameterAnnotationTypes == null || parameterAnnotationTypes.length == 0) {
            parameterAnnotationTypes = null;
        }

        if (!parameterTypes.matches(resolvedParameters, TypePattern.STATIC, parameterAnnotationTypes)
                .alwaysTrue()) {
            // It could still be a match based on the generic sig parameter types of a parameterized type
            if (!parameterTypes.matches(world.resolve(aConstructor.getGenericParameterTypes()), TypePattern.STATIC,
                    parameterAnnotationTypes).alwaysTrue()) {
                return FuzzyBoolean.MAYBE;
                // It could STILL be a match based on the erasure of the parameter types??
                // to be determined via test cases...
            }
        }

        // check that varargs specifications match
        if (!matchesVarArgs(aConstructor, world)) {
            return FuzzyBoolean.NO;
        }

        // Check the throws pattern
        if (!throwsPattern.matches(aConstructor.getExceptions(), world)) {
            return FuzzyBoolean.NO;
        }

        // passed all the guards..
        return FuzzyBoolean.YES;
    }

    /**
     * We've matched against this method or constructor so far, but without considering varargs (which has been matched as a simple
     * array thus far). Now we do the additional checks to see if the parties agree on whether the last parameter is varargs or a
     * straight array.
     */
    private boolean matchesVarArgs(JoinPointSignature aMethodOrConstructor, World inAWorld) {
        if (parameterTypes.size() == 0) {
            return true;
        }

        TypePattern lastPattern = parameterTypes.get(parameterTypes.size() - 1);
        boolean canMatchVarArgsSignature = lastPattern.isStar() || lastPattern.isVarArgs()
                || (lastPattern == TypePattern.ELLIPSIS);

        if (aMethodOrConstructor.isVarargsMethod()) {
            // we have at least one parameter in the pattern list, and the method has a varargs signature
            if (!canMatchVarArgsSignature) {
                // XXX - Ideally the shadow would be included in the msg but we don't know it...
                inAWorld.getLint().cantMatchArrayTypeOnVarargs.signal(aMethodOrConstructor.toString(),
                        getSourceLocation());
                return false;
            }
        } else {
            // the method ends with an array type, check that we don't *require* a varargs
            if (lastPattern.isVarArgs()) {
                return false;
            }
        }

        return true;
    }

    private FuzzyBoolean matchesAnnotations(ResolvedMember member, World world) {
        if (member == null) {
            // world.getLint().unresolvableMember.signal(member.toString(), getSourceLocation());
            return FuzzyBoolean.NO;
        }
        annotationPattern.resolve(world);

        // optimization before we go digging around for annotations on ITDs
        if (annotationPattern instanceof AnyAnnotationTypePattern) {
            return FuzzyBoolean.YES;
        }

        // fake members represent ITD'd fields - for their annotations we should go and look up the
        // relevant member in the original aspect
        if (member.isAnnotatedElsewhere() && member.getKind() == Member.FIELD) {
            // FIXME asc duplicate of code in AnnotationPointcut.matchInternal()? same fixmes apply here.
            // ResolvedMember [] mems = member.getDeclaringType().resolve(world).getDeclaredFields(); // FIXME asc should include
            // supers with getInterTypeMungersIncludingSupers?
            List<ConcreteTypeMunger> mungers = member.getDeclaringType().resolve(world).getInterTypeMungers();
            for (ConcreteTypeMunger typeMunger : mungers) {
                if (typeMunger.getMunger() instanceof NewFieldTypeMunger) {
                    ResolvedMember fakerm = typeMunger.getSignature();
                    ResolvedMember ajcMethod = AjcMemberMaker.interFieldInitializer(fakerm,
                            typeMunger.getAspectType());
                    ResolvedMember rmm = findMethod(typeMunger.getAspectType(), ajcMethod);
                    if (fakerm.equals(member)) {
                        member = rmm;
                    }
                }
            }
        }

        if (annotationPattern.matches(member).alwaysTrue()) {
            return FuzzyBoolean.YES;
        } else {
            // do NOT look at ancestor members... only the subject can have an annotation match
            // see http://www.eclipse.org/aspectj/doc/next/adk15notebook/join-point-modifiers.html
            return FuzzyBoolean.NO;
        }
    }

    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;
    }

    public boolean declaringTypeMatchAllowingForCovariance(Member member, UnresolvedType shadowDeclaringType,
            World world, TypePattern returnTypePattern, ResolvedType sigReturn) {

        ResolvedType onType = shadowDeclaringType.resolve(world);

        // fastmatch
        if (declaringType.matchesStatically(onType) && returnTypePattern.matchesStatically(sigReturn)) {
            return true;
        }

        Collection<ResolvedType> declaringTypes = member.getDeclaringTypes(world);

        boolean checkReturnType = true;
        // XXX Possible enhancement? Doesn't seem to speed things up
        // if (returnTypePattern.isStar()) {
        // if (returnTypePattern instanceof WildTypePattern) {
        // if (((WildTypePattern)returnTypePattern).getDimensions()==0) checkReturnType = false;
        // }
        // }

        // Sometimes that list includes types that don't explicitly declare the member we are after -
        // they are on the list because their supertype is on the list, that's why we use
        // lookupMethod rather than lookupMemberNoSupers()
        for (ResolvedType type : declaringTypes) {
            if (declaringType.matchesStatically(type)) {
                if (!checkReturnType) {
                    return true;
                }
                ResolvedMember rm = type.lookupMethod(member);
                if (rm == null) {
                    rm = type.lookupMethodInITDs(member); // It must be in here, or we have *real* problems
                }
                if (rm == null) {
                    continue; // might be currently looking at the generic type and we need to continue searching in case we hit a
                }
                // parameterized version of this same type...
                UnresolvedType returnTypeX = rm.getReturnType();
                ResolvedType returnType = returnTypeX.resolve(world);
                if (returnTypePattern.matchesStatically(returnType)) {
                    return true;
                }
            }
        }
        return false;
    }

    // private Collection getDeclaringTypes(Signature sig) {
    // List l = new ArrayList();
    // Class onType = sig.getDeclaringType();
    // String memberName = sig.getName();
    // if (sig instanceof FieldSignature) {
    // Class fieldType = ((FieldSignature)sig).getFieldType();
    // Class superType = onType;
    // while(superType != null) {
    // try {
    // Field f = (superType.getDeclaredField(memberName));
    // if (f.getType() == fieldType) {
    // l.add(superType);
    // }
    // } catch (NoSuchFieldException nsf) {}
    // superType = superType.getSuperclass();
    // }
    // } else if (sig instanceof MethodSignature) {
    // Class[] paramTypes = ((MethodSignature)sig).getParameterTypes();
    // Class superType = onType;
    // while(superType != null) {
    // try {
    // superType.getDeclaredMethod(memberName,paramTypes);
    // l.add(superType);
    // } catch (NoSuchMethodException nsm) {}
    // superType = superType.getSuperclass();
    // }
    // }
    // return l;
    // }

    public NamePattern getName() {
        return name;
    }

    public TypePattern getDeclaringType() {
        return declaringType;
    }

    public MemberKind getKind() {
        return kind;
    }

    @Override
    public String toString() {
        StringBuffer buf = new StringBuffer();

        if (annotationPattern != AnnotationTypePattern.ANY) {
            buf.append(annotationPattern.toString());
            buf.append(' ');
        }

        if (modifiers != ModifiersPattern.ANY) {
            buf.append(modifiers.toString());
            buf.append(' ');
        }

        if (kind == Member.STATIC_INITIALIZATION) {
            buf.append(declaringType.toString());
            buf.append(".<clinit>()");// FIXME AV - bad, cannot be parsed again
        } else if (kind == Member.HANDLER) {
            buf.append("handler(");
            buf.append(parameterTypes.get(0));
            buf.append(")");
        } else {
            if (!(kind == Member.CONSTRUCTOR)) {
                buf.append(returnType.toString());
                buf.append(' ');
            }
            if (declaringType != TypePattern.ANY) {
                buf.append(declaringType.toString());
                buf.append('.');
            }
            if (kind == Member.CONSTRUCTOR) {
                buf.append("new");
            } else {
                buf.append(name.toString());
            }
            if (kind == Member.METHOD || kind == Member.CONSTRUCTOR) {
                buf.append(parameterTypes.toString());
            }
            // FIXME AV - throws is not printed here, weird
        }
        return buf.toString();
    }

    @Override
    public boolean equals(Object other) {
        if (!(other instanceof SignaturePattern)) {
            return false;
        }
        SignaturePattern o = (SignaturePattern) other;
        return o.kind.equals(this.kind) && o.modifiers.equals(this.modifiers)
                && o.returnType.equals(this.returnType) && o.declaringType.equals(this.declaringType)
                && o.name.equals(this.name) && o.parameterTypes.equals(this.parameterTypes)
                && o.throwsPattern.equals(this.throwsPattern) && o.annotationPattern.equals(this.annotationPattern);
    }

    @Override
    public int hashCode() {
        if (hashcode == -1) {
            hashcode = 17;
            hashcode = 37 * hashcode + kind.hashCode();
            hashcode = 37 * hashcode + modifiers.hashCode();
            hashcode = 37 * hashcode + returnType.hashCode();
            hashcode = 37 * hashcode + declaringType.hashCode();
            hashcode = 37 * hashcode + name.hashCode();
            hashcode = 37 * hashcode + parameterTypes.hashCode();
            hashcode = 37 * hashcode + throwsPattern.hashCode();
            hashcode = 37 * hashcode + annotationPattern.hashCode();
        }
        return hashcode;
    }

    @Override
    public void write(CompressingDataOutputStream s) throws IOException {
        kind.write(s);
        modifiers.write(s);
        returnType.write(s);
        declaringType.write(s);
        name.write(s);
        parameterTypes.write(s);
        throwsPattern.write(s);
        annotationPattern.write(s);
        writeLocation(s);
    }

    public static SignaturePattern read(VersionedDataInputStream s, ISourceContext context) throws IOException {
        // ISignaturePattern kind should already have been read by the time this read is entered
        MemberKind kind = MemberKind.read(s);
        ModifiersPattern modifiers = ModifiersPattern.read(s);
        TypePattern returnType = TypePattern.read(s, context);
        TypePattern declaringType = TypePattern.read(s, context);
        NamePattern name = NamePattern.read(s);
        TypePatternList parameterTypes = TypePatternList.read(s, context);
        ThrowsPattern throwsPattern = ThrowsPattern.read(s, context);

        AnnotationTypePattern annotationPattern = AnnotationTypePattern.ANY;

        if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) {
            annotationPattern = AnnotationTypePattern.read(s, context);
        }

        SignaturePattern ret = new SignaturePattern(kind, modifiers, returnType, declaringType, name,
                parameterTypes, throwsPattern, annotationPattern);
        ret.readLocation(context, s);
        return ret;
    }

    /**
     * @return
     */
    public ModifiersPattern getModifiers() {
        return modifiers;
    }

    /**
     * @return
     */
    public TypePatternList getParameterTypes() {
        return parameterTypes;
    }

    /**
     * @return
     */
    public TypePattern getReturnType() {
        return returnType;
    }

    /**
     * @return
     */
    public ThrowsPattern getThrowsPattern() {
        return throwsPattern;
    }

    /**
     * return true if last argument in params is an Object[] but the modifiers say this method was declared with varargs
     * (Object...). We shouldn't be matching if this is the case.
     */
    // private boolean matchedArrayAgainstVarArgs(TypePatternList params,int modifiers) {
    // if (params.size()>0 && (modifiers & Constants.ACC_VARARGS)!=0) {
    // // we have at least one parameter in the pattern list, and the method has a varargs signature
    // TypePattern lastPattern = params.get(params.size()-1);
    // if (lastPattern.isArray() && !lastPattern.isVarArgs) return true;
    // }
    // return false;
    // }
    public AnnotationTypePattern getAnnotationPattern() {
        return annotationPattern;
    }

    @Override
    public boolean isStarAnnotation() {
        return annotationPattern == AnnotationTypePattern.ANY;
    }

    @Override
    public Object accept(PatternNodeVisitor visitor, Object data) {
        return visitor.visit(this, data);
    }

    public boolean isExactDeclaringTypePattern() {
        return isExactDeclaringTypePattern;
    }

    @Override
    public boolean isMatchOnAnyName() {
        return getName().isAny();
    }

    @Override
    public List<ExactTypePattern> getExactDeclaringTypes() {
        if (declaringType instanceof ExactTypePattern) {
            List<ExactTypePattern> l = new ArrayList<ExactTypePattern>();
            l.add((ExactTypePattern) declaringType);
            return l;
        } else {
            return Collections.emptyList();
        }
    }

    @Override
    public boolean couldEverMatch(ResolvedType type) {
        return declaringType.matches(type, TypePattern.STATIC).maybeTrue();
    }

}