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; } } }