package lawOfDemeter; import java.util.*; /** * The ImmediatePartBin aspect is part of a Law of Demeter Checker - a set of * classes and aspects that verify a given program does not violate * the Law of Demeter. This aspect stores the immediate parts, or * instance variables, of objects as they are set for later * examination by the Checker aspect. * * @author David H. Lorenz * @author Modifications made by Paul Freeman */ public aspect ImmediatePartBin extends Supplier pertarget(Any.ConstructorExecution()){ /** * What this supplier contains. */ public String contains(){ return "immediate part objects"; } /** * Returns the object associated with the supplied argument thisObj. * * @param thisObj * @return Returns the correct supplier for the supplied Object thisObject * if there is one, else returns null. */ public Supplier getSupplier(Object thisObj) { try{ // this needs to be "tried" as there might NOT be an aspect associated with this obj return this.aspectOf(thisObj); }catch(org.aspectj.lang.NoAspectBoundException e){ return null; } } /** * Advice that gathers the immediate part objects. */ before(): Any.Set() { // even though this aspect is declared perTarget, ensure we have the correct bin ImmediatePartBin partBin = ImmediatePartBin.aspectOf(thisJoinPoint.getTarget()); // remove the old value based on this partName String partName = thisJoinPoint.getSignature().getName(); partBin.removeOldValue(partName); // There is only one argument to a set join point. Object newValue = thisJoinPoint.getArgs()[0]; partBin.addNewValue(partName, newValue); partBin.add(newValue, direct+at(thisJoinPoint)); // System.out.println("Adding to ImmediatePartBin of " + thisJoinPoint.getTarget() + " --" + thisJoinPoint + at(thisJoinPoint) + " in " + thisJoinPoint.getTarget()); } // HashMap to store immediate part values by immediate part name so that old // immediate part values can be removed when new values are set // NOTE: must be a standard hashmap as the String representing the instance // variable name (the key) will not necessarily be the same object between // puts and removals. Counts of the instances of the target object in // the partNameTargets hashmap are maintained in an IdentityHashMap declared below. private HashMap partNameTargets = new HashMap(); // code to add the value to the partNameTargets HashMap protected void addNewValue(String partName, Object newValue){ partNameTargets.put(partName, newValue); long count = increaseCount(newValue); // System.out.println("-- set: " + partName + " = " + newValue + " count = " + count); } // code to remove the value from the partNameTargets HashMap AND the targets HashMap protected void removeOldValue(String partName){ // find the object that corresponds to the partName and remove this pair from partNameTargets Object oldVal = partNameTargets.remove(partName); long instanceCount = decreaseCount(oldVal); // System.out.println("-- removed partNameTarget: " + partName + " = " + oldVal + " count = " + instanceCount); // if removed the last oldVal instance, i.e. oldVal is not aliased to another // partName in this object, then remove it from targets. This is required since // each object exists only once in targets if(oldVal != null && instanceCount == 0){ targets.remove(oldVal); // System.out.println("-- removed target: " + partName + " = " + oldVal); } } //HashMap to store counts of target object instances in the partNameTargets HashMap // this count is used to determine whether or not it is safe to remove the object // instance from the targets IdentityHashMap. We must keep a count of the number // of times the target object exists in the partNameTargets hashmap as a single // object might be aliased to more than one part name. private IdentityHashMap targetInstances = new IdentityHashMap(); //helper code to increase the target instance count private long increaseCount(Object targetObject){ long count = getCount(targetObject) + 1; targetInstances.put(targetObject, new Long(count)); return count; } // helper code that returns the count for a particular target object private long getCount(Object targetObject){ Long count = (Long)targetInstances.get(targetObject); if(count == null){ return 0; }else{ return count.longValue(); } } // helper code that decreases the target instance count private long decreaseCount(Object targetObject){ long count = getCount(targetObject) - 1; if(count == 0){ targetInstances.remove(targetObject); }else if(count < 0){ count = 0; }else{ targetInstances.put(targetObject, new Long(count)); } return count; } }