package lawOfDemeter.objectform;
import java.util.*;
import org.aspectj.lang.*;

public abstract aspect LawofDemeter{


//interfaces
public interface Checked{
}

public interface MethodSupplier{
}

public interface FieldSupplier{
}

//abstract pointcuts
public abstract pointcut Check(Object thiz, Checked target);
public abstract pointcut ArgumentRule();
public abstract pointcut AssociatedRule();
public abstract pointcut Set(FieldSupplier target, Object value);

//advice
Object around(Object thiz, Checked target):Check(thiz, target){
	//check the LOD 
	if(!((MethodSupplier)thiz).contains(target) &&
	   !((FieldSupplier)thiz).containsSupplier(target))
		System.out.println(" !! LoD Object Violation !! " + 
		thisJoinPoint + " at " + 
		thisJoinPoint.getSourceLocation());
	return proceed(thiz, target);
}

Object around():AssociatedRule(){
	Object r = proceed();
	HashSet enclosingSet= (HashSet) MethodSupplier.st.peek();
	enclosingSet.add(r);
	return r; 
}

Object around():ArgumentRule(){
	Vector v = new Vector();
	Object[] args = thisJoinPoint.getArgs();
	for(int i=0;i < args.length; i++)
		v.add(args[i]);
	HashSet newSet = new HashSet();
	newSet.addAll(v);

	MethodSupplier.st.push(newSet);
	Object r = proceed();
	MethodSupplier.st.pop();
	return r;
}

Object around(FieldSupplier target, Object value): Set(target, value){  
	//context insensitive situation
	Object newObj = proceed(target, value);

	JoinPoint.StaticPart sp = thisJoinPointStaticPart;
	String fieldName = sp.getSignature().
	   getDeclaringType().getName() + ":" +
	   sp.getSignature().getName();


	if(target.fieldNames.containsKey(fieldName))
		fieldName = (String) target.fieldNames.get(fieldName);
	else
		target.fieldNames.put(fieldName,fieldName);
	
	target.targets.put(fieldName, value);
	return newObj;
}

// MethodSupplier
private static Stack MethodSupplier.st = new Stack();

private boolean MethodSupplier.contains(Object target){
	if(this == target)
		return true;

	HashSet enclosingSet= (HashSet) st.peek();
	return enclosingSet.contains(target);
}
    
//FieldSupplier
private HashMap FieldSupplier.targets = new HashMap();
private HashMap FieldSupplier.fieldNames = new HashMap();

private boolean FieldSupplier.containsSupplier(Object supplier){
	return  targets.containsValue(supplier);
}

}



