package compareAlgorithm;
import java.util.ArrayList;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.language.Soundex;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
/**
* Algorithm class that compares all the features of a class diagram.
*
* @author Shoeb Shaikh
*
*
*/
public class Algo {
private EList<EObject> firstModel; // ECore objects
private EList<EObject> secondModel;
private ECorePackage ePackage1; // Packages
private ECorePackage ePackage2;
private ECoreClass eClass1; // Classes
private ECoreClass eClass2;
private ArrayList<String> matchedClasses; // Records matched classes
private ArrayList<String> comparedClasses; // Records compared classes
//private ArrayList<String> similarClasses;
//private ArrayList<String> aloneClasses;
Report report; // for writing report to the pdf file
/**
* Constructor to initialize necessary class members.
*
* @param firstModel
* first Ecore model to compare
* @param secondModel
* second ECore model to compare
* @param reportFile
* path to store report
*/
public Algo(EList<EObject> firstModel, EList<EObject> secondModel,
String reportFile) {
this.firstModel = firstModel;
this.secondModel = secondModel;
report = new Report(reportFile);
matchedClasses = new ArrayList<String>();
comparedClasses = new ArrayList<String>();
}
/**
* Main dispatch function for comparing the features of a class.
*/
public void compare() {
report.addToReport("Begin Comparison....");
if (comparePackage()) {
eClass1 = new ECoreClass(ePackage1);
eClass2 = new ECoreClass(ePackage2);
// Comparing classes entirely including the details
compareClasses(eClass1, eClass2);
// Reporting unmatch classes
reportUnmatchedClasses();
} else {
report.addToReport("Checking individual classes due to absence of packages");
for (int i = 0; i < firstModel.size(); i++) {
EClass firstClass = (EClass) firstModel.get(i);
for (int j = 0; j < secondModel.size(); j++) {
EClass secondClass = (EClass) secondModel.get(j);
compareUnPackedClasses(firstClass, secondClass);
}
}
// Reporting unmatch classes
reportUnmatchedClasses();
}
//********************************For testing matchedClasses, plz keep, Dong Guo
for(int i = 0; i < matchedClasses.size(); i++){
report.addToReport(i + ": " + matchedClasses.get(i));
}
//**********************************************************
// Close the report
report.finalize();
}
/**
* Checks packages and returns if the packages are similar or not.
*
* @return true if similar else false
*/
private boolean comparePackage() {
// report.startRoutine("packages");
try {
if (checkPackages()) {
ePackage1 = new ECorePackage(firstModel);
ePackage2 = new ECorePackage(secondModel);
if (packageNameSimilar(ePackage1.getName(), ePackage2.getName()) > 0) {
report.addToReport("Packages match : "
+ ePackage1.getName() + " and "
+ ePackage2.getName());
return true;
} else {
report.addToReport("Packages don't match\n"
+ "\nName of first Package : "
+ ePackage1.getName()
+ "\n Name of second Package : "
+ ePackage1.getName());
return false;
}
} else {
return false;
}
} catch (Exception ex) {
// ex.printStackTrace();
} finally {
// report.terminateRoutine("Packages");
}
return false;
}
/**
* Checking if files are in proper format
*
* @return true if files are valid, false otherwise
*/
private boolean checkPackages() {
if (firstModel.get(0) instanceof EPackage) {
if (secondModel.get(0) instanceof EPackage) {
} else {
report.addToReport("Could not find packages in second model");
return false;
}
} else {
report.addToReport("Could not find packages in first model");
return false;
}
return true;
}
/**
* Compares package names.
*
* @param firstName
* first name to compare
* @param secondName
* second name to compare
* @return true if names match else false
*/
private int packageNameSimilar(String firstName, String secondName) {
return this.compareNames(firstName, secondName);
}
/**
* General method for comparing any pair of names.
*
* @param name1
* - first name to compare
* @param name2
* - second name to compare
* @return PERFECT_MATCH if same names
* @return PARTIAL_MATCH if names are similar based on soundex comparison
* @return NOT_MATCH if names not matched at all
*/
private int compareNames(String name1, String name2) {
try {
if (new Soundex().difference(name1, name2) == 4) {
return Constants.PERFECT_MATCH;
}
if (new Soundex().difference(name1, name2) > 2) {
return Constants.PARTIAL_MATCH;
}
} catch (EncoderException e) {
return Constants.NOT_MATCH;
}
return Constants.NOT_MATCH;
}
/**
* Compare package information. If package are the same, compare class
* details. If package are not the same, compare structure details. Adds
* comments to report.
*
* @param class1
* first class to compare
* @param class2
* second class to compare
*/
private void compareUnPackedClasses(EClass class1, EClass class2) {
// report.startRoutine("classes");
try {
// compare class names by soundex
int comparedValue = compareNames(class1.getName(), class2.getName());
if (comparedValue == Constants.PERFECT_MATCH) {
// add classes to the list of matched classes
matchedClasses.add(class1.getName());
matchedClasses.add(class2.getName());
// add to the report
report.addToReport("Perfect Match : " + class1.getName()
+ " : " + class2.getName());
// send the classes for comparing details
compareClassDetails(class1, class2);
} else {
// Pass the two classes for structural class comparison
this.structuralComparison(class1, class2);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Unpacked Classes");
}
}
/**
* Compares Classes. Finds perfect and partial match. Adds comments to
* report object.
*
* @param firstEClass
* first class to compare
* @param secondEClass
* second class to compare
*/
private void compareClasses(ECoreClass firstEClass, ECoreClass secondEClass) {
// report.startRoutine("classes");
try {
for (int x = 0; x < firstEClass.size(); x++) {
// flag for determining whether a class is found
boolean classFound = false;
EClass class1 = firstEClass.getEClass(x);
for (int y = 0; y < secondEClass.size(); y++) {
EClass class2 = secondEClass.getEClass(y);
// compare class names by soundex
int comparedValue = compareNames(class1.getName(),
class2.getName());
if (comparedValue == Constants.PERFECT_MATCH) {
// add classes to the list of matched classes
matchedClasses.add(class1.getName());
matchedClasses.add(class2.getName());
// add to the report
report.addToReport("Perfect Match : "
+ class1.getName() + " : " + class2.getName());
// send the classes for comparing details
compareClassDetails(class1, class2);
// set the flag
classFound = true;
} else {
// Pass the two classes for structural class comparison
this.structuralComparison(class1, class2);
// set the flag
classFound = true;
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Classes");
}
}
/**
* Compare classes details after classes match.
*
* @param cls1
* first class to compare
* @param cls2
* second class to compare
*/
private void compareClassDetails(EClass cls1, EClass cls2) {
compareSuperClass(cls1, cls2);
compareAttributes(cls1, cls2);
compareMethods(cls1, cls2);
compareReferences(cls1, cls2);
/*
* If the classes match each other,
* they will be added into matchedClasses in compareClasses()
* These two lines are duplicated.
*
* Dong Guo 02/17/2013
*/
//matchedClasses.add(cls1.getName());
//matchedClasses.add(cls2.getName());
}
/**
* Compares super classes of designated classes and adds comments to report
* object.
*
* @param cls1
* first class to compare super class
* @param cls2
* second class to compare super class
*/
private void compareSuperClass(EClass cls1, EClass cls2) {
// report.startRoutine("super classes");
try {
// Get super class list from classes
EList<EClass> superClassList1 = cls1.getESuperTypes();
EList<EClass> superClassList2 = cls2.getESuperTypes();
// Select classes one by one and compare
for (int i = 0; i < superClassList1.size(); i++) {
for (int j = 0; j < superClassList2.size(); j++) {
if (compareNames(superClassList1.get(i).getName(),
superClassList2.get(j).getName()) > 0) {
report.addToReport("Super Classes matched : "
+ "first " + superClassList1.get(i).getName()
+ " with " + "second "
+ superClassList2.get(i).getName());
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Super classes");
}
}
/**
* Compare class attributes and adds comments to report object.
*
* @param cls1
* first class containing attribute to compare
* @param cls2
* second class containing attributes to compare
*/
private void compareAttributes(EClass cls1, EClass cls2) {
// report.startRoutine("attributes");
try {
EList<EAttribute> attrList1 = cls1.getEAttributes();
EList<EAttribute> attrList2 = cls2.getEAttributes();
for (int i = 0; i < attrList1.size(); i++) {
// flag set if attribute found
boolean attrFound = false;
for (int j = 0; j < attrList2.size(); j++) {
if (compareNames(attrList1.get(i).getName(),
attrList2.get(j).getName()) > 1) {
// set the flag
attrFound = true;
report.addToReport(" Attributes name matches : "
+ "first " + attrList1.get(i).getName()
+ " with " + "second "
+ attrList2.get(i).getName());
// compare attribute type
if (compareETypes(attrList1.get(i).getEType(),
attrList2.get(j).getEType())) {
report.addToReport(" Attributes type matches : "
+ "first "
+ attrList1.get(j).getEType().getName()
+ " with " + "second "
+ attrList2.get(j).getEType().getName());
}
}
}
// add to report the unmatched attribute
if (!attrFound) {
report.addToReport("Attributes from first that don't match : "
+ attrList1.get(i).getName());
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Attributes");
}
}
/**
* Compare methods by method name and type and adds comments to report
* object.
*
* @param cls1
* first class containing operations to compare
* @param cls2
* second class containing operations to compare
*/
private void compareMethods(EClass cls1, EClass cls2) {
// report.startRoutine("methods");
try {
EList<EOperation> methodList1 = cls1.getEAllOperations();
EList<EOperation> methodList2 = cls2.getEAllOperations();
for (int i = 0; i < methodList1.size(); i++) {
for (int j = 0; j < methodList2.size(); j++) {
if (compareNames(methodList1.get(i).getName(), methodList2
.get(j).getName()) > 0) {
report.addToReport("Methods name matches : " + "first "
+ methodList1.get(i).getName() + " with "
+ "second " + methodList2.get(i).getName());
if (this.compareETypes(methodList1.get(i).getEType(),
methodList2.get(j).getEType())) {
report.addToReport("Methods return type matches : "
+ "first "
+ methodList1.get(i).getEType().getName()
+ " with " + "second "
+ methodList2.get(i).getEType().getName());
}
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Methods");
}
}
/**
* Compare references of a class. Adds comments to report object if
* references match or not.
*
* @param cls1
* first class containing references to compare
* @param cls2
* second class containing references to compare
*/
private void compareReferences(EClass cls1, EClass cls2) {
// report.addToReport("references");
try {
EList<EReference> refList1 = cls1.getEAllReferences();
EList<EReference> refList2 = cls2.getEAllReferences();
for (int i = 0; i < refList1.size(); i++) {
// flag set if references match
boolean refFound = false;
for (int j = 0; j < refList2.size(); j++) {
if (this.compareNames(refList1.get(i).getEType().getName(),
refList2.get(j).getEType().getName()) > 0) {
// add to report
report.addToReport("Reference Name match : "
+ refList1.get(i).getName());
String refName1 = refList1.get(i).getEType().getName()
.toString();
String refName2 = refList2.get(j).getEType().getName()
.toString();
// compare opposite reference
if (compareNames(refName1, refName2) == 2) {
report.addToReport("Opposite Reference match : "
+ refName1);
refFound = true;
}
}
}
if (!refFound) {
report.addToReport("Reference from first diagram "
+ refList1.get(i).getName() + " -->"
+ refList1.get(i).getEType().getName()
+ " not found in second diagram");
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("References");
}
}
/**
* Compare ETypes
*
* @param etype1
* first classifier to compare
* @param etype2
* second classifier to compare
* @return true if equals else false
*/
private boolean compareETypes(EClassifier etype1, EClassifier etype2) {
return etype1.equals(etype2);
}
/**
* Structurally compare the two classes ie reference attribute n methods
*
* @param cls1
* first class to compare
* @param cls2
* second class to compare
*/
private void structuralComparison(EClass cls1, EClass cls2) {
// report.startRoutine("structural class");
try {
// check if any of the class already in list of matched classes
if (!(this.matchedClasses.contains(cls1.getName()) || this.matchedClasses
.contains(cls2.getName()))) {
report.addToReport("Comparing classes.." + cls1.getName()
+ " : " + cls2.getName());
// Check if structurally the similarity is greater than 50 %
if (this.structAttrCompare(cls1, cls2) >= 0.5
&& structMethodCompare(cls1, cls2) >= 0.5
&& structRefCompare(cls1, cls2) >= 0.5) {
report.addToReport("Structural Match " + cls1.getName()
+ " : " + cls2.getName());
/*
* set list of classes with different names but might be same
* Dong Guo
*
* similarClasses.add(cls1.getName());
* similarClasses.add(cls2.getName());
*/
this.compareClassDetails(cls1, cls2);
} else {
// Add the unmatched classes to the list of classes compared
listComparedClasses(cls1, cls2);
}
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Structural class");
}
}
/**
* Compare attributes based on name.
*
* @param cls1
* first class containing attributes to compare
* @param cls2
* second class containing attributes to compare
* @return float between 0 and 1. Percentage of attributes matching.
*/
private float structAttrCompare(EClass cls1, EClass cls2) {
// report.startRoutine("structural attributes");
try {
EList<EAttribute> attrList1 = cls1.getEAttributes();
EList<EAttribute> attrList2 = cls2.getEAttributes();
// If attribute list of both classes is empty then return 1
// This allows comparison based on method and reference similarities
if (attrList1.size() == 0 && attrList2.size() == 0)
return 1;
float attrScore = 0;
for (int i = 0; i < attrList1.size(); i++) {
for (int j = 0; j < attrList2.size(); j++) {
if (compareNames(attrList1.get(i).getName(),
attrList2.get(j).getName()) == 2) {
// debug code
report.addToReport("Attribute match "
+ attrList1.get(i).getName());
attrScore += 1;
}
}
}
// If attribute list is not empty then return, percentage of the
// matched attribute
// In this case a value between 0 and 1
if (attrList1.size() != 0)
return attrScore / attrList1.size();
// Return 0 if attribute list of class 1 is empty and other is not
return attrScore;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Structural attributes");
}
return 0;
}
/**
* Compare methods structurally based on name and return type
*
* @param cls1
* first class containing operations to compare
* @param cls2
* second class containing operations to compare
* @return float between 0 to 1. Percentage of operations matching.
*/
private float structMethodCompare(EClass cls1, EClass cls2) {
// report.startRoutine("structural methods");
try {
EList<EOperation> methodList1 = cls1.getEAllOperations();
EList<EOperation> methodList2 = cls2.getEAllOperations();
if (methodList1.size() == 0 && methodList2.size() == 0)
return 1;
float methodScore = 0;
for (int i = 0; i < methodList1.size(); i++) {
for (int j = 0; j < methodList2.size(); j++) {
// Compare method name
if (this.compareNames(methodList1.get(i).getName(),
methodList2.get(j).getName()) > 0) {
// Compare method return type
if (this.compareETypes(methodList1.get(i).getEType(),
methodList2.get(j).getEType())) {
// method params not compared
report.addToReport("Method match "
+ methodList1.get(i).getName());
methodScore += 1;
}
}
}
}
// Return a percentage of the matched methods
// In this case a decimal value between 0 & 1
if (methodList1.size() != 0)
return methodScore / methodList1.size();
// Return 0 if the method list of class1 is empty and other class is
// not
return methodScore;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Structural methods");
}
return 0;
}
/**
* Compare two classes structure
*
* @param cls1
* first class containing references to compare
* @param cls2
* second class containing references to compare
* @return a float value between 1 and 0. 1 is perfect match and 0
* completely different
*
*/
private float structRefCompare(EClass cls1, EClass cls2) {
// report.startRoutine("structural references");
try {
EList<EReference> refList1 = cls1.getEAllReferences();
EList<EReference> refList2 = cls2.getEAllReferences();
// If both classes do not hav any references return 1
// This allows comparison to be continued on the basis of matched
// attributes and methods
if (refList1.size() == 0 && refList2.size() == 0)
return 1;
float refScore = 0;
for (int i = 0; i < refList1.size(); i++) {
for (int j = 0; j < refList2.size(); j++) {
// Compare if the referenced class is same
if (this.compareNames(refList1.get(i).getEType().getName(),
refList2.get(j).getEType().getName()) > 0) {
String refName1 = refList1.get(i).getEType().getName()
.toString();
String refName2 = refList2.get(j).getEType().getName()
.toString();
// Check if the relationship name is same
if (this.compareNames(refName1, refName2) == 2) {
report.addToReport("Reference match "
+ refList1.get(i).getName());
refScore += 1;
}
}
}
}
// Return a percentage of the matched references
if (refList1.size() != 0)
return refScore / refList1.size();
return refScore;
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// report.terminateRoutine("Structural references");
}
return 0;
}
/**
* Add classes to the list of compared classes
*
* @param cls1
* first class that was compared
* @param cls2
* second class that was compared
*/
private void listComparedClasses(EClass cls1, EClass cls2) {
if (!comparedClasses.contains(cls1.getName()))
comparedClasses.add(cls1.getName());
if (!comparedClasses.contains(cls2.getName()))
comparedClasses.add(cls2.getName());
}
/**
* Add to report the list of unmatched classes by seperating the classes
* already present in matched classes
*/
private void reportUnmatchedClasses() {
for (int i = 0; i < comparedClasses.size(); i++) {
if (!matchedClasses.contains(comparedClasses.get(i))) {
report.addToReport("Unmatched Classes : "
+ comparedClasses.get(i));
}
}
}
/*
* The aloneClasses list that might be useful
*
* private void setAloneClasses(){
* for(int i = 0; i < comparedClasses.size(); i++) {
* if(!matchedClasses.contains(comparedClasses.get(i))
* && !similarClasses.contains(comparedClasses.get(i))){
* aloneClasses.add(comparedClasses.get(i));
* }
* }
* }
*/
}