Invariant preservation now checked for constructors

/*******************************************************************************
 * Copyright (c) 2012-2013 Tim Molderez.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the 3-Clause BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/BSD-3-Clause
 ******************************************************************************/

package be.ac.ua.ansymo.adbc.aspects;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.EmptyStackException;
import java.util.Vector;

import javax.script.ScriptException;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.ConstructorSignature;
import org.aspectj.lang.reflect.MethodSignature;

import be.ac.ua.ansymo.adbc.AdbcConfig;
import be.ac.ua.ansymo.adbc.annotations.ensures;
import be.ac.ua.ansymo.adbc.annotations.invariant;
import be.ac.ua.ansymo.adbc.annotations.requires;
import be.ac.ua.ansymo.adbc.exceptions.InvariantException;
import be.ac.ua.ansymo.adbc.exceptions.PostConditionException;
import be.ac.ua.ansymo.adbc.exceptions.PreConditionException;
import be.ac.ua.ansymo.adbc.exceptions.SubstitutionException;
import be.ac.ua.ansymo.adbc.utilities.ContractInterpreter;
import be.ac.ua.ansymo.adbc.utilities.ContractStore;

/**
 * This aspect enforces the contracts of all application classes.
 * If a contract is broken, a ContractEnforcementException is thrown.
 * @author Tim Molderez
 */
public aspect ClassContractEnforcer extends AbstractContractEnforcer {

	/**
	 * This advice enforces contracts of regular method calls
	 * If a contract is broken, a ContractEnforcementException is thrown.
	 * @param dyn	the this object, used to determine the dynamic type
	 */
	Object around(Object dyn): execution(* *.*(..)) && this(dyn)
	&& excludeContractEnforcers() {
		/* Very sensitive pointcut!! Only use what's excluded by excludeContractEnforcers()
		 * or you'll trigger an infinite recursion! */

		try {
			PostData pD = preCheck(thisJoinPoint, dyn);
			Object result = proceed(dyn);
			if (AdbcConfig.checkPostconditions) {
				postCheck(pD, thisJoinPoint, dyn, result);
			}
			return result;
		} catch (ScriptException e) {
			throw new RuntimeException("Failed to evaluate contract: " + e.getMessage());
		}
	}

	/**
	 * This advice enforces contracts of constructors
	 * If a contract is broken, a ContractEnforcementException is thrown.
	 * @param dyn	the this object, used to determine the dynamic type
	 */
	Object around(Object dyn): execution(*.new(..)) && excludeContractEnforcers() && this(dyn) {
		// Skip enforcement if this is the internal constructor of an aspect..
		if(aspectConstructorCheck(thisJoinPoint)) {
			return proceed(dyn);
		}

		try {
			PostData pD = preCheck(thisJoinPoint, null);
			Object result = proceed(dyn);
			if (AdbcConfig.checkPostconditions) {
				postCheck(pD, thisJoinPoint, dyn, null);
			}
			return result;
		} catch (ScriptException e) {
			throw new RuntimeException("Failed to evaluate contract (in constructor): " + e.getMessage());
		}
	}

	/*
	 * Check contracts before method execution (preconditions, invariants, substitution principle)
	 * @param jp	thisJoinPoint
	 * @param dyn	the this object
	 * @return data to be passed on to postCheck()
	 */
	private PostData preCheck(JoinPoint jp, Object dyn) throws ScriptException {
		/* ****************************************************************
		 * Fetching the necessary info...
		 **************************************************************** */

		// Retrieve the join point of the method call we're contract-enforcing
		JoinPoint callJp = null;
		try {
			callJp = CallStack.pop();
		} catch (EmptyStackException e) {
			callJp = jp; // You might end up here in case of constructors..
		}

		// Reset bindings
		ContractInterpreter ceval = new ContractInterpreter();

		// Get the contracts of the method call's static type
		CodeSignature sig = (CodeSignature)(callJp.getSignature());
		AccessibleObject body = null;
		if(sig instanceof MethodSignature) {
			body = ((MethodSignature)sig).getMethod();
		} else if (sig instanceof ConstructorSignature) {
			body = ((ConstructorSignature)sig).getConstructor();
		}

		ContractStore store = ContractStore.getInstance();
		String[] pre = store.getPre(body);
		String[] post = store.getPost(body);
		String[] inv = dyn==null?new String[]{"true"}:store.getInvariant(callJp.getSignature().getDeclaringType());
		// Reset postconditions (used in substitution checking)
		Vector<String[]> postContracts = new Vector<String[]>();

		/* ****************************************************************
		 * Binding contract variables
		 **************************************************************** */

		// Bind parameter values
		ceval.setParameterBindings(sig.getParameterNames(), callJp.getArgs());

		// Bind the this object
		ceval.setThisBinding(dyn);

		/* ****************************************************************
		 * Actual contract enforcement
		 **************************************************************** */

		// Test preconditions
		String brokenContract = ceval.evalContract(pre);
		if(brokenContract!=null) {
			throw new PreConditionException(brokenContract, getStaticSignature(callJp), getCallerSignature());
		}

		// Test invariants
		brokenContract = ceval.evalContract(inv);
		if(brokenContract!=null) {
			throw new InvariantException(brokenContract, callJp.getSignature().getDeclaringTypeName(), getCallerSignature(), "precondition");
		}

		// Test precondition substitution rule (does not apply to constructors..)
		if (dyn!=null && AdbcConfig.checkSubstitutionPrinciple) {
			subPreCheck(ceval, dyn.getClass(), null, sig, postContracts);
		}

		// Evaluate calls to the $old() function in the postcondition
		// (This should be done last; it should be safe for the developer to assume that the preconditions passed when using the $old() function.)
		try {
			if (AdbcConfig.checkPostconditions) {
				post = ceval.evalOldFunction(post);
			}
		} catch (ScriptException e) {
			throw new RuntimeException("Failed to evaluate old() call: " + e.getMessage());
		}

		return new PostData(ceval, post, inv, callJp, postContracts);
	}

	/*
	 * Check contracts after method execution (postconditions, invariants, substitution principle)
	 * @param pD		container object with various information produced during the preCheck
	 * @param jp		thisJoinPoint
	 * @param dyn		the this object
	 * @param result	return value of the method call
	 */
	private void postCheck(PostData pD, JoinPoint jp, Object dyn, Object result) throws ScriptException {
		// Get information from the PostData containeer
		ContractInterpreter ceval = pD.ceval;
		JoinPoint callJp = pD.callJp;
		String[] inv = pD.inv;
		String[] post = pD.post;
		Vector<String[]> postContracts = pD.postContracts;

		// Bind the return value
		ceval.setReturnValueBinding(result);

		// Retrieve the method signature of the join point we matched on
		CodeSignature sig = (CodeSignature)(callJp.getSignature());
		boolean isConstructor = sig instanceof ConstructorSignature;

		// In case of constructors, now you can fetch the invariants and bind this..
		if (isConstructor) {
			ContractStore store = ContractStore.getInstance();
			inv = store.getInvariant(dyn.getClass());
			ceval.setThisBinding(dyn);
		}

		// Test postconditions
		String brokenContract = ceval.evalContract(post);
		if(brokenContract!=null) {
			throw new PostConditionException(brokenContract, getStaticSignature(callJp), getDynamicSignature(dyn.getClass(), sig));
		}

		// Test invariants
		brokenContract = ceval.evalContract(inv);
		if(brokenContract!=null) {
			throw new InvariantException(brokenContract, callJp.getSignature().getDeclaringTypeName(), getDynamicSignature(dyn.getClass(), sig), "postcondition");
		}

		// Test postcondition substitution rule
		if (!isConstructor && AdbcConfig.checkSubstitutionPrinciple) {
			subPostCheck(ceval, true, dyn.getClass(), null, sig, postContracts, 0);
		} else if (AdbcConfig.checkSubstitutionPrinciple) {
			// Only test invariants for constructors
			subPostConstructorCheck(ceval, dyn.getClass(), null);
		}
	}

	/*
	 * Checks whether the current join point is the execution of an internal constructor in an aspect
	 * @param jp	thisJoinPoint
	 */
	private boolean aspectConstructorCheck(JoinPoint jp) {
		return jp.getThis().getClass().isAnnotationPresent(org.aspectj.lang.annotation.Aspect.class);
	}

	/*
	 * Check the precondition rule of behavioural subtyping
	 * (Subtype's precondition should be equal to or weaker than the supertype's. Invariants should also be preserved.)
	 * @param ceval			contract interpreter
	 * @param dynType		check whether this type's preconditions respect its parents
	 * @param toBeBlamed	blame this class if a contract is broken
	 * @param sig			signature of the method to be checked
	 * @param postContracts	when the method finishes, this will be filled up with the postconditions in the traversed type hierarchy, with their $old functions evaluated
	 * @return				true if the precondition+invariant rule holds
	 */
	private boolean subPreCheck(ContractInterpreter ceval, Class<?> dynType, Class<?> toBeBlamed, CodeSignature sig, Vector<String[]> postContracts) throws ScriptException {
		try {
			boolean res = true;
			boolean next = true;
			String brokenContract=null;
			ContractStore store = ContractStore.getInstance();

			// Note that getMethod basically does a lookup procedure! (unlike getDeclaredMethod)
			Method mBody = dynType.getMethod(sig.getName(), sig.getParameterTypes());

			if (mBody.isAnnotationPresent(requires.class)) {
				brokenContract = ceval.evalContract(store.getPre(mBody));
				res = brokenContract==null;
			}

			if (mBody.getDeclaringClass().getSuperclass()!=Object.class) {
				next = subPreCheck(ceval, mBody.getDeclaringClass().getSuperclass(), mBody.getDeclaringClass(), sig, postContracts);
			}

			if (dynType.isAnnotationPresent(invariant.class)) {
				String brokenInv = ceval.evalContract(store.getInvariant(dynType));
				if (brokenInv != null) {
					throw new SubstitutionException(brokenInv,dynType.getCanonicalName(), toBeBlamed.getCanonicalName(), "invariant not preserved");
				}
			}

			if (mBody.isAnnotationPresent(ensures.class)) {
				postContracts.add(ceval.evalOldFunction(store.getPost(mBody)));
			}

			if (!next || res) {
				return res;
			} else {
				throw new SubstitutionException(brokenContract,sig.toLongString() , dynType.getCanonicalName() + "." + mBody.toString(), "precondition too strong");
			}
		} catch (SecurityException e) {
			e.printStackTrace();
			return false;
		} catch (NoSuchMethodException e) {
			return true;
		}

	}

	/*
	 * Check the postcondition rule of behavioural subtyping
	 * (If the precondition of the supertype held in the pre-state,
	 * the postcondition of the subtype should be equal to or stronger than the supertype's.
	 * Invariants should also be preserved.)
	 * @param ceval		contract interpreter
	 * @param last		result of the caller
	 * @param dynType	check whether this type's postconditions respect its parents
	 * @param toBeBlamed	blame this class if a contract is broken
	 * @param sig		signature of the method to be checked
	 * @param i			index indicating which entry of postContracts to use
	 * @return			true if the postcondition+invariant rule holds
	 */
	private boolean subPostCheck(ContractInterpreter ceval, boolean last, Class<?> dynType, Class<?> toBeBlamed, CodeSignature sig, Vector<String[]> postContracts, int i) throws ScriptException {
		boolean res = true;
		String brokenContract=null;
		Method mBody=null;
		ContractStore store = ContractStore.getInstance();
		try {
			mBody = dynType.getMethod(sig.getName(), sig.getParameterTypes());

			if (mBody.isAnnotationPresent(ensures.class) && i<postContracts.size()) {
				brokenContract = ceval.evalContract(postContracts.get(i));
				res = brokenContract==null;
			}
		} catch (SecurityException e) {
			e.printStackTrace();
			return false;
		} catch (NoSuchMethodException e) {
			return true;
		}

		if (dynType.isAnnotationPresent(invariant.class)) {
			String brokenInv = ceval.evalContract(store.getInvariant(dynType));
			if (brokenInv != null) {
				throw new SubstitutionException(brokenInv,dynType.getCanonicalName(), toBeBlamed.getCanonicalName(), "invariant not preserved");
			}
		}

		if (!last || res) {
			return subPostCheck(ceval, res, dynType.getSuperclass(), dynType, sig, postContracts, i+1);
		} else {
			throw new SubstitutionException(brokenContract, sig.toLongString() , dynType.getCanonicalName() + "." + mBody.toString(), "postcondition too weak");
		}
	}

	/*
	 * Check that invariants are preserved in the post-state of a constructor
	 * @param ceval			contract interpreter
	 * @param last			result of the caller
	 * @param dynType		check whether this type's postconditions respect its parents
	 * @param toBeBlamed	blame this class if a contract is broken
	 * @return				true if the invariant rule holds
	 */
	private boolean subPostConstructorCheck(ContractInterpreter ceval,Class<?> dynType, Class<?> toBeBlamed) throws ScriptException {
		if(dynType==null) {
			return true;
		}

		ContractStore store = ContractStore.getInstance();
		String brokenInv = ceval.evalContract(store.getInvariant(dynType));
		if (brokenInv != null) {
			throw new SubstitutionException(brokenInv,dynType.getCanonicalName(), toBeBlamed.getCanonicalName(), "invariant not preserved");

		}
		return subPostConstructorCheck(ceval, dynType.getSuperclass(), dynType);
	}

	/*
	 * Retrieve the caller of the method
	 * @return the caller's signature
	 */
	private String getCallerSignature() {
		/* Runtime stack at this point:
		 * 0: getStackTrace()
		 * 1: getCallerSignature_aroundBody()
		 * 2: getCallerSignature()
		 * 3: preCheck_aroundBody()
		 * 4: preCheck()
		 * 5: inlineAccessMethod
		 * 6: contract advice
		 * 7: user advice
		 * 8+ The actual caller should be around here, after skipping all the internal stuff AspectJ creates.. */

		int i=8;
		StackTraceElement[] elems = Thread.currentThread().getStackTrace();
		while (i<elems.length) {
			String m = elems[i].getMethodName();

			// Anything ending in proceed or run or aroundBody should be internal AspectJ stuff..
			if(m.matches(".*proceed\\d*") || m.endsWith("run") || m.matches(".*aroundBody\\d*(\\$advice)?")) {
				i++;
			} else {
				return elems[i].getClassName() + "." + elems[i].getMethodName();
			}
		}
		return "(caller not found)";
	}

	/*
	 * Retrieve the signature of the method body in the method call's dynamic type
	 * @param dynType	dynamic type
	 * @param sig		method signature (its declaring type is ignored..)
	 * @return dynamic signature
	 */
	private String getDynamicSignature(Class<?> dynType, CodeSignature sig) {
		Method mBody;
		try {
			mBody = dynType.getMethod(sig.getName(), sig.getParameterTypes());
			return dynType.getCanonicalName() + "." + mBody.toString();
		} catch (Exception e) {
			return "(method not found)";
		}
	}

	/*
	 * Retrieve the signature of the method body in the method call's static type
	 * @param jp	call join point
	 * @return static signature
	 */
	private String getStaticSignature(JoinPoint jp) {
		return jp.getSignature().toLongString();
	}

	/*
	 * Container for the data to be passed from preCheck() to postCheck()
	 */
	private class PostData {
		public PostData(ContractInterpreter ceval, String[] post, String[] inv, JoinPoint callJp, Vector<String[]> postContracts) {
			this.ceval = ceval;
			this.post = post;
			this.inv = inv;
			this.callJp = callJp;
			this.postContracts = postContracts;
		}

		public ContractInterpreter ceval;		// Contract interpreter
		public String[] post;					// Postconditions of method call's static type
		public String[] inv;					// Invariants of method call's static type

		public JoinPoint callJp;				// The call join point corresponding to the execution join point captured by the contract enforcement advice
		public Vector<String[]> postContracts;	// Postconditions of ancestors, with their $old() calls processed
	}
}

/*******************************************************************************
 * Copyright (c) 2012-2013 Tim Molderez.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the 3-Clause BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/BSD-3-Clause
 ******************************************************************************/

package be.ac.ua.ansymo.adbc.aspects;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.EmptyStackException;
import java.util.Vector;

import javax.script.ScriptException;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.ConstructorSignature;
import org.aspectj.lang.reflect.MethodSignature;

import be.ac.ua.ansymo.adbc.AdbcConfig;
import be.ac.ua.ansymo.adbc.annotations.ensures;
import be.ac.ua.ansymo.adbc.annotations.invariant;
import be.ac.ua.ansymo.adbc.annotations.requires;
import be.ac.ua.ansymo.adbc.exceptions.InvariantException;
import be.ac.ua.ansymo.adbc.exceptions.PostConditionException;
import be.ac.ua.ansymo.adbc.exceptions.PreConditionException;
import be.ac.ua.ansymo.adbc.exceptions.SubstitutionException;
import be.ac.ua.ansymo.adbc.utilities.ContractInterpreter;
import be.ac.ua.ansymo.adbc.utilities.ContractStore;

/**
 * This aspect enforces the contracts of all application classes.
 * If a contract is broken, a ContractEnforcementException is thrown.
 * @author Tim Molderez
 */
public aspect ClassContractEnforcer extends AbstractContractEnforcer {

	/**
	 * This advice enforces contracts of regular method calls
	 * If a contract is broken, a ContractEnforcementException is thrown.
	 * @param dyn	the this object, used to determine the dynamic type
	 */
	Object around(Object dyn): execution(* *.*(..)) && this(dyn)
	&& excludeContractEnforcers() {
		/* Very sensitive pointcut!! Only use what's excluded by excludeContractEnforcers()
		 * or you'll trigger an infinite recursion! */

		try {
			PostData pD = preCheck(thisJoinPoint, dyn);
			Object result = proceed(dyn);
			if (AdbcConfig.checkPostconditions) {
				postCheck(pD, thisJoinPoint, dyn, result);
			}
			return result;
		} catch (ScriptException e) {
			throw new RuntimeException("Failed to evaluate contract: " + e.getMessage());
		}
	}

	/**
	 * This advice enforces contracts of constructors
	 * If a contract is broken, a ContractEnforcementException is thrown.
	 * @param dyn	the this object, used to determine the dynamic type
	 */
	Object around(Object dyn): execution(*.new(..)) && excludeContractEnforcers() && this(dyn) {
		// Skip enforcement if this is the internal constructor of an aspect..
		if(aspectConstructorCheck(thisJoinPoint)) {
			return proceed(dyn);
		}

		// Temporarily disable substitution checking, as it does not apply to constructors..
		boolean subst = AdbcConfig.checkSubstitutionPrinciple;
		AdbcConfig.checkSubstitutionPrinciple = false;
		try {
			PostData pD = preCheck(thisJoinPoint, null);
			Object result = proceed(dyn);
			if (AdbcConfig.checkPostconditions) {
				postCheck(pD, thisJoinPoint, dyn, null);
			}
			AdbcConfig.checkSubstitutionPrinciple=subst;
			return result;
		} catch (ScriptException e) {
			AdbcConfig.checkSubstitutionPrinciple=subst;
			throw new RuntimeException("Failed to evaluate contract (in constructor): " + e.getMessage());
		}
	}

	/*
	 * Check contracts before method execution (preconditions, invariants, substitution principle)
	 * @param jp	thisJoinPoint
	 * @param dyn	the this object
	 * @return data to be passed on to postCheck()
	 */
	private PostData preCheck(JoinPoint jp, Object dyn) throws ScriptException {
		/* ****************************************************************
		 * Fetching the necessary info...
		 **************************************************************** */

		// Retrieve the join point of the method call we're contract-enforcing
		JoinPoint callJp = null;
		try {
			callJp = CallStack.pop();
		} catch (EmptyStackException e) {
			callJp = jp; // You might end up here in case of constructors..
		}

		// Reset bindings
		ContractInterpreter ceval = new ContractInterpreter();

		// Get the contracts of the method call's static type
		CodeSignature sig = (CodeSignature)(callJp.getSignature());
		AccessibleObject body = null;
		if(sig instanceof MethodSignature) {
			body = ((MethodSignature)sig).getMethod();
		} else if (sig instanceof ConstructorSignature) {
			body = ((ConstructorSignature)sig).getConstructor();
		}

		ContractStore store = ContractStore.getInstance();
		String[] pre = store.getPre(body);
		String[] post = store.getPost(body);
		String[] inv = dyn==null?new String[]{"true"}:store.getInvariant(callJp.getSignature().getDeclaringType());
		// Reset postconditions (used in substitution checking)
		Vector<String[]> postContracts = new Vector<String[]>();

		/* ****************************************************************
		 * Binding contract variables
		 **************************************************************** */

		// Bind parameter values
		ceval.setParameterBindings(sig.getParameterNames(), callJp.getArgs());

		// Bind the this object
		ceval.setThisBinding(dyn);

		/* ****************************************************************
		 * Actual contract enforcement
		 **************************************************************** */

		// Test preconditions
		String brokenContract = ceval.evalContract(pre);
		if(brokenContract!=null) {
			throw new PreConditionException(brokenContract, getStaticSignature(callJp), getCallerSignature());
		}

		// Test invariants
		brokenContract = ceval.evalContract(inv);
		if(brokenContract!=null) {
			throw new InvariantException(brokenContract, callJp.getSignature().getDeclaringTypeName(), getCallerSignature(), "precondition");
		}

		// Test precondition substitution rule
		if (AdbcConfig.checkSubstitutionPrinciple) {
			subPreCheck(ceval, dyn.getClass(), null, sig, postContracts);
		}

		// Evaluate calls to the $old() function in the postcondition
		// (This should be done last; it should be safe for the developer to assume that the preconditions passed when using the $old() function.)
		try {
			if (AdbcConfig.checkPostconditions) {
				post = ceval.evalOldFunction(post);
			}
		} catch (ScriptException e) {
			throw new RuntimeException("Failed to evaluate old() call: " + e.getMessage());
		}

		return new PostData(ceval, post, inv, callJp, postContracts);
	}

	/*
	 * Check contracts after method execution (postconditions, invariants, substitution principle)
	 * @param pD		container object with various information produced during the preCheck
	 * @param jp		thisJoinPoint
	 * @param dyn		the this object
	 * @param result	return value of the method call
	 */
	private void postCheck(PostData pD, JoinPoint jp, Object dyn, Object result) throws ScriptException {
		// Get information from the PostData containeer
		ContractInterpreter ceval = pD.ceval;
		JoinPoint callJp = pD.callJp;
		String[] inv = pD.inv;
		String[] post = pD.post;
		Vector<String[]> postContracts = pD.postContracts;

		// Bind the return value
		ceval.setReturnValueBinding(result);

		// Retrieve the method signature of the join point we matched on
		CodeSignature sig = (CodeSignature)(callJp.getSignature());

		// In case of constructors, now you can fetch the invariants and bind this..
		if (sig instanceof ConstructorSignature) {
			ContractStore store = ContractStore.getInstance();
			inv = store.getInvariant(dyn.getClass());
			ceval.setThisBinding(dyn);
		}

		// Test postconditions
		String brokenContract = ceval.evalContract(post);
		if(brokenContract!=null) {
			throw new PostConditionException(brokenContract, getStaticSignature(callJp), getDynamicSignature(dyn.getClass(), sig));
		}

		// Test invariants
		brokenContract = ceval.evalContract(inv);
		if(brokenContract!=null) {
			throw new InvariantException(brokenContract, callJp.getSignature().getDeclaringTypeName(), getDynamicSignature(dyn.getClass(), sig), "postcondition");
		}

		// Test postcondition substitution rule
		// (skip if the dynamic type == the static type; there are no constraints on the postcondition in this case, since there's no substituting going on...)
		if (AdbcConfig.checkSubstitutionPrinciple
				&& !dyn.getClass().getCanonicalName().equals(callJp.getSignature().getDeclaringTypeName())) {
			subPostCheck(ceval, true, dyn.getClass(), null, sig, postContracts, 0);
		}
	}

	/*
	 * Checks whether the current join point is the execution of an internal constructor in an aspect
	 * @param jp	thisJoinPoint
	 */
	private boolean aspectConstructorCheck(JoinPoint jp) {
		return jp.getThis().getClass().isAnnotationPresent(org.aspectj.lang.annotation.Aspect.class);
	}

	/*
	 * Check the precondition rule of behavioural subtyping
	 * (Subtype's precondition should be equal to or weaker than the supertype's.)
	 * @param ceval			contract interpreter
	 * @param dynType		check whether this type's preconditions respect its parents
	 * @param toBeBlamed	if the check fails, this class is to be blamed
	 * @param sig			signature of the method to be checked
	 * @param postContracts	when the method finishes, this will be filled up with the postconditions in the traversed type hierarchy, with their $old functions evaluated
	 * @return				true if the precondition rule holds
	 */
	private boolean subPreCheck(ContractInterpreter ceval, Class<?> dynType, Class<?> toBeBlamed, CodeSignature sig, Vector<String[]> postContracts) throws ScriptException {
		try {
			boolean res = true;
			boolean next = true;
			String brokenContract=null;
			ContractStore store = ContractStore.getInstance();

			// Note that getMethod basically does a lookup procedure! (unlike getDeclaredMethod)
			Method mBody = dynType.getMethod(sig.getName(), sig.getParameterTypes());

			if (mBody.isAnnotationPresent(requires.class)) {
				brokenContract = ceval.evalContract(store.getPre(mBody));
				res = brokenContract==null;
			}

			if (mBody.getDeclaringClass().getSuperclass()!=Object.class) {
				next = subPreCheck(ceval, mBody.getDeclaringClass().getSuperclass(), mBody.getDeclaringClass(), sig, postContracts);
			}

			if (dynType.isAnnotationPresent(invariant.class)) {
				String brokenInv = ceval.evalContract(store.getInvariant(dynType));
				if (brokenInv != null) {
					throw new SubstitutionException(brokenInv,dynType.getCanonicalName(), toBeBlamed.getCanonicalName(), "invariant not preserved");
				}
			}

			if (mBody.isAnnotationPresent(ensures.class)) {
				postContracts.add(ceval.evalOldFunction(store.getPost(mBody)));
			}

			if (!next || res) {
				return res;
			} else {
				throw new SubstitutionException(brokenContract,sig.toLongString() , dynType.getCanonicalName() + "." + mBody.toString(), "precondition too strong");
			}
		} catch (SecurityException e) {
			e.printStackTrace();
			return false;
		} catch (NoSuchMethodException e) {
			return true;
		}

	}

	/*
	 * Check the postcondition rule of behavioural subtyping
	 * (If the precondition of the supertype held in the pre-state,
	 * the postcondition of the subtype should be equal to or stronger than the supertype's.)
	 * @param ceval		contract interpreter
	 * @param last		result of the caller
	 * @param dynType	check whether this type's postconditions respect its parents
	 * @param sig		signature of the method to be checked
	 * @param i			index indicating which entry of postContracts to use
	 * @return			true if the postcondition rule holds
	 */
	private boolean subPostCheck(ContractInterpreter ceval, boolean last, Class<?> dynType, Class<?> toBeBlamed, CodeSignature sig, Vector<String[]> postContracts, int i) throws ScriptException {
		boolean res = true;
		String brokenContract=null;
		ContractStore store = ContractStore.getInstance();

		try {
			Method mBody = dynType.getMethod(sig.getName(), sig.getParameterTypes());

			if (mBody.isAnnotationPresent(ensures.class) && i<postContracts.size()) {
				brokenContract = ceval.evalContract(postContracts.get(i));
				res = brokenContract==null;
			}

			if (dynType.isAnnotationPresent(invariant.class)) {

				String brokenInv = ceval.evalContract(store.getInvariant(dynType));
				if (brokenInv != null) {
					throw new SubstitutionException(brokenInv,dynType.getCanonicalName(), toBeBlamed.getCanonicalName(), "invariant not preserved");
				}
			}

			if (!last || res) {
				return subPostCheck(ceval, res, dynType.getSuperclass(), dynType, sig, postContracts, i+1);
			} else {
				throw new SubstitutionException(brokenContract, sig.toLongString() , dynType.getCanonicalName() + "." + mBody.toString(), "postcondition too weak");
			}
		} catch (SecurityException e) {
			e.printStackTrace();
			return false;
		} catch (NoSuchMethodException e) {
			return true;
		}
	}

	/*
	 * Retrieve the caller of the method
	 * @return the caller's signature
	 */
	private String getCallerSignature() {
		/* Runtime stack at this point:
		 * 0: getStackTrace()
		 * 1: getCallerSignature_aroundBody()
		 * 2: getCallerSignature()
		 * 3: preCheck_aroundBody()
		 * 4: preCheck()
		 * 5: inlineAccessMethod
		 * 6: contract advice
		 * 7: user advice
		 * 8+ The actual caller should be around here, after skipping all the internal stuff AspectJ creates.. */

		int i=8;
		StackTraceElement[] elems = Thread.currentThread().getStackTrace();
		while (i<elems.length) {
			String m = elems[i].getMethodName();

			// Anything ending in proceed or run or aroundBody should be internal AspectJ stuff..
			if(m.matches(".*proceed\\d*") || m.endsWith("run") || m.matches(".*aroundBody\\d*(\\$advice)?")) {
				i++;
			} else {
				return elems[i].getClassName() + "." + elems[i].getMethodName();
			}
		}
		return "(caller not found)";
	}

	/*
	 * Retrieve the signature of the method body in the method call's dynamic type
	 * @param dynType	dynamic type
	 * @param sig		method signature (its declaring type is ignored..)
	 * @return dynamic signature
	 */
	private String getDynamicSignature(Class<?> dynType, CodeSignature sig) {
		Method mBody;
		try {
			mBody = dynType.getMethod(sig.getName(), sig.getParameterTypes());
			return dynType.getCanonicalName() + "." + mBody.toString();
		} catch (Exception e) {
			return "(method not found)";
		}
	}

	/*
	 * Retrieve the signature of the method body in the method call's static type
	 * @param jp	call join point
	 * @return static signature
	 */
	private String getStaticSignature(JoinPoint jp) {
		return jp.getSignature().toLongString();
	}

	/*
	 * Container for the data to be passed from preCheck() to postCheck()
	 */
	private class PostData {
		public PostData(ContractInterpreter ceval, String[] post, String[] inv, JoinPoint callJp, Vector<String[]> postContracts) {
			this.ceval = ceval;
			this.post = post;
			this.inv = inv;
			this.callJp = callJp;
			this.postContracts = postContracts;
		}

		public ContractInterpreter ceval;		// Contract interpreter
		public String[] post;					// Postconditions of method call's static type
		public String[] inv;					// Invariants of method call's static type

		public JoinPoint callJp;				// The call join point corresponding to the execution join point captured by the contract enforcement advice
		public Vector<String[]> postContracts;	// Postconditions of ancestors, with their $old() calls processed
	}
}


Interpose on timers: scheduleOnce and schedule

package sample;

import static java.lang.Thread.sleep;

import akka.dispatch.verification.Instrumenter;

import akka.actor.ActorRef;
import akka.actor.ScalaActorRef;
import akka.actor.Actor;
import akka.actor.Props;
import akka.actor.ActorCell;
import akka.actor.ActorSystem;
import akka.actor.ActorContext;
import akka.actor.Scheduler;
import akka.actor.Cancellable;
import akka.actor.LightArrayRevolverScheduler;

import akka.pattern.AskSupport;

import akka.dispatch.Envelope;
import akka.dispatch.MessageQueue;
import akka.dispatch.MessageDispatcher;

import scala.concurrent.impl.CallbackRunnable;
import scala.concurrent.duration.FiniteDuration;
import scala.concurrent.ExecutionContext;
import java.lang.Runnable;

privileged public aspect WeaveActor {

  Instrumenter inst = Instrumenter.apply();

  pointcut enqueueOperation(MessageQueue me, ActorRef receiver, Envelope handle):
  execution(public * akka.dispatch.MessageQueue.enqueue(ActorRef, Envelope)) &&
  args(receiver, handle) && this(me);

  Object around(MessageQueue me, ActorRef receiver, Envelope handle):
  enqueueOperation(me, receiver, handle) {
	return proceed(me, receiver, handle);
  }

  before(ActorCell me, Object msg):
  execution(* akka.actor.ActorCell.receiveMessage(Object)) &&
  args(msg, ..) && this(me) {
	inst.beforeMessageReceive(me, msg);
  }

  after(ActorCell me, Object msg):
  execution(* akka.actor.ActorCell.receiveMessage(Object)) &&
  args(msg, ..) && this(me) {
	inst.afterMessageReceive(me, msg);
  }

  pointcut dispatchOperation(MessageDispatcher me, ActorCell receiver, Envelope handle):
  execution(* akka.dispatch.MessageDispatcher.dispatch(..)) &&
  args(receiver, handle, ..) && this(me);

  Object around(MessageDispatcher me, ActorCell receiver, Envelope handle):
  dispatchOperation(me, receiver, handle) {
  	if (inst.aroundDispatch(me, receiver, handle))
   		return proceed(me, receiver, handle);
   	else
   		return null;
  }

  after(ActorSystem me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props)) &&
  args(props) && this(me) {
  	inst.new_actor(me, props, actor);
  }

  after(ActorSystem me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props, String)) &&
  args(props, name) && this(me) {
  	inst.new_actor(me, props, name, actor);
  }

  after(ActorContext me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props)) &&
  args(props) && this(me) {
  	inst.new_actor(me.system(), props, actor);
  }

  after(ActorContext me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props, String)) &&
  args(props, name) && this(me) {
  	inst.new_actor(me.system(), props, name, actor);
  }

  before(ActorRef me, Object msg, ActorRef sender):
  execution(* akka.actor.ScalaActorRef.$bang(Object, ActorRef)) &&
  args(msg, sender) && this(me) {
  	inst.tell(me, msg, sender);
  }

  before(ActorRef me, Object msg, ActorRef sender):
  execution(* akka.actor.ActorRef.tell(Object, ActorRef)) &&
  args(msg, sender, ..) && this(me) {
  }

  // Override akka.actor.Scheduler.schedulerOnce
  pointcut scheduleOnce(LightArrayRevolverScheduler me, FiniteDuration delay, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  execution(public * akka.actor.LightArrayRevolverScheduler.scheduleOnce(FiniteDuration,ActorRef,Object,ExecutionContext,ActorRef)) &&
  args(delay, receiver, msg, exc, sender) && this(me);

  // Never actually proceed(), just schedule our own timer, which does not use
  // ! directly, but instead calls enqueue_message..
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  scheduleOnce(me, delay, receiver, msg, exc, sender) {
    class MyRunnable implements java.lang.Runnable {
      ActorRef rcv;
      Object m;
      Instrumenter i;

      public MyRunnable(ActorRef receiver, Object msg, Instrumenter inst) {
        rcv = receiver;
        m = msg;
        i = inst;
      }

      public void run() {
        // Use essentially the same scheduleOnce implementation, but don't use !
        // See:
        //   https://github.com/akka/akka/blob/cb05725c1ec8a09e9bfd57dd093911dd41c7b288/akka-actor/src/main/scala/akka/actor/Scheduler.scala#L105
        i.handleTick(rcv, m);
      }
    }
    inst.registerCancellable(me.scheduleOnce(delay, new MyRunnable(receiver, msg, inst), exc));
	  return null;
  }

  // Override akka.actor.Scheduler.scheduler
  pointcut schedule(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  execution(public * akka.actor.LightArrayRevolverScheduler.schedule(FiniteDuration,FiniteDuration,ActorRef,Object,ExecutionContext,ActorRef)) &&
  args(delay, interval, receiver, msg, exc, sender) && this(me);

  // Never actually proceed(), just schedule our own timer, which does not use
  // ! directly, but instead calls enqueue_message..
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  schedule(me, delay, interval, receiver, msg, exc, sender) {
    class MyRunnable implements java.lang.Runnable {
      ActorRef rcv;
      Object m;
      Instrumenter i;

      public MyRunnable(ActorRef receiver, Object msg, Instrumenter inst) {
        rcv = receiver;
        m = msg;
        i = inst;
      }

      public void run() {
        // Use essentially the same scheduleOnce implementation, but don't use !
        // See:
        //   https://github.com/akka/akka/blob/cb05725c1ec8a09e9bfd57dd093911dd41c7b288/akka-actor/src/main/scala/akka/actor/Scheduler.scala#L105
        i.handleTick(rcv, m);
      }
    }
    inst.registerCancellable(me.schedule(delay, interval, new MyRunnable(receiver, msg, inst), exc));
	  return null;
  }
}

package sample;

import static java.lang.Thread.sleep;

import akka.dispatch.verification.Instrumenter;

import akka.actor.ActorRef;
import akka.actor.ScalaActorRef;
import akka.actor.Actor;
import akka.actor.Props;
import akka.actor.ActorCell;
import akka.actor.ActorSystem;
import akka.actor.ActorContext;
import akka.actor.Scheduler;

import akka.pattern.AskSupport;

import akka.dispatch.Envelope;
import akka.dispatch.MessageQueue;
import akka.dispatch.MessageDispatcher;

import scala.concurrent.impl.CallbackRunnable;
import scala.concurrent.duration.FiniteDuration;
import scala.concurrent.ExecutionContext;
import java.lang.Runnable;

privileged public aspect WeaveActor {

  Instrumenter inst = Instrumenter.apply();

  pointcut enqueueOperation(MessageQueue me, ActorRef receiver, Envelope handle):
  execution(public * akka.dispatch.MessageQueue.enqueue(ActorRef, Envelope)) &&
  args(receiver, handle) && this(me);

  Object around(MessageQueue me, ActorRef receiver, Envelope handle):
  enqueueOperation(me, receiver, handle) {
	return proceed(me, receiver, handle);
  }

  before(ActorCell me, Object msg):
  execution(* akka.actor.ActorCell.receiveMessage(Object)) &&
  args(msg, ..) && this(me) {
	inst.beforeMessageReceive(me, msg);
  }

  after(ActorCell me, Object msg):
  execution(* akka.actor.ActorCell.receiveMessage(Object)) &&
  args(msg, ..) && this(me) {
	inst.afterMessageReceive(me, msg);
  }

  pointcut dispatchOperation(MessageDispatcher me, ActorCell receiver, Envelope handle):
  execution(* akka.dispatch.MessageDispatcher.dispatch(..)) &&
  args(receiver, handle, ..) && this(me);

  Object around(MessageDispatcher me, ActorCell receiver, Envelope handle):
  dispatchOperation(me, receiver, handle) {
  	if (inst.aroundDispatch(me, receiver, handle))
   		return proceed(me, receiver, handle);
   	else
   		return null;
  }

  after(ActorSystem me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props)) &&
  args(props) && this(me) {
  	inst.new_actor(me, props, actor);
  }

  after(ActorSystem me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props, String)) &&
  args(props, name) && this(me) {
  	inst.new_actor(me, props, name, actor);
  }

  after(ActorContext me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props)) &&
  args(props) && this(me) {
  	inst.new_actor(me.system(), props, actor);
  }

  after(ActorContext me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props, String)) &&
  args(props, name) && this(me) {
  	inst.new_actor(me.system(), props, name, actor);
  }

  before(ActorRef me, Object msg, ActorRef sender):
  execution(* akka.actor.ScalaActorRef.$bang(Object, ActorRef)) &&
  args(msg, sender) && this(me) {
  	inst.tell(me, msg, sender);
  }

  before(ActorRef me, Object msg, ActorRef sender):
  execution(* akka.actor.ActorRef.tell(Object, ActorRef)) &&
  args(msg, sender, ..) && this(me) {
  }

  /*
  // TODO(cs): the following pointcut is not applied, for a reason I do not
  // understand. Debug this later, since it will probably come in handy.
  // For what it's worth, here is the decompiled byte code from
  // akka.actor.Scheduler.scala:

  $ javap Scheduler.class
  Compiled from "Scheduler.scala"
  public abstract class akka.actor.Scheduler$class extends java.lang.Object{
      public static final akka.actor.Cancellable schedule(akka.actor.Scheduler, scala.concurrent.duration.FiniteDuration, scala.concurrent.duration.FiniteDuration, akka.actor.ActorRef, java.lang.Object, scala.concurrent.ExecutionContext, akka.actor.ActorRef);
      public static final akka.actor.Cancellable schedule(akka.actor.Scheduler, scala.concurrent.duration.FiniteDuration, scala.concurrent.duration.FiniteDuration, scala.Function0, scala.concurrent.ExecutionContext);
      public static final akka.actor.ActorRef schedule$default$6(akka.actor.Scheduler, scala.concurrent.duration.FiniteDuration, scala.concurrent.duration.FiniteDuration, akka.actor.ActorRef, java.lang.Object);
      public static final akka.actor.Cancellable scheduleOnce(akka.actor.Scheduler, scala.concurrent.duration.FiniteDuration, akka.actor.ActorRef, java.lang.Object, scala.concurrent.ExecutionContext, akka.actor.ActorRef);
      public static final akka.actor.Cancellable scheduleOnce(akka.actor.Scheduler, scala.concurrent.duration.FiniteDuration, scala.Function0, scala.concurrent.ExecutionContext);
      public static final akka.actor.ActorRef scheduleOnce$default$5(akka.actor.Scheduler, scala.concurrent.duration.FiniteDuration, akka.actor.ActorRef, java.lang.Object);
      public static void $init$(akka.actor.Scheduler);
  }

  // Override akka.actor.Scheduler.schedulerOnce
  // TODO(cs): also interpose on akka.actor.Scheduler.schedule()
  pointcut Timer(Scheduler me, FiniteDuration delay, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  execution(* akka.actor.Scheduler.schedulerOnce(..)) &&
  args(delay, receiver, msg, exc, sender) && this(me);

  // Never actually proceed(), just schedule our own timer, which does not use
  // ! directly, but instead calls enqueue_message.
  Object around(Scheduler me, FiniteDuration delay, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  Timer(me, delay, receiver, msg, exc, sender) {
    class MyRunnable implements java.lang.Runnable {
      ActorRef rcv;
      Object m;
      Instrumenter i;

      public MyRunnable(ActorRef receiver, Object msg, Instrumenter inst) {
        rcv = receiver;
        m = msg;
        i = inst;
      }

      public void run() {
        // Use essentially the same scheduleOnce implementation, but don't use !
        // See:
        //   https://github.com/akka/akka/blob/cb05725c1ec8a09e9bfd57dd093911dd41c7b288/akka-actor/src/main/scala/akka/actor/Scheduler.scala#L105
        i.handleTick(rcv, m);
      }
    }
    me.scheduleOnce(delay, new MyRunnable(receiver, msg, inst), exc);
	  return null;
  }
  */
}


do not log INFO rollback statements

do not log INFO rollback statements

/**
 * Copyright 2010 CosmoCode GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.cosmocode.palava.jpa.aspectj;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceException;

import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.gag.annotation.remark.Hack;
import com.google.inject.Inject;
import com.google.inject.Provider;

import de.cosmocode.palava.core.aop.AbstractPalavaAspect;
import de.cosmocode.palava.jpa.Transactional;

public final aspect EntityTransactionAspect extends AbstractPalavaAspect issingleton() {

    private static final Logger LOG = LoggerFactory.getLogger(EntityTransactionAspect.class);

    private Provider<EntityManager> currentManager;

    @Inject
    public void setProvider(Provider<EntityManager> manager) {
        this.currentManager = Preconditions.checkNotNull(manager, "Manager");
    }

    pointcut transactional(): execution(@Transactional * *.*(..));

    @SuppressAjWarnings("adviceDidNotMatch")
    Object around(): transactional() {
        final EntityManager manager = currentManager.get();
        LOG.trace("Retrieved entitymanager {}", manager);
        final EntityTransaction tx = manager.getTransaction();
        LOG.trace("Using transaction {}", tx);

        final boolean local = !tx.isActive();

        if (local) {
            LOG.debug("Beginning automatic transaction {}", tx);
            tx.begin();
        } else {
            LOG.trace("Transaction {} already active", tx);
        }

        final Object returnValue;

        try {
            returnValue = proceed();
        } catch (Exception e) {
            try {
                if (local && (tx.isActive() || tx.getRollbackOnly())) {
                    LOG.debug("Rolling back local/active transaction {}", tx);
                    tx.rollback();
                } else if (!tx.getRollbackOnly()) {
                    LOG.debug("Setting transaction {} as rollback only", tx);
                    tx.setRollbackOnly();
                }
            } catch (PersistenceException inner) {
                LOG.error("Rollback failed", inner);
            }

            // hack to support throwing checked exceptions
            return sneakyThrow(e);
        }

        if (local && tx.isActive()) {
            if (tx.getRollbackOnly()) {
                LOG.debug("Rolling back marked transaction {}", tx);
                tx.rollback();
            } else {
                try {
                    tx.commit();
                    LOG.trace("Committed automatic transaction {}", tx);
                } catch (PersistenceException e) {
                    try {
                        LOG.debug("Rolling back transaction {}", tx);
                        tx.rollback();
                    } catch (PersistenceException inner) {
                        LOG.error("Rollback failed", inner);
                    }
                    throw e;
                }
            }
        } else {
            LOG.trace("Not committing non-local transaction {}", tx);
        }

        return returnValue;
    }

    @Hack("Why is this possible?")
    private Object sneakyThrow(Throwable t) {
        this.<RuntimeException>doSneakyThrow(t);
        return null;
    }

    @SuppressWarnings("unchecked")
    private <T extends Throwable> T doSneakyThrow(Throwable t) throws T {
        throw (T) t;
    }

}

/**
 * Copyright 2010 CosmoCode GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.cosmocode.palava.jpa.aspectj;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceException;

import org.aspectj.lang.annotation.SuppressAjWarnings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.gag.annotation.remark.Hack;
import com.google.inject.Inject;
import com.google.inject.Provider;

import de.cosmocode.palava.core.aop.AbstractPalavaAspect;
import de.cosmocode.palava.jpa.Transactional;

public final aspect EntityTransactionAspect extends AbstractPalavaAspect issingleton() {

    private static final Logger LOG = LoggerFactory.getLogger(EntityTransactionAspect.class);

    private Provider<EntityManager> currentManager;

    @Inject
    public void setProvider(Provider<EntityManager> manager) {
        this.currentManager = Preconditions.checkNotNull(manager, "Manager");
    }

    pointcut transactional(): execution(@Transactional * *.*(..));

    @SuppressAjWarnings("adviceDidNotMatch")
    Object around(): transactional() {
        final EntityManager manager = currentManager.get();
        LOG.trace("Retrieved entitymanager {}", manager);
        final EntityTransaction tx = manager.getTransaction();
        LOG.trace("Using transaction {}", tx);

        final boolean local = !tx.isActive();

        if (local) {
            LOG.debug("Beginning automatic transaction {}", tx);
            tx.begin();
        } else {
            LOG.trace("Transaction {} already active", tx);
        }

        final Object returnValue;

        try {
            returnValue = proceed();
        } catch (Exception e) {
            try {
                if (local && (tx.isActive() || tx.getRollbackOnly())) {
                    LOG.info("Rolling back local/active transaction {}", tx);
                    tx.rollback();
                } else if (!tx.getRollbackOnly()) {
                    LOG.debug("Setting transaction {} as rollback only", tx);
                    tx.setRollbackOnly();
                }
            } catch (PersistenceException inner) {
                LOG.error("Rollback failed", inner);
            }

            // hack to support throwing checked exceptions
            return sneakyThrow(e);
        }

        if (local && tx.isActive()) {
            if (tx.getRollbackOnly()) {
                LOG.debug("Rolling back marked transaction {}", tx);
                tx.rollback();
            } else {
                try {
                    tx.commit();
                    LOG.trace("Committed automatic transaction {}", tx);
                } catch (PersistenceException e) {
                    try {
                        LOG.info("Rolling back transaction {}", tx);
                        tx.rollback();
                    } catch (PersistenceException inner) {
                        LOG.error("Rollback failed", inner);
                    }
                    throw e;
                }
            }
        } else {
            LOG.trace("Not committing non-local transaction {}", tx);
        }

        return returnValue;
    }

    @Hack("Why is this possible?")
    private Object sneakyThrow(Throwable t) {
        this.<RuntimeException>doSneakyThrow(t);
        return null;
    }

    @SuppressWarnings("unchecked")
    private <T extends Throwable> T doSneakyThrow(Throwable t) throws T {
        throw (T) t;
    }

}


added null/state check

added null/state check

package de.cosmocode.palava.security.aspectj;

import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Guice;
import com.google.inject.Provider;

abstract aspect AbstractSecurityAspect {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractSecurityAspect.class);

    private Provider<Subject> provider;

    @Inject
    void setProvider(Provider<Subject> provider) {
        this.provider = Preconditions.checkNotNull(provider, "Provider");
    }

    protected Subject getCurrentUser() {
        return provider.get();
    }

    pointcut createInjector(): call(Injector Guice.createInjector(..));

    after() returning (Injector injector): createInjector() {
        LOG.trace("Injecting members on {}", this);
        if (provider == null) {
            injector.injectMembers(this);
        } else {
            throw new IllegalStateException("An injector has already been created");
        }
    }

}

package de.cosmocode.palava.security.aspectj;

import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Guice;
import com.google.inject.Provider;

abstract aspect AbstractSecurityAspect {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractSecurityAspect.class);

    private Provider<Subject> provider;

    @Inject
    void setProvider(Provider<Subject> provider) {
        this.provider = Preconditions.checkNotNull(provider, "Provider");
    }

    protected Subject getCurrentUser() {
        return provider.get();
    }

    pointcut createInjector(): call(Injector Guice.createInjector(..));

    after() returning (Injector injector): createInjector() {
        LOG.trace("Injecting members on {}", this);
        injector.injectMembers(this);
    }

}


commit 7a2264c68e31bd32443d9ed791cd9a1d16d00305
Author: Konstantin Boudnik <cos@yahoo-inc.com>
Date:   Mon May 17 11:16:52 2010 -0700

    HDFS:1157 from https://issues.apache.org/jira/secure/attachment/12444716/hdfs-1157.patch

    +++ b/YAHOO-CHANGES.txt
    +    HDFS-1157. Modifications introduced by HDFS-1150 are breaking aspect's
    +    bindings (cos)

git-svn-id: 7e7a2d564dde2945c3a26e08d1235fcc0cb7aba1@1077468 13f79535-47bb-0310-9956-ffa450edef68

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.hadoop.hdfs.server.datanode;

import java.io.File;
import java.io.IOException;
import java.util.AbstractList;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.test.system.DNProtocol;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.test.system.DaemonProtocol;
import org.apache.hadoop.hdfs.server.datanode.SecureDataNodeStarter.SecureResources;

public privileged aspect DataNodeAspect {
  declare parents : DataNode implements DNProtocol;

  public Configuration DataNode.getDaemonConf() {
    return super.getConf();
  }

  pointcut dnConstructorPointcut(Configuration conf, AbstractList<File> dirs,
      SecureResources resources) :
    call(DataNode.new(Configuration, AbstractList<File>, SecureResources))
    && args(conf, dirs, resources);

  after(Configuration conf, AbstractList<File> dirs, SecureResources resources)
    returning (DataNode datanode):
    dnConstructorPointcut(conf, dirs, resources) {
    try {
      UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
      datanode.setUser(ugi.getShortUserName());
    } catch (IOException e) {
      datanode.LOG.warn("Unable to get the user information for the " +
          "DataNode");
    }
    datanode.setReady(true);
  }

  pointcut getVersionAspect(String protocol, long clientVersion) :
    execution(public long DataNode.getProtocolVersion(String ,
      long) throws IOException) && args(protocol, clientVersion);

  long around(String protocol, long clientVersion) :
    getVersionAspect(protocol, clientVersion) {
    if(protocol.equals(DaemonProtocol.class.getName())) {
      return DaemonProtocol.versionID;
    } else if(protocol.equals(DNProtocol.class.getName())) {
      return DNProtocol.versionID;
    } else {
      return proceed(protocol, clientVersion);
    }
  }
}

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.hadoop.hdfs.server.datanode;

import java.io.File;
import java.io.IOException;
import java.util.AbstractList;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.test.system.DNProtocol;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.test.system.DaemonProtocol;

public privileged aspect DataNodeAspect {
  declare parents : DataNode implements DNProtocol;

  public Configuration DataNode.getDaemonConf() {
    return super.getConf();
  }

  pointcut dnConstructorPointcut(Configuration conf, AbstractList<File> dirs) :
    call(DataNode.new(Configuration, AbstractList<File>))
    && args(conf, dirs);

  after(Configuration conf, AbstractList<File> dirs) returning (DataNode datanode):
    dnConstructorPointcut(conf, dirs) {
    try {
      UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
      datanode.setUser(ugi.getShortUserName());
    } catch (IOException e) {
      datanode.LOG.warn("Unable to get the user information for the " +
          "Jobtracker");
    }
    datanode.setReady(true);
  }

  pointcut getVersionAspect(String protocol, long clientVersion) :
    execution(public long DataNode.getProtocolVersion(String ,
      long) throws IOException) && args(protocol, clientVersion);

  long around(String protocol, long clientVersion) :
    getVersionAspect(protocol, clientVersion) {
    if(protocol.equals(DaemonProtocol.class.getName())) {
      return DaemonProtocol.versionID;
    } else if(protocol.equals(DNProtocol.class.getName())) {
      return DNProtocol.versionID;
    } else {
      return proceed(protocol, clientVersion);
    }
  }
}


Explain after advice behavior in static method mock aspect

This commit makes it clear that mock verification will not occur if an
advised test method throws an exception.

/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.mock.staticmock;

/**
 * Annotation-based aspect to use in test builds to enable mocking of static methods
 * on JPA-annotated {@code @Entity} classes, as used by Spring Roo for so-called
 * finder methods.
 *
 *
Mocking will occur within the call stack of any method in a class (typically a
 * test class) that is annotated with {@code @MockStaticEntityMethods}.
 *
 *
This aspect also provides static methods to simplify the programming model for
 * setting expectations and entering playback mode.
 *
 *
Usage

 *

 *
Annotate a test class with {@code @MockStaticEntityMethods}.
 *
In each test method, the {@code AnnotationDrivenStaticEntityMockingControl}
 * will begin in recording mode.
 *
Invoke static methods on JPA-annotated {@code @Entity} classes, with each
 * recording-mode invocation being followed by an invocation of either
 * {@link #expectReturn(Object)} or {@link #expectThrow(Throwable)} on the
 * {@code AnnotationDrivenStaticEntityMockingControl}.
 *
Invoke the {@link #playback()} method.
 *
Call the code you wish to test that uses the static methods on the
 * JPA-annotated {@code @Entity} classes.
 *
Verification will occur automatically after the test method has executed
 * and returned. However, mock verification will not occur if the test method
 * throws an exception.
 *

 *
 * @author Rod Johnson
 * @author Ramnivas Laddad
 * @author Sam Brannen
 * @see MockStaticEntityMethods
 */
public aspect AnnotationDrivenStaticEntityMockingControl extends AbstractMethodMockingControl {

	/**
	 * Expect the supplied {@link Object} to be returned by the previous static
	 * method invocation.
	 * @see #playback()
	 */
	public static void expectReturn(Object retVal) {
		AnnotationDrivenStaticEntityMockingControl.aspectOf().expectReturnInternal(retVal);
	}

	/**
	 * Expect the supplied {@link Throwable} to be thrown by the previous static
	 * method invocation.
	 * @see #playback()
	 */
	public static void expectThrow(Throwable throwable) {
		AnnotationDrivenStaticEntityMockingControl.aspectOf().expectThrowInternal(throwable);
	}

	/**
	 * Stop recording mock expectations and enter playback mode.
	 * @see #expectReturn(Object)
	 * @see #expectThrow(Throwable)
	 */
	public static void playback() {
		AnnotationDrivenStaticEntityMockingControl.aspectOf().playbackInternal();
	}

	// Apparently, the following pointcut was originally defined to only match
	// methods directly annotated with @Test (in order to allow methods in
	// @MockStaticEntityMethods classes to invoke each other without resetting
	// the mocking environment); however, this is no longer the case. The current
	// pointcut applies to all public methods in @MockStaticEntityMethods classes.
	protected pointcut mockStaticsTestMethod() : execution(public * (@MockStaticEntityMethods *).*(..));

	protected pointcut methodToMock() : execution(public static * (@javax.persistence.Entity *).*(..));

}

/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.mock.staticmock;

/**
 * Annotation-based aspect to use in test builds to enable mocking of static methods
 * on JPA-annotated {@code @Entity} classes, as used by Spring Roo for so-called
 * finder methods.
 *
 *
Mocking will occur within the call stack of any method in a class (typically a
 * test class) that is annotated with {@code @MockStaticEntityMethods}.
 *
 *
This aspect also provides static methods to simplify the programming model for
 * setting expectations and entering playback mode.
 *
 *
Usage:
 *

 *
Annotate a test class with {@code @MockStaticEntityMethods}.
 *
In each test method, {@code AnnotationDrivenStaticEntityMockingControl}
 * will begin in recording mode.
 *
Invoke static methods on JPA-annotated {@code @Entity} classes, with each
 * recording-mode invocation being followed by an invocation of either the static
 * {@link #expectReturn(Object)} method or the static {@link #expectThrow(Throwable)}
 * method on {@code AnnotationDrivenStaticEntityMockingControl}.
 *
Invoke the static {@link #playback()} method.
 *
Call the code you wish to test that uses the static methods.
 *
Verification will occur automatically.
 *

 *
 * @author Rod Johnson
 * @author Ramnivas Laddad
 * @author Sam Brannen
 * @see MockStaticEntityMethods
 */
public aspect AnnotationDrivenStaticEntityMockingControl extends AbstractMethodMockingControl {

	/**
	 * Expect the supplied {@link Object} to be returned by the previous static
	 * method invocation.
	 * @see #playback()
	 */
	public static void expectReturn(Object retVal) {
		AnnotationDrivenStaticEntityMockingControl.aspectOf().expectReturnInternal(retVal);
	}

	/**
	 * Expect the supplied {@link Throwable} to be thrown by the previous static
	 * method invocation.
	 * @see #playback()
	 */
	public static void expectThrow(Throwable throwable) {
		AnnotationDrivenStaticEntityMockingControl.aspectOf().expectThrowInternal(throwable);
	}

	/**
	 * Stop recording mock expectations and enter playback mode.
	 * @see #expectReturn(Object)
	 * @see #expectThrow(Throwable)
	 */
	public static void playback() {
		AnnotationDrivenStaticEntityMockingControl.aspectOf().playbackInternal();
	}

	// Apparently, the following pointcut was originally defined to only match
	// methods directly annotated with @Test (in order to allow methods in
	// @MockStaticEntityMethods classes to invoke each other without resetting
	// the mocking environment); however, this is no longer the case. The current
	// pointcut applies to all public methods in @MockStaticEntityMethods classes.
	protected pointcut mockStaticsTestMethod() : execution(public * (@MockStaticEntityMethods *).*(..));

	protected pointcut methodToMock() : execution(public static * (@javax.persistence.Entity *).*(..));

}


More work on hyperlinking; fixed issue with missing sources in Open Type; highlighting for additional basic types.

git-svn-id: f4ecfe233223879a4a16cd7937f11b5152963d8e@19021 5e8d7ff9-d8ef-0310-90f0-a4852d11357a

/*
 * Copyright 2005-2009 LAMP/EPFL
 */
// $Id$

package scala.tools.eclipse.contribution.weaving.jdt.cfprovider;

import java.util.HashSet;

import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.search.TypeNameMatch;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.IGenericType;
import org.eclipse.jdt.internal.compiler.env.ISourceImport;
import org.eclipse.jdt.internal.compiler.env.ISourceType;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter;
import org.eclipse.jdt.internal.core.BinaryType;
import org.eclipse.jdt.internal.core.ClassFile;
import org.eclipse.jdt.internal.core.CompilationUnitElementInfo;
import org.eclipse.jdt.internal.core.ImportDeclaration;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.NameLookup;
import org.eclipse.jdt.internal.core.Openable;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.eclipse.jdt.internal.core.SourceMapper;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver;
import org.eclipse.jdt.internal.core.hierarchy.HierarchyType;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.jdt.internal.corext.util.OpenTypeHistory;
import org.eclipse.jdt.internal.ui.filters.InnerClassFilesFilter;
import org.eclipse.jdt.ui.SharedASTProvider;
import org.eclipse.jface.viewers.Viewer;

import scala.tools.eclipse.contribution.weaving.jdt.IScalaClassFile;
import scala.tools.eclipse.contribution.weaving.jdt.IScalaElement;

@SuppressWarnings("restriction")
public privileged aspect ClassFileProviderAspect {

  pointcut classFileCreations(PackageFragment parent, String name) :
    call(protected ClassFile.new(PackageFragment, String)) &&
    within(org.eclipse.jdt..*) &&
    args(parent, name);

  pointcut mapSource() :
    execution(IBuffer ClassFile.mapSource(SourceMapper, IBinaryType)) &&
    target(IScalaClassFile);

  pointcut mapSource2() :
    execution(void SourceMapper.mapSource(IType, char[], IBinaryType));

  pointcut getSourceFileName(BinaryType bt) :
    execution(String BinaryType.getSourceFileName(IBinaryType)) &&
    target(bt);

  pointcut select(Viewer viewer, Object parentElement, Object element) :
    execution(boolean InnerClassFilesFilter.select(Viewer, Object, Object)) &&
    args(viewer, parentElement, element);

  pointcut getChildren(BinaryType bt) :
    execution(IJavaElement[] BinaryType.getChildren()) &&
    target(bt);

  pointcut getAST(ITypeRoot element, SharedASTProvider.WAIT_FLAG waitFlag, IProgressMonitor progressMonitor) :
    execution(CompilationUnit SharedASTProvider.getAST(ITypeRoot, SharedASTProvider.WAIT_FLAG, IProgressMonitor)) &&
    args(element, waitFlag, progressMonitor);

  pointcut resolve(HierarchyResolver hr, IGenericType suppliedType) :
    execution(void HierarchyResolver.resolve(IGenericType)) &&
    target(hr) &&
    args(suppliedType);

  pointcut remember(HierarchyResolver hr, IType type, ReferenceBinding typeBinding) :
    execution(void HierarchyResolver.remember(IType, ReferenceBinding)) &&
    target(hr) &&
    args(type, typeBinding);

  pointcut acceptType(IType type, int acceptFlags, boolean isSourceType) :
    execution(boolean NameLookup.acceptType(IType, int, boolean)) &&
    args(type, acceptFlags, isSourceType);

  pointcut find(SearchableEnvironment se, String typeName, String packageName) :
    execution(NameEnvironmentAnswer find(String, String)) &&
    target(se) &&
    args(typeName, packageName);

  pointcut convert(SourceTypeConverter stc, ISourceType[] sourceTypes, CompilationResult compilationResult) :
    execution(CompilationUnitDeclaration SourceTypeConverter.convert(ISourceType[], CompilationResult)) &&
    target(stc) &&
    args(sourceTypes, compilationResult);

  pointcut getAncestor(JavaElement je, int ancestorType) :
    execution(IJavaElement JavaElement.getAncestor(int)) &&
    target(je) &&
    args(ancestorType);

  pointcut isContainerDirty(TypeNameMatch match) :
    execution(boolean OpenTypeHistory.isContainerDirty(TypeNameMatch)) &&
    args(match);

  ClassFile around(PackageFragment parent, String name) :
    classFileCreations(parent, name) {

    ClassFile javaClassFile = proceed(parent, name);
    byte[] bytes;

    try {
      bytes = javaClassFile.getBytes();
    } catch(Throwable t) {
      return javaClassFile;
    }

    for (IClassFileProvider provider : ClassFileProviderRegistry.getInstance().getProviders()) {
      ClassFile cf = provider.create(bytes, parent, name);
      if (cf != null)
        return cf;
    }

    return javaClassFile;
  }

  void around() :
    mapSource2() &&
    cflow(mapSource()) {
    return;
  }

  boolean around(Viewer viewer, Object parentElement, Object element) :
    select(viewer, parentElement, element) {
    if (element instanceof IScalaClassFile) {
      IClassFile classFile = (IClassFile) element;
      String name = classFile.getElementName();
      int dollarIndex = name.indexOf('$');
      return dollarIndex == -1 || dollarIndex == name.length()-7; // Trailing '$' implies object rather than inner class
    }
    return proceed(viewer, parentElement, element);
  }

  IJavaElement[] around(BinaryType bt) throws JavaModelException :
    getChildren(bt) &&
    target(BinaryType) {
    ClassFile cf = (ClassFile)bt.parent;
    if (cf instanceof IScalaClassFile)
      return cf.getChildren();
    else
      return proceed(bt);
  }

  CompilationUnit around(ITypeRoot element, SharedASTProvider.WAIT_FLAG waitFlag, IProgressMonitor progressMonitor) :
    getAST(element, waitFlag, progressMonitor) {
      if (element instanceof IScalaElement)
        return null;
      else
        return proceed(element, waitFlag, progressMonitor);
  }

  void around(HierarchyResolver hr, IGenericType suppliedType) :
    resolve(hr, suppliedType) {
    if (!suppliedType.isBinaryType()) {
      IType tpe = ((SourceTypeElementInfo)suppliedType).getHandle();
      IClassFile cf = tpe.getClassFile();
      if (cf instanceof IScalaClassFile) {
        hr.resolve(new Openable[]{(Openable)cf}, new HashSet(), null);
        return;
      }
    }

    proceed(hr, suppliedType);
  }

  void around(HierarchyResolver hr, IType type, ReferenceBinding typeBinding) :
    remember(hr, type, typeBinding) {
    if (((IOpenable)type.getCompilationUnit()).isOpen()) {
      try {
        IGenericType genericType = (IGenericType)((JavaElement)type).getElementInfo();
        hr.remember(genericType, typeBinding);
      } catch (JavaModelException e) {
        // cannot happen since element is open
        return;
      }
    } else {
      if (typeBinding == null) return;

      TypeDeclaration typeDeclaration = ((SourceTypeBinding)typeBinding).scope.referenceType();

      // simple super class name
      char[] superclassName = null;
      TypeReference superclass;
      if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {
        superclass = typeDeclaration.allocation.type;
      } else {
        superclass = typeDeclaration.superclass;
      }
      if (superclass != null) {
        char[][] typeName = superclass.getTypeName();
        superclassName = typeName == null ? null : typeName[typeName.length-1];
      }

      // simple super interface names
      char[][] superInterfaceNames = null;
      TypeReference[] superInterfaces = typeDeclaration.superInterfaces;
      if (superInterfaces != null) {
        int length = superInterfaces.length;
        superInterfaceNames = new char[length][];
        for (int i = 0; i < length; i++) {
          TypeReference superInterface = superInterfaces[i];
          char[][] typeName = superInterface.getTypeName();
          superInterfaceNames[i] = typeName[typeName.length-1];
        }
      }

      HierarchyType hierarchyType = new HierarchyType(
        type,
        typeDeclaration.name,
        typeDeclaration.binding.modifiers,
        superclassName,
        superInterfaceNames);
      hr.remember(hierarchyType, typeDeclaration.binding);
    }
  }

  String around(BinaryType bt) :
    getSourceFileName(bt) {
    IJavaElement parent = bt.getParent();
    if (parent instanceof IScalaClassFile)
      return ((IScalaClassFile)parent).getSourceFileName();
    else
      return proceed(bt);
  }

  boolean around(IType type, int acceptFlags, boolean isSourceType) :
    acceptType(type, acceptFlags, isSourceType) {
    if (!isSourceType && (type instanceof SourceType)) {
      IJavaElement parent = type.getParent();
      if (parent instanceof IScalaClassFile)
        return proceed(type, acceptFlags, true);
    }

    return proceed(type, acceptFlags, isSourceType);
  }

  CompilationUnitDeclaration around(SourceTypeConverter stc, ISourceType[] sourceTypes, CompilationResult compilationResult) throws JavaModelException :
    convert(stc, sourceTypes, compilationResult) {
    stc.unit = new CompilationUnitDeclaration(stc.problemReporter, compilationResult, 0);
    // not filled at this point

    if (sourceTypes.length == 0) return stc.unit;
    SourceTypeElementInfo topLevelTypeInfo = (SourceTypeElementInfo) sourceTypes[0];
    org.eclipse.jdt.core.ICompilationUnit cuHandle = topLevelTypeInfo.getHandle().getCompilationUnit();
    stc.cu = (ICompilationUnit) cuHandle;

    CompilationUnitElementInfo cuei = null;
    Object info = ((JavaElement)stc.cu).getElementInfo();
    if (info instanceof CompilationUnitElementInfo)
      cuei = (CompilationUnitElementInfo)info;

    if (stc.has1_5Compliance && cuei != null && cuei.annotationNumber > 10) { // experimental value
      // if more than 10 annotations, diet parse as this is faster
      return new Parser(stc.problemReporter, true).dietParse(stc.cu, compilationResult);
    }

    /* only positions available */
    int start = topLevelTypeInfo.getNameSourceStart();
    int end = topLevelTypeInfo.getNameSourceEnd();

    /* convert package and imports */
    String[] packageName = ((PackageFragment) cuHandle.getParent()).names;
    if (packageName.length > 0)
      // if its null then it is defined in the default package
      stc.unit.currentPackage =
        stc.createImportReference(packageName, start, end, false, ClassFileConstants.AccDefault);
    IImportDeclaration[] importDeclarations = topLevelTypeInfo.getHandle().getCompilationUnit().getImports();
    int importCount = importDeclarations.length;
    stc.unit.imports = new ImportReference[importCount];
    for (int i = 0; i < importCount; i++) {
      ImportDeclaration importDeclaration = (ImportDeclaration) importDeclarations[i];
      ISourceImport sourceImport = (ISourceImport) importDeclaration.getElementInfo();
      String nameWithoutStar = importDeclaration.getNameWithoutStar();
      stc.unit.imports[i] = stc.createImportReference(
        Util.splitOn('.', nameWithoutStar, 0, nameWithoutStar.length()),
        sourceImport.getDeclarationSourceStart(),
        sourceImport.getDeclarationSourceEnd(),
        importDeclaration.isOnDemand(),
        sourceImport.getModifiers());
    }
    /* convert type(s) */
    try {
      int typeCount = sourceTypes.length;
      final TypeDeclaration[] types = new TypeDeclaration[typeCount];
      /*
       * We used a temporary types collection to prevent this.unit.types from being null during a call to
       * convert(...) when the source is syntactically incorrect and the parser is flushing the unit's types.
       * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=97466
       */
      for (int i = 0; i < typeCount; i++) {
        SourceTypeElementInfo typeInfo = (SourceTypeElementInfo) sourceTypes[i];
        types[i] = stc.convert((SourceType) typeInfo.getHandle(), compilationResult);
      }
      stc.unit.types = types;
      return stc.unit;
    } catch (SourceTypeConverter.AnonymousMemberFound e) {
      return new Parser(stc.problemReporter, true).parse(stc.cu, compilationResult);
    }
  }

  IJavaElement around(JavaElement je, int ancestorType) :
    getAncestor(je, ancestorType) {
    IJavaElement element = je;
    while (element != null) {
      if (element.getElementType() == ancestorType)
        return element;
      else if (element instanceof IScalaClassFile && ancestorType == IJavaElement.COMPILATION_UNIT)
        return ((JavaElement)element).getCompilationUnit();
      element= element.getParent();
    }
    return null;
  }

  boolean around(TypeNameMatch match) :
    isContainerDirty(match) {
    org.eclipse.jdt.core.ICompilationUnit cu = match.getType().getCompilationUnit();
    if (cu == null) {
      return false;
    }
    IResource resource= cu.getResource();
    if (resource == null)
      return false;

    ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
    ITextFileBuffer textFileBuffer= manager.getTextFileBuffer(resource.getFullPath(), LocationKind.IFILE);
    if (textFileBuffer != null) {
      return textFileBuffer.isDirty();
    }
    return false;
  }
}

/*
 * Copyright 2005-2009 LAMP/EPFL
 */
// $Id$

package scala.tools.eclipse.contribution.weaving.jdt.cfprovider;

import org.eclipse.jdt.core.IJavaElement;

import java.util.HashSet;

import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.env.ISourceType;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.IGenericType;
import org.eclipse.jdt.internal.compiler.env.ISourceImport;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter;
import org.eclipse.jdt.internal.core.BinaryType;
import org.eclipse.jdt.internal.core.ClassFile;
import org.eclipse.jdt.internal.core.CompilationUnitElementInfo;
import org.eclipse.jdt.internal.core.ImportDeclaration;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.Openable;
import org.eclipse.jdt.internal.core.NameLookup;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.eclipse.jdt.internal.core.SourceMapper;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver;
import org.eclipse.jdt.internal.core.hierarchy.HierarchyType;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.jdt.internal.ui.filters.InnerClassFilesFilter;
import org.eclipse.jdt.ui.SharedASTProvider;
import org.eclipse.jface.viewers.Viewer;

import scala.tools.eclipse.contribution.weaving.jdt.IScalaElement;
import scala.tools.eclipse.contribution.weaving.jdt.IScalaClassFile;

@SuppressWarnings("restriction")
public privileged aspect ClassFileProviderAspect {

  pointcut classFileCreations(PackageFragment parent, String name) :
    call(protected ClassFile.new(PackageFragment, String)) &&
    within(org.eclipse.jdt..*) &&
    args(parent, name);

  pointcut mapSource() :
    execution(IBuffer ClassFile.mapSource(SourceMapper, IBinaryType)) &&
    target(IScalaClassFile);

  pointcut mapSource2() :
    execution(void SourceMapper.mapSource(IType, char[], IBinaryType));

  pointcut getSourceFileName(BinaryType bt) :
    execution(String BinaryType.getSourceFileName(IBinaryType)) &&
    target(bt);

  pointcut select(Viewer viewer, Object parentElement, Object element) :
    execution(boolean InnerClassFilesFilter.select(Viewer, Object, Object)) &&
    args(viewer, parentElement, element);

  pointcut getChildren(BinaryType bt) :
    execution(IJavaElement[] BinaryType.getChildren()) &&
    target(bt);

  pointcut getAST(ITypeRoot element, SharedASTProvider.WAIT_FLAG waitFlag, IProgressMonitor progressMonitor) :
    execution(CompilationUnit SharedASTProvider.getAST(ITypeRoot, SharedASTProvider.WAIT_FLAG, IProgressMonitor)) &&
    args(element, waitFlag, progressMonitor);

  pointcut resolve(HierarchyResolver hr, IGenericType suppliedType) :
    execution(void HierarchyResolver.resolve(IGenericType)) &&
    target(hr) &&
    args(suppliedType);

  pointcut remember(HierarchyResolver hr, IType type, ReferenceBinding typeBinding) :
    execution(void HierarchyResolver.remember(IType, ReferenceBinding)) &&
    target(hr) &&
    args(type, typeBinding);

  pointcut acceptType(IType type, int acceptFlags, boolean isSourceType) :
    execution(boolean NameLookup.acceptType(IType, int, boolean)) &&
    args(type, acceptFlags, isSourceType);

  pointcut find(SearchableEnvironment se, String typeName, String packageName) :
    execution(NameEnvironmentAnswer find(String, String)) &&
    target(se) &&
    args(typeName, packageName);

  pointcut convert(SourceTypeConverter stc, ISourceType[] sourceTypes, CompilationResult compilationResult) :
    execution(CompilationUnitDeclaration SourceTypeConverter.convert(ISourceType[], CompilationResult)) &&
    target(stc) &&
    args(sourceTypes, compilationResult);

  pointcut getAncestor(JavaElement je, int ancestorType) :
    execution(IJavaElement JavaElement.getAncestor(int)) &&
    target(je) &&
    args(ancestorType);

  ClassFile around(PackageFragment parent, String name) :
    classFileCreations(parent, name) {

    ClassFile javaClassFile = proceed(parent, name);
    byte[] bytes;

    try {
      bytes = javaClassFile.getBytes();
    } catch(Throwable t) {
      return javaClassFile;
    }

    for (IClassFileProvider provider : ClassFileProviderRegistry.getInstance().getProviders()) {
      ClassFile cf = provider.create(bytes, parent, name);
      if (cf != null)
        return cf;
    }

    return javaClassFile;
  }

  void around() :
    mapSource2() &&
    cflow(mapSource()) {
    return;
  }

  boolean around(Viewer viewer, Object parentElement, Object element) :
    select(viewer, parentElement, element) {
    if (element instanceof IScalaClassFile) {
      IClassFile classFile = (IClassFile) element;
      String name = classFile.getElementName();
      int dollarIndex = name.indexOf('$');
      return dollarIndex == -1 || dollarIndex == name.length()-7; // Trailing '$' implies object rather than inner class
    }
    return proceed(viewer, parentElement, element);
  }

  IJavaElement[] around(BinaryType bt) throws JavaModelException :
    getChildren(bt) &&
    target(BinaryType) {
    ClassFile cf = (ClassFile)bt.parent;
    if (cf instanceof IScalaClassFile)
      return cf.getChildren();
    else
      return proceed(bt);
  }

  CompilationUnit around(ITypeRoot element, SharedASTProvider.WAIT_FLAG waitFlag, IProgressMonitor progressMonitor) :
    getAST(element, waitFlag, progressMonitor) {
      if (element instanceof IScalaElement)
        return null;
      else
        return proceed(element, waitFlag, progressMonitor);
  }

  void around(HierarchyResolver hr, IGenericType suppliedType) :
    resolve(hr, suppliedType) {
    if (!suppliedType.isBinaryType()) {
      IType tpe = ((SourceTypeElementInfo)suppliedType).getHandle();
      IClassFile cf = tpe.getClassFile();
      if (cf instanceof IScalaClassFile) {
        hr.resolve(new Openable[]{(Openable)cf}, new HashSet(), null);
        return;
      }
    }

    proceed(hr, suppliedType);
  }

  void around(HierarchyResolver hr, IType type, ReferenceBinding typeBinding) :
    remember(hr, type, typeBinding) {
    if (((IOpenable)type.getCompilationUnit()).isOpen()) {
      try {
        IGenericType genericType = (IGenericType)((JavaElement)type).getElementInfo();
        hr.remember(genericType, typeBinding);
      } catch (JavaModelException e) {
        // cannot happen since element is open
        return;
      }
    } else {
      if (typeBinding == null) return;

      TypeDeclaration typeDeclaration = ((SourceTypeBinding)typeBinding).scope.referenceType();

      // simple super class name
      char[] superclassName = null;
      TypeReference superclass;
      if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {
        superclass = typeDeclaration.allocation.type;
      } else {
        superclass = typeDeclaration.superclass;
      }
      if (superclass != null) {
        char[][] typeName = superclass.getTypeName();
        superclassName = typeName == null ? null : typeName[typeName.length-1];
      }

      // simple super interface names
      char[][] superInterfaceNames = null;
      TypeReference[] superInterfaces = typeDeclaration.superInterfaces;
      if (superInterfaces != null) {
        int length = superInterfaces.length;
        superInterfaceNames = new char[length][];
        for (int i = 0; i < length; i++) {
          TypeReference superInterface = superInterfaces[i];
          char[][] typeName = superInterface.getTypeName();
          superInterfaceNames[i] = typeName[typeName.length-1];
        }
      }

      HierarchyType hierarchyType = new HierarchyType(
        type,
        typeDeclaration.name,
        typeDeclaration.binding.modifiers,
        superclassName,
        superInterfaceNames);
      hr.remember(hierarchyType, typeDeclaration.binding);
    }
  }

  String around(BinaryType bt) :
    getSourceFileName(bt) {
    IJavaElement parent = bt.getParent();
    if (parent instanceof IScalaClassFile)
      return ((IScalaClassFile)parent).getSourceFileName();
    else
      return proceed(bt);
  }

  boolean around(IType type, int acceptFlags, boolean isSourceType) :
    acceptType(type, acceptFlags, isSourceType) {
    if (!isSourceType && (type instanceof SourceType)) {
      IJavaElement parent = type.getParent();
      if (parent instanceof IScalaClassFile)
        return proceed(type, acceptFlags, true);
    }

    return proceed(type, acceptFlags, isSourceType);
  }

  CompilationUnitDeclaration around(SourceTypeConverter stc, ISourceType[] sourceTypes, CompilationResult compilationResult) throws JavaModelException :
    convert(stc, sourceTypes, compilationResult) {
    stc.unit = new CompilationUnitDeclaration(stc.problemReporter, compilationResult, 0);
    // not filled at this point

    if (sourceTypes.length == 0) return stc.unit;
    SourceTypeElementInfo topLevelTypeInfo = (SourceTypeElementInfo) sourceTypes[0];
    org.eclipse.jdt.core.ICompilationUnit cuHandle = topLevelTypeInfo.getHandle().getCompilationUnit();
    stc.cu = (ICompilationUnit) cuHandle;

    CompilationUnitElementInfo cuei = null;
    Object info = ((JavaElement)stc.cu).getElementInfo();
    if (info instanceof CompilationUnitElementInfo)
      cuei = (CompilationUnitElementInfo)info;

    if (stc.has1_5Compliance && cuei != null && cuei.annotationNumber > 10) { // experimental value
      // if more than 10 annotations, diet parse as this is faster
      return new Parser(stc.problemReporter, true).dietParse(stc.cu, compilationResult);
    }

    /* only positions available */
    int start = topLevelTypeInfo.getNameSourceStart();
    int end = topLevelTypeInfo.getNameSourceEnd();

    /* convert package and imports */
    String[] packageName = ((PackageFragment) cuHandle.getParent()).names;
    if (packageName.length > 0)
      // if its null then it is defined in the default package
      stc.unit.currentPackage =
        stc.createImportReference(packageName, start, end, false, ClassFileConstants.AccDefault);
    IImportDeclaration[] importDeclarations = topLevelTypeInfo.getHandle().getCompilationUnit().getImports();
    int importCount = importDeclarations.length;
    stc.unit.imports = new ImportReference[importCount];
    for (int i = 0; i < importCount; i++) {
      ImportDeclaration importDeclaration = (ImportDeclaration) importDeclarations[i];
      ISourceImport sourceImport = (ISourceImport) importDeclaration.getElementInfo();
      String nameWithoutStar = importDeclaration.getNameWithoutStar();
      stc.unit.imports[i] = stc.createImportReference(
        Util.splitOn('.', nameWithoutStar, 0, nameWithoutStar.length()),
        sourceImport.getDeclarationSourceStart(),
        sourceImport.getDeclarationSourceEnd(),
        importDeclaration.isOnDemand(),
        sourceImport.getModifiers());
    }
    /* convert type(s) */
    try {
      int typeCount = sourceTypes.length;
      final TypeDeclaration[] types = new TypeDeclaration[typeCount];
      /*
       * We used a temporary types collection to prevent this.unit.types from being null during a call to
       * convert(...) when the source is syntactically incorrect and the parser is flushing the unit's types.
       * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=97466
       */
      for (int i = 0; i < typeCount; i++) {
        SourceTypeElementInfo typeInfo = (SourceTypeElementInfo) sourceTypes[i];
        types[i] = stc.convert((SourceType) typeInfo.getHandle(), compilationResult);
      }
      stc.unit.types = types;
      return stc.unit;
    } catch (SourceTypeConverter.AnonymousMemberFound e) {
      return new Parser(stc.problemReporter, true).parse(stc.cu, compilationResult);
    }
  }

  IJavaElement around(JavaElement je, int ancestorType) :
    getAncestor(je, ancestorType) {
    IJavaElement element = je;
    while (element != null) {
      if (element.getElementType() == ancestorType)
        return element;
      else if (element instanceof IScalaClassFile && ancestorType == IJavaElement.COMPILATION_UNIT)
        return ((JavaElement)element).getCompilationUnit();
      element= element.getParent();
    }
    return null;
  }
}


Fixed #1741.

Fixed #1741.

git-svn-id: f4ecfe233223879a4a16cd7937f11b5152963d8e@17176 5e8d7ff9-d8ef-0310-90f0-a4852d11357a

/*
 * Copyright 2005-2009 LAMP/EPFL
 */
// $Id$

package scala.tools.eclipse.contribution.weaving.jdt.builderoptions;

import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.internal.core.builder.BatchImageBuilder;
//import org.eclipse.jdt.internal.core.builder.ClasspathMultiDirectory;
import org.eclipse.jdt.internal.core.util.Util;

@SuppressWarnings("restriction")
public privileged aspect ScalaJavaBuilderAspect {
  pointcut build() : execution(IProject[] ScalaJavaBuilder.build(int, Map, IProgressMonitor) throws CoreException);
  pointcut cleanOutputFolders(boolean copyBack) : args(copyBack) && execution(void BatchImageBuilder.cleanOutputFolders(boolean) throws CoreException);
  pointcut isJavaLikeFileName(String fileName) : args(fileName) && execution(boolean Util.isJavaLikeFileName(String));

  void around(BatchImageBuilder builder, boolean copyBack) throws CoreException : target(builder) && cleanOutputFolders(copyBack) && cflow(build()) {
    // Suppress the cleaning behaviour but do the extra resource copying if requested
    if (copyBack)
      for (int i = 0, l = builder.sourceLocations.length; i < l; i++) {
        org.eclipse.jdt.internal.core.builder.ClasspathMultiDirectory sourceLocation = builder.sourceLocations[i];
        if (sourceLocation.hasIndependentOutputFolder)
          builder.copyExtraResourcesBack(sourceLocation, false);
      }
  }

  boolean around(String fileName) : isJavaLikeFileName(fileName) && cflow(build()) && !cflow(cleanOutputFolders(*)) {
    if (fileName != null && fileName.endsWith("scala"))
      return false;
    else
      return proceed(fileName);
  }
}

/*
 * Copyright 2005-2009 LAMP/EPFL
 */
// $Id$

package scala.tools.eclipse.contribution.weaving.jdt.builderoptions;

import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.internal.core.builder.BatchImageBuilder;
import org.eclipse.jdt.internal.core.util.Util;

@SuppressWarnings("restriction")
public aspect ScalaJavaBuilderAspect {
  pointcut build() : execution(IProject[] ScalaJavaBuilder.build(int, Map, IProgressMonitor) throws CoreException);
  pointcut clean() : execution(void ScalaJavaBuilder.clean(IProgressMonitor) throws CoreException);
  pointcut cleanOutputFolders() : execution(void BatchImageBuilder.cleanOutputFolders(boolean) throws CoreException);
  pointcut isJavaLikeFileName(String fileName) : args(fileName) && execution(boolean Util.isJavaLikeFileName(String));

  void around() : cleanOutputFolders() && cflow(build()) {
    // Just suppress the behaviour
  }

  boolean around(String fileName) : isJavaLikeFileName(fileName) && (cflow(build()) || cflow(clean())) {
    if (fileName != null && fileName.endsWith("scala"))
      return false;
    else
      return proceed(fileName);
  }
}