package com.openMap1.mapper.util; import java.util.Iterator; import java.util.Hashtable; import java.util.Vector; import java.util.StringTokenizer; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.common.util.EList; /** * Methods in this class compare two EMF model structures to find discrepancies. * Generally the methods a pragmatic rough match of two structures, * so as to point out the most useful discrepancies even if they do not match exactly * * @author robert * */ public class ModelMatcher { static boolean sameClass(EObject obj1, EObject obj2) {return (obj1.eClass().getName().equals(obj2.eClass().getName()));} static int shallowMatch(EObject obj1, EObject obj2) { // of two EObjects are not of the same class, return a huge discrepancy if (!sameClass(obj1,obj2)) return 100; int match = 0; // accumulate discrepancies of EAttribute values for (Iterator<EAttribute> it = obj1.eClass().getEAllAttributes().iterator();it.hasNext();) { EAttribute att = it.next(); if (obj1.eGet(att) == null) { if (obj2.eGet(att) != null) match++; } else { String v1 = stringValue(att,obj1.eGet(att)); String v2 = stringValue(att,obj2.eGet(att)); if (!v1.equals(v2)) match++; } } // accumulate discrepancies of number of eReference targets, by class for (Iterator<EReference> it = obj1.eClass().getEReferences().iterator();it.hasNext();) { EReference ref = it.next(); // find mismatches of target class or existence for any single-valued reference if (ref.getUpperBound() == 1) { if ((obj1.eGet(ref) == null) && (obj2.eGet(ref) != null)) match++; if ((obj1.eGet(ref) != null) && (obj2.eGet(ref) == null)) match++; if ((obj1.eGet(ref) != null) && (obj2.eGet(ref) != null)) { EObject c1 = (EObject)obj1.eGet(ref); EObject c2 = (EObject)obj2.eGet(ref); if (!sameClass(c1,c2)) match++; } } // count target objects of the reference by class for each owing object else if (ref.getUpperBound() == -1) { Hashtable<String,Vector<EObject>> h1 = targetsByClass(obj1,ref); Hashtable<String,Vector<EObject>> h2 = targetsByClass(obj2,ref); match = match + imbalancesByClass(h1,h2); } } return match; } /** * sort the target objects of a multi-valued feature into objects of the same class * @param owner the owning object * @param ref the feature * @return key = class name; value = Vector of target objects of that class */ private static Hashtable<String,Vector<EObject>> targetsByClass(EObject owner, EReference ref) { Hashtable<String,Vector<EObject>> res = new Hashtable<String,Vector<EObject>>(); Object feature = owner.eGet(ref); if (feature instanceof EList<?>) { EList<?> lf = (EList<?>)feature; for (Iterator<?> it = lf.iterator();it.hasNext();) { Object next = it.next(); if (next instanceof EObject) { EObject eo = (EObject)next; String className = eo.eClass().getName(); Vector<EObject> soFar = res.get(className); if (soFar == null) soFar = new Vector<EObject>(); soFar.add(eo); res.put(className, soFar); } } } return res; } /** * count the discrepancies in number of target object by class * @param h1 target objects of a feature in one object, grouped by class * @param h2 target objects of the same feature in another object, grouped by class * @return */ private static int imbalancesByClass(Hashtable<String,Vector<EObject>> h1, Hashtable<String,Vector<EObject>> h2) { int score = 0; // classes present in h1, may be absent from h2 for (Iterator<String> it = h1.keySet().iterator();it.hasNext();) { String className = it.next(); Vector<EObject> o1 = h1.get(className); Vector<EObject> o2 = h2.get(className); if (o2 == null) score = score + o1.size(); // none in h2 if (o1.size() > o2.size()) score = score + o1.size() - o2.size(); if (o2.size() > o1.size()) score = score + o2.size() - o1.size(); } // classes present in h2, absent from h1 for (Iterator<String> it = h2.keySet().iterator();it.hasNext();) { String className = it.next(); Vector<EObject> o2 = h2.get(className); if (h1.get(className) == null) score = score + o2.size(); // none in h1 } return score; } private static String stringValue(EAttribute att,Object value) { String sVal = ""; if (value == null) return sVal; if (att.getEType().getName().equals("EString")) sVal = (String)value; else if (att.getEType().getName().equals("EInt")) sVal = value.toString(); else System.out.println("New data type '" + att.getEType().getName() + "' of attribute '" + att.getName() + "'" ); return sVal; } /* private static int[] bagMatch(EObject obj1, EObject obj2) { // to placate the compiler int[] dummy = new int[1]; dummy[0] = 0; return dummy; } */ class ValueTriple{ String path; // path of class names of objects down from root String attName; // name of the attribute String stringValue; // value of the attribute, converted to a String ValueTriple(String path, EAttribute att, Object value) { this.path = path; this.attName = att.getName(); stringValue = stringValue(att,value); } // key for hashed storage String key() {return (path + "_" + attName + "_" + stringValue);} // depth = length of path -1 int depth() {return (new StringTokenizer(path,"/").countTokens() -1);} } }