/* *******************************************************************
 * Copyright (c) 2005 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://eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *   Adrian Colyer			Initial implementation
 * ******************************************************************/
package org.aspectj.weaver.reflect;

import java.lang.reflect.Member;

import org.aspectj.util.FuzzyBoolean;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.ast.And;
import org.aspectj.weaver.ast.Call;
import org.aspectj.weaver.ast.FieldGetCall;
import org.aspectj.weaver.ast.HasAnnotation;
import org.aspectj.weaver.ast.ITestVisitor;
import org.aspectj.weaver.ast.Instanceof;
import org.aspectj.weaver.ast.Literal;
import org.aspectj.weaver.ast.Not;
import org.aspectj.weaver.ast.Or;
import org.aspectj.weaver.ast.Test;
import org.aspectj.weaver.ast.Var;
import org.aspectj.weaver.internal.tools.MatchingContextBasedTest;
import org.aspectj.weaver.patterns.ExposedState;
import org.aspectj.weaver.tools.DefaultMatchingContext;
import org.aspectj.weaver.tools.JoinPointMatch;
import org.aspectj.weaver.tools.MatchingContext;
import org.aspectj.weaver.tools.PointcutParameter;
import org.aspectj.weaver.tools.ShadowMatch;

Author:colyer Implementation of ShadowMatch for reflection based worlds.
/** * @author colyer Implementation of ShadowMatch for reflection based worlds. */
public class ShadowMatchImpl implements ShadowMatch { private FuzzyBoolean match; private ExposedState state; private Test residualTest; private PointcutParameter[] params; private Member withinCode; private Member subject; private Class<?> withinType; private MatchingContext matchContext = new DefaultMatchingContext(); public ShadowMatchImpl(FuzzyBoolean match, Test test, ExposedState state, PointcutParameter[] params) { this.match = match; this.residualTest = test; this.state = state; this.params = params; } public void setWithinCode(Member aMember) { this.withinCode = aMember; } public void setSubject(Member aMember) { this.subject = aMember; } public void setWithinType(Class<?> aClass) { this.withinType = aClass; } public boolean alwaysMatches() { return match.alwaysTrue(); } public boolean maybeMatches() { return match.maybeTrue(); } public boolean neverMatches() { return match.alwaysFalse(); } public JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args) { if (neverMatches()) { return JoinPointMatchImpl.NO_MATCH; } if (new RuntimeTestEvaluator(residualTest, thisObject, targetObject, args, this.matchContext).matches()) { return new JoinPointMatchImpl(getPointcutParameters(thisObject, targetObject, args)); } else { return JoinPointMatchImpl.NO_MATCH; } } /* * (non-Javadoc) * * @see org.aspectj.weaver.tools.ShadowMatch#setMatchingContext(org.aspectj.weaver.tools.MatchingContext) */ public void setMatchingContext(MatchingContext aMatchContext) { this.matchContext = aMatchContext; } private PointcutParameter[] getPointcutParameters(Object thisObject, Object targetObject, Object[] args) { Var[] vars = state.vars; PointcutParameterImpl[] bindings = new PointcutParameterImpl[params.length]; for (int i = 0; i < bindings.length; i++) { bindings[i] = new PointcutParameterImpl(params[i].getName(), params[i].getType()); bindings[i].setBinding(((ReflectionVar) vars[i]).getBindingAtJoinPoint(thisObject, targetObject, args, subject, withinCode, withinType)); } return bindings; } private static class RuntimeTestEvaluator implements ITestVisitor { private boolean matches = true; private final Test test; private final Object thisObject; private final Object targetObject; private final Object[] args; private final MatchingContext matchContext; public RuntimeTestEvaluator(Test aTest, Object thisObject, Object targetObject, Object[] args, MatchingContext context) { this.test = aTest; this.thisObject = thisObject; this.targetObject = targetObject; this.args = args; this.matchContext = context; } public boolean matches() { test.accept(this); return matches; } public void visit(And e) { boolean leftMatches = new RuntimeTestEvaluator(e.getLeft(), thisObject, targetObject, args, matchContext).matches(); if (!leftMatches) { matches = false; } else { matches = new RuntimeTestEvaluator(e.getRight(), thisObject, targetObject, args, matchContext).matches(); } } public void visit(Instanceof instanceofTest) { ReflectionVar v = (ReflectionVar) instanceofTest.getVar(); Object value = v.getBindingAtJoinPoint(thisObject, targetObject, args); World world = v.getType().getWorld(); ResolvedType desiredType = instanceofTest.getType().resolve(world); if (value == null) { matches = false; } else { ResolvedType actualType = world.resolve(value.getClass().getName()); matches = desiredType.isAssignableFrom(actualType); } } public void visit(MatchingContextBasedTest matchingContextTest) { matches = matchingContextTest.matches(this.matchContext); } public void visit(Not not) { matches = !new RuntimeTestEvaluator(not.getBody(), thisObject, targetObject, args, matchContext).matches(); } public void visit(Or or) { boolean leftMatches = new RuntimeTestEvaluator(or.getLeft(), thisObject, targetObject, args, matchContext).matches(); if (leftMatches) { matches = true; } else { matches = new RuntimeTestEvaluator(or.getRight(), thisObject, targetObject, args, matchContext).matches(); } } public void visit(Literal literal) { if (literal == Literal.FALSE) { matches = false; } else { matches = true; } } public void visit(Call call) { throw new UnsupportedOperationException("Can't evaluate call test at runtime"); } public void visit(FieldGetCall fieldGetCall) { throw new UnsupportedOperationException("Can't evaluate fieldGetCall test at runtime"); } public void visit(HasAnnotation hasAnnotation) { ReflectionVar v = (ReflectionVar) hasAnnotation.getVar(); Object value = v.getBindingAtJoinPoint(thisObject, targetObject, args); World world = v.getType().getWorld(); ResolvedType actualVarType = world.resolve(value.getClass().getName()); ResolvedType requiredAnnotationType = hasAnnotation.getAnnotationType().resolve(world); matches = actualVarType.hasAnnotation(requiredAnnotationType); } } }