// Main behavior file. Contains the behavior for all the metrics // except for LoD. CompilationUnits { (@ void printSingleFileMetrics() { Enumeration units = getUnits().elements(); System.out.println(""); System.out.println("*****************************"); System.out.println("*** Printing File Metrics ***"); System.out.println("*****************************"); while (units.hasMoreElements()) { CompilationUnit cu = (CompilationUnit) units.nextElement(); System.out.println(""); System.out.println("-------------------------------------------"); System.out.println("Metrics for the file: " + cu.printFileName()); System.out.println("-------------------------------------------"); cu.printFileMetrics(this); } } @) (@ void printSystemMetrics() { System.out.println(""); System.out.println("*******************************"); System.out.println("*** Printing System Metrics ***"); System.out.println("*******************************"); System.out.println("There are "+SIZ()+" classes in this system. (SIZ)"); System.out.println("There are "+NOH()+" hierarchies in this system. (NOH)"); System.out.println("There are "+NLC()+" leaf classes in this system. (NLC)"); System.out.println("There are "+NSI()+" subclasses in this system. (NSI)"); System.out.println("There are "+SysType1LoD()+" type 1 violations of the LoD"); System.out.println("There are "+SysType2LoD()+" type 2 violations of the LoD"); System.out.println("There average inheritance width is "+AWI(this)+". (AWI)"); System.out.println("There average inheritance depth is "+ADI(this)+". (ADI)"); } @) // The number of classes in the system int SIZ() to UnmodifiedClassDeclaration { (@ int i = 0;@) before UnmodifiedClassDeclaration (@ i = i + 1; @) return int (@ i @) } // The number of hierarcheis in the system is determined by the // the number of classes with no parent that have children. int NOH() to UnmodifiedClassDeclaration { (@ int i = 0; @) before UnmodifiedClassDeclaration (@ if(host.get_name() == null && host.get_children().hasMoreElements()) i = i + 1; @) return int (@ i @) } // The number of independent classes is determined by the // number fo classes that have no parent and no children int NIC() to UnmodifiedClassDeclaration { (@ int i = 0; @) before UnmodifiedClassDeclaration (@ if(host.get_name()==null && !host.get_children().hasMoreElements()) i = i + 1; @) return int (@ i @) } // The number of leaf classes is determine by the // number of classes that have a parent but no children int NLC() to UnmodifiedClassDeclaration { (@ int i = 0; @) before UnmodifiedClassDeclaration (@ if (host.get_name() != null && !host.get_children().hasMoreElements()) i = i + 1; @) return int (@ i @) } // The number of classes using inheritance is determined by the // number of classes that have parents int NSI() to UnmodifiedClassDeclaration { (@ int i = 0; @) before UnmodifiedClassDeclaration (@ if (host.get_name() != null) i = i + 1; @) return int (@ i @) } // The average depth of inheritance is determined by the // sum of all inheritance depths of each class divided by the // number of classes in the system. float ADI(CompilationUnits self) to UnmodifiedClassDeclaration { (@ float totalDepth = 0; @) before UnmodifiedClassDeclaration (@ CompilationUnits another = get_self(); totalDepth = totalDepth + host.DOI(another); @) return (@ totalDepth/get_self().SIZ(); @) } // The average width of inheritance is determined by the // sum of all inheritance widths of each class divided by the // number of classes in the system. float AWI(CompilationUnits self) to UnmodifiedClassDeclaration { (@ float totalWidth = 0; @) before UnmodifiedClassDeclaration (@ totalWidth = totalWidth + host.NOC(); @) return (@ totalWidth/ ((float)get_self().SIZ()); @) } void printHiearchies() to UnmodifiedClassDeclaration { (@ Name Parent; Enumeration Children; @) init (@ System.out.println("----------------------------"); System.out.println("---Printing the Hierarchy---"); System.out.println("----------------------------"); @) before UnmodifiedClassDeclaration (@ if (host.get_name() != null) Parent = host.get_name(); else Parent = null; Children = host.get_children().elements(); if (Parent != null && Children.hasMoreElements()) { System.out.print("The " + host.get_ident().toString() +" class has "); System.out.println(Parent.convertToString() + " as a parent. It's children are: "); while(Children.hasMoreElements()) { System.out.println(((Ident)Children.nextElement()).toString()); } } if (Parent == null && Children.hasMoreElements()) { System.out.print("The " + host.get_ident().toString() + " class has no parent."); System.out.println("It's children are: "); while(Children.hasMoreElements()) { System.out.println(((Ident)Children.nextElement()).toString()); } } if (Parent != null && !Children.hasMoreElements()) { System.out.print("The " + host.get_ident().toString() +" class has "); System.out.println(Parent.convertToString() + " as a parent. It has no children. "); } if (Parent == null && !Children.hasMoreElements()) { System.out.print("The " + host.get_ident().toString() +" class has "); System.out.println("no parent and no children. "); } @) } // Returns the compilationUnits (i.e. files) that are in the system Vector getUnits() to CompilationUnit { (@ Vector units = new Vector();@) before CompilationUnit (@ units.addElement(host);@) return Vector (@ units @) } // This method will install children pointers inside each parent object. // It does this by going to each class, reading it's parent name, // finding it's parent object and put it's name into the children object // of the parent. void updateChildren(CompilationUnits self) to UnmodifiedClassDeclaration { (@ Ident childName; UnmodifiedClassDeclaration parent; @) before UnmodifiedClassDeclaration (@ childName = host.get_ident(); if (false) System.out.print("**" +childName + "'s parent is "); if (host.get_name() != null) { if (false) System.out.println("**Inside updateChildren and trying to find: " + host.get_name().convertToString()); parent= self.findUCD(host.get_name()); if (parent != null) parent.get_children().addElement(childName); } else if (false) System.out.println("non existant."); @) } // This class will find a class object given it's name. UnmodifiedClassDeclaration findUCD(Name name) to {PackageDeclaration, UnmodifiedClassDeclaration} { (@ Name packageName; String localName; @) init (@ return_val = null; @) before PackageDeclaration (@ if (host.get_name() != null) packageName = host.get_name(); @) before UnmodifiedClassDeclaration (@ if (name == null) System.out.println("Inside UCD with a null name!"); String subject = name.convertToString(); if (false) System.out.println("** Inside findUCD looking for " + subject); if (packageName == null) { // Used to have new String(...) here localName = host.get_ident().toString(); } else { localName = packageName.convertToString() + "." + host.get_ident().toString(); } if (false) System.out.println("** and I found " + localName); if (localName.equals(subject)) return_val= host;@) } } // End CompilationUnits UnmodifiedClassDeclaration{ // This class returns the nubmer of Type2 LoD violation in a class. Type2 // violations are the violations that look like: // void o() { // H h = a(); // h.foo(); // } // where H is not a friendly class of the operation o. The friendly // classes are the class that are in class(o) data members, that // are passed to o as argument and classes of object that are created // in o (with new). // Passing in yourself looks strange. I had to do this because I wanted // to call other methods (namely CollectDataMembers()) and the code // that uses this call is actually compiled into a different class other // than UnmodifiedClassDeclaration.java. int Type2LoD(UnmodifiedClassDeclaration self) to MethodDeclaration { (@ Hashtable members; int i; @) before MethodDeclaration (@ // This is inefficient because it is called for each Method. It // should be in an init clause but they only support no arg // constructors. In this case, get_self() returns null. if (false) System.out.println("Checking " + host.getMethodName()); members = get_self().CollectDataMembers(); Hashtable args = host.CollectArgs(); Hashtable newObjs = host.CollectNewObjs(); if (false) System.out.println("CollectNewObjs returned: " + newObjs.toString()); // Combine the three hashtables Hashtable total = (Hashtable)args.clone(); Enumeration e = members.keys(); while (e.hasMoreElements()) { String key = (String)e.nextElement(); total.put(key,members.get(key)); } e = newObjs.keys(); while (e.hasMoreElements()) { String key = (String)e.nextElement(); total.put(key,newObjs.get(key)); } if (false) { System.out.println("Method " +host.getMethodName()+ " has the following friends:"); System.out.println(total.toString()); } i = i + host.countType2LoDViolations(total); @) return (@ i @) } // Returns the number of children for each class (@ int NOC() { return this.get_children().size(); } @) // Return the depth of inheritance for each class. The // entire system has to be passed in so the method can traverse // backwards looking for the respective parents. (@ int DOI(CompilationUnits system) { Name UCDtoFind = this.get_name(); UnmodifiedClassDeclaration parent; int i = 0; while (UCDtoFind != null) { if (false) System.out.println("Inside DOI starting with: "+ UCDtoFind.convertToString()); parent = system.findUCD(UCDtoFind); i = i + 1; if (parent != null) UCDtoFind = parent.get_name(); else UCDtoFind = null; } return i; } @) // Return the number of methods for the class int NOM() to MethodDeclarator { (@ int i; @) init (@ i = 0;@) before MethodDeclarator (@ i = i + 1; @) return int (@ i @) } // Returns the number of public methods (the interface) for the class int CIS() to MM_Public { (@ int i; @) init (@ i = 0;@) before MM_Public (@ i = i + 1; @) return int (@ i @) } // Returns the number of public attributes. This should be close to 0! int NPA() to FM_Public { (@ int i; @) init (@ i = 0;@) before FM_Public (@ i = i + 1; @) return int (@ i @) } // Returns the number of data members of the class int NOA() to FieldDeclaration { (@ int i; @) init (@ i = 0;@) before FieldDeclaration(@ i = i + 1; @) return int (@ i @) } // Returns the number of abstract data types (i.e. not primitives) // listed as data members of the class. int NAD() via FieldDeclaration to TName { (@ int i; @) init (@ i = 0;@) before TName(@ i = i + 1; @) return int (@ i @) } // Return the total number of parameters in all methods of a class int totalParameters() via MethodDeclarator to FormalParameter { (@ int i; @) init (@ i = 0;@) before FormalParameter (@ i = i + 1; @) return int (@ i @) } int DCC() = DCCTraversal(UniqueVisitor); int DAC() = DACTraversal(UniqueVisitor); // The Direct Class Coupling traversal visits all the class names // that are listed as data members of the class and as parameters // in any method. traversal DCCTraversal(UniqueVisitor) { through {FieldDeclaration, FormalParameter} bypassing {VariableDeclarators, VariableDeclaratorId } to Name; } // The Direct Attribute Coupling traversal visits all the class names // that are listed as attributes (data members) of the class. traversal DACTraversal(UniqueVisitor) { through{FieldDeclaration} bypassing {VariableDeclarators} to Name; } (@ void printMetrics(CompilationUnits system) { System.out.println("There are " + NOM() + " methods. (NOM)"); System.out.println("There are " + CIS() + " public methods. (CIS)"); System.out.println("The OAM is " + (float)CIS()/NOM()); System.out.println("There are " + NOA() + " attributes. (NOA)"); System.out.println("There are " + NPA() + " public attributes. (NPA)"); System.out.println("The DAM is " + (float)(NOA()-NPA())/NOA()); System.out.println("There are " + NAD() + " abstract attributes.(NAD)"); System.out.println("The NPM is " + (float)totalParameters()/NOM()); System.out.println("The total # param are " + totalParameters()); System.out.println("This class is coupled to " + DCC() + " classes. (DCC)"); System.out.println("There are "+DAC()+" different attribute classes. (DAC)"); System.out.println("It has " + NOC() + " children. (NOC)"); System.out.println("It is "+DOI(system)+" deep in an inheritance hierarchy. (DOI)"); System.out.println("It has " + Type1LoD() + " type 1 LoD violations"); System.out.println("It has " + Type2LoD(this) + " type 2 LoD violations"); } @) (@ String getClassName() { return new String(ident.toString()); } @) } // End UCD CompilationUnit { // This method returns the name of the first class in a file. THis // is supposed to be the filename. String printFileName() to UnmodifiedClassDeclaration { (@ boolean firstOne = true; String className;@) before UnmodifiedClassDeclaration (@ if (firstOne) { className = new String(host.get_ident().toString()); firstOne = false; } @) return String (@ className @) } void printFileMetrics(CompilationUnits system) to UnmodifiedClassDeclaration { before UnmodifiedClassDeclaration (@ System.out.println("Metrics for the class: " + host.getClassName()); host.printMetrics(system); @) } } // End CompilationUnit //********************* //***** Visitors ***** //********************* // A visitor used to collect the unique set of Names that // have been traversed. It does this by relying on the fact that // a hashtable will replace previous entries with the same key. UniqueVisitor { init (@ ht = new Hashtable(); @) before Name (@ if (false) System.out.println("**UniqueVisitor is visiting: " + host.convertToString()); ht.put(host.convertToString(), new Integer(1)); @) return int (@ ht.size(); @) } Main { (@ static public void main(String args[]) throws Exception { // Get ready to go! CompilationUnits javaSystem = new CompilationUnits();; CompilationUnit unit; DataInputStream in = new DataInputStream(System.in); String response = null; System.out.print("Please enter the directory name: "); System.out.flush(); try { response = in.readLine(); } catch (IOException ex) {System.out.println("Error getting response"); } // Collect an array of filenames that ends in java. File dir = new File(response); if (dir == null) { System.out.println("Unable to open that directory"); System.exit(0); } FilenameFilter filter = new EndsWith(".java"); String[] files = dir.list(filter); if (files == null) { System.out.println("Sorry no files there"); System.exit(0); } // Parse in each file and add the new object to the javaSystem container. // If we get an error during parsing ingore that file and move on. Don't // forget to add the directory to the filename to get the entire path // based on the users input. for (int i = 0; i < files.length; i ++) { System.out.print("Parsing " + files[i] + "... "); try { StringBuffer entireFilename = new StringBuffer(response + "/" + files[i]); String name = new String(entireFilename); unit = CompilationUnit.parse(new FileInputStream(name)); javaSystem.addElement(unit); System.out.println("done"); } catch(ParseError e) { System.out.println("Syntax problems, skipping"); continue; } catch(NumberFormatException format) { System.out.println("Syntax problems, skipping"); continue; } } javaSystem.updateChildren(javaSystem); javaSystem.printSingleFileMetrics(); javaSystem.printHiearchies(); javaSystem.printSystemMetrics(); } @) } //ends Main