package lawOfDemeter; import java.util.*; /** * The Any class 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 class contains all of the pointcuts that * need to be examined in an application to verify it meets the law of demeter. * * @author David H. Lorenz * @author Modifications made by Paul Freeman */ abstract class Any { // we encapsulate all pointcuts we need in a class and // refer to them later as Any.* // not in this package and either // - not within the controlflow of a call in this package // OR // - within the control flow of a method call in the Tests aspect of this package private pointcut scope(): !within(lawOfDemeter..*) && (!cflow(execution(* lawOfDemeter..*(..))) || cflow(execution(* lawOfDemeter.Tests.*(..)))); // capturing method calls. pointcut ConstructorCall(): scope() && call(*.new(..)); pointcut MethodCall(): scope() && call(* *(..)); // capturing method executions pointcut MainMethodExecution(): scope() && execution(void *.main(String[])); pointcut MethodExecution(): scope() && execution(* *(..)); pointcut ConstructorExecution(): scope() && execution(*.new (..)); pointcut Execution(): ConstructorExecution() || MethodExecution(); // capturing assignments to data members pointcut Set(): scope() && set(* *.*) ; // capturing accesses of data members pointcut Get(): scope() && get(* *.*); //Modify the pointcuts below to exclude join points from being checked by the application // denotes classes to avoid checking pointcut ToAvoid(): !Stable() && !(call(static * testcase..*(..)) || get(static testcase..* *)); // Karl: why !Stable // NOTE: With the following pointcuts, to nullify the pointcut, i.e. // have it pick out no join points, use: // Nothing() // as the sole designation // denotes all globally preferred static members // NOTE: scope() must always remain and be &&'ed with any further // pointcuts added. Multiple pointcuts added should be placed in parenteses // and separated with ||, i.e. scope() && (pointcut1() || pointcut2()) pointcut GlobalPreferred(): scope() && Nothing(); // denotes stable classes calls to which should not be checked; // multiple pointcuts should be separated with || pointcut Stable(): AllJavaPackageAccesses() ; // // what follows is a series of useful predefined pointcuts // // denotes the opposite of all classes // !within(*) can be read as not within any package. // It can be used to represent the empty set private pointcut Nothing(): !within(*); //denotes all statically accessed immediate parts and methods private pointcut AllStaticAccesses(): scope() && call(static * *.*(..)) || get(static * *); private pointcut AllJavaPackageAccesses(): scope() && call(* java..*(..)) || get(java..* *); } package lawOfDemeter; import java.util.*; /** * The Checker 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 verifies that method calls do not * violate the Law of Demeter. * * @author David H. Lorenz * @author Modifications made by Paul Freeman */ public aspect Checker { // statistcs variables private long totalChecks = 0; private long numViolations = 0; private Vector validChecks = new Vector(); // position 0 = total valid checks // position i = total valid checks where // i = num of matches perCheck /** * Advice that does the checking. */ before(): (Any.MethodCall() || Any.Get()) && Any.ToAvoid(){ Object thisObj = thisJoinPoint.getThis(); Object target = thisJoinPoint.getTarget(); Vector suppliers = new Vector(); int binCount = 0; // System.out.println("Checking--" + thisJoinPoint + JPUtil.at(thisJoinPoint)); totalChecks++; // get the global "Bin" objects // suppliers.add( ArgumentBin.aspectOf() ); // suppliers.add( LocallyConstructedBin.aspectOf() ); // suppliers.add( ReturnValueBin.aspectOf() ); // suppliers.add( GlobalPreferredBin.aspectOf() ); // get the "Bin" objects associated with the currently executing object (this) try{ // this needs to be "tried" as there might NOT be an aspect associated with this obj suppliers.add( ImmediatePartBin.aspectOf(thisObj) ); // ImmediatePartBin.aspectOf(thisObj).printBin(); }catch(org.aspectj.lang.NoAspectBoundException e){ } // ArgumentBin.aspectOf().printAllBins(); // LocallyConstructedBin.aspectOf().printAllBins(); // ReturnValueBin.aspectOf().printAllBins(); //check if target is in any of the bins boolean isInBin = false; Iterator supIt = suppliers.iterator(); while(supIt.hasNext()){ Supplier thisBin = (Supplier)supIt.next(); if(thisBin.contains(target)){ isInBin = true; binCount++; } } if (!isInBin){ // if the target is not a preferred supplier -> Violation numViolations++; System.out.println("!! LoD violation: " + thisJoinPoint + JPUtil.at(thisJoinPoint)); }else{ addToStats(binCount); } } // Keeps track of the valid count statistics private void addToStats(int binCount){ //initialize count being increased if necessary try{ if(validChecks.elementAt(0) == null); }catch(java.lang.ArrayIndexOutOfBoundsException e){ validChecks.setSize(1); validChecks.setElementAt(new Long(0), 0); // total count } try{ if(validChecks.elementAt(binCount) == null); }catch(java.lang.ArrayIndexOutOfBoundsException e){ validChecks.setSize(binCount + 1); validChecks.setElementAt(new Long(0), binCount); } // increase counts Long val; val = new Long(((Long)validChecks.elementAt(0)).longValue() + 1); validChecks.setElementAt(val, 0); val = new Long(((Long)validChecks.elementAt(binCount)).longValue() + 1); validChecks.setElementAt(val, binCount); } // prints statistics when program is done executing after(): Any.MainMethodExecution(){ long totalValid = 0; try{ totalValid = ((Long)validChecks.elementAt(0)).longValue(); }catch(java.lang.ArrayIndexOutOfBoundsException e){ } System.out.println("\nStatistics:"); System.out.println("Type \t\tCount\tValid%\tTotal%"); System.out.println("Total Checks\t\t" + totalChecks); System.out.println("Violations \t\t" + numViolations + "\t"+ fix((double)numViolations/(double)totalValid * 100, 2) + "\t"+ fix((double)numViolations/(double)totalChecks * 100, 2)); for(int i = 0; i < validChecks.size(); i++){ String type = ""; if(i==0){type = "Valid Checks";} else if(i==1){type = "As "+i+" Supplier";} else{type = "As "+i+" Suppliers";} long thisCount = ((Long)validChecks.elementAt(i)).longValue(); System.out.println(type + "\t\t" + thisCount + "\t"+ fix((double)thisCount/(double)totalValid * 100, 2) + "\t"+ fix((double)thisCount/(double)totalChecks * 100, 2)); } } // fixes a double to a certain number of decimal places private double fix(double aDouble, int numDec){ try{ String d = new String(new Double(aDouble).toString()); int dotPos = d.indexOf('.'); int len = d.length(); int lastPos = dotPos + numDec + 1; if(lastPos > len){lastPos = len;} return new Double(d.substring(0, lastPos)).doubleValue(); }catch(java.lang.NumberFormatException e){ return 0; } } } 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 perthis(Any.ConstructorExecution()){ /** * Prints the objects currently in the bin. * Used for debugging. */ public void printBin(){ System.out.println("-- immediate part objects --"); printHashMap(targets); System.out.println("----------------------------"); } /** * Advice that stores the immediate part objects, or instance variables, of * the object currently executing. */ before(): // direct parts Any.Set() { // There is only one argument to a set join point. // Becomes a preferred supplier and is therefore added to targets. addAll(thisJoinPoint.getArgs(), direct+at(thisJoinPoint)); // System.out.println("Adding--" + thisJoinPoint + at(thisJoinPoint)); } } package lawOfDemeter; public class JPUtil { protected static String at(org.aspectj.lang.JoinPoint jp) { return " at " + jp.getSourceLocation().getFileName() + ":" + jp.getSourceLocation().getLine() + ":" + jp.getSourceLocation().getColumn() + " this " + jp.getThis() + " target " + jp.getTarget(); } }package lawOfDemeter; import java.util.*; /** * The Supplier class is a super class for the bin aspects. * It provides common functionality around a HashMap (targets) * that keeps track of the preferred supplier objects stored in the * various bins. * * @author David H. Lorenz * @author Modifications made by Paul Freeman */ abstract class Supplier { boolean contains(Object target) { if (targets.containsKey(target)) { // System.out.println(" target found " + // targets.get(target) +" object id " + target.toString()); return true; } return false; } void add(Object target,String i) { targets.put(target,i); } void addAll(Object[] targetlist,String i) { for (int k=0; k