//------------------------------------------------------------------------------ // Copyright (c) 2005, 2006 IBM Corporation and others. // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // which accompanies this distribution, and is available at // http://www.eclipse.org/legal/epl-v10.html // // Contributors: // IBM Corporation - initial implementation //------------------------------------------------------------------------------ package org.eclipse.epf.library.tester; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.epf.importing.services.ResourceScanner; import org.eclipse.epf.library.tester.iface.TestTracer; import org.eclipse.epf.library.util.LibraryUtil; import org.eclipse.epf.uma.Diagram; import org.eclipse.epf.uma.GraphNode; import org.eclipse.epf.uma.MethodElement; import org.eclipse.epf.uma.MethodLibrary; import org.eclipse.epf.uma.MethodPackage; import org.eclipse.epf.uma.ecore.EProperty; /** * Class to analyze the differneces between two method libraries * * @author Weiping Lu * @since 1.0 * */ public class LibraryDiffAnalyzor { private final boolean localDebug = false; private TestTracer tracer; private MethodLibrary lib1; private MethodLibrary lbi2; private boolean greedy = true; private int compareOption = 1; //0: element1 and element2 are on equal footing, //1: element1 is a super set of element2 private boolean needToAnalyze = true; private boolean compareResult = false; private int diffCount = 0; private int elemComparedCount = 0; private static String emptyString = ""; public static final Pattern emptyLine = Pattern.compile("\\s", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$ private static boolean trim = false; private static boolean skipRef = true; private static boolean useNameAs2ndId = false; private static Set skipFeatureSet = new HashSet(); public LibraryDiffAnalyzor(TestTracer t, MethodLibrary l1, MethodLibrary l2) { tracer = t; lib1 = l1; lbi2 = l2; LibraryUtil.loadAll((MethodLibrary) lib1); LibraryUtil.loadAll((MethodLibrary) lbi2); } public static void setTrim(boolean b) { trim = b; } public static void setSkipRef(boolean b) { skipRef = b; } public static void addExcludedFeature(String featureName) { skipFeatureSet.add(featureName); } private void analyze() { if (! needToAnalyze) { return; } compareResult = elementEquals(lib1, lbi2); needToAnalyze = false; } public boolean compare() { analyze(); return compareResult; } public void setGreedy(boolean b) { if (greedy != b) { needToAnalyze = true; } greedy = b; } public void setCompareOption(int ix) { if (compareOption != ix) { needToAnalyze = true; } compareOption = ix; } public static void setUseNameAs2ndId(boolean b) { useNameAs2ndId = b; } public void trace(String line) { if (tracer == null) { return; } tracer.trace(line); if (localDebug) { System.out.println(line); } } //Top entry for comparizon public boolean elementEquals(MethodElement elem1, MethodElement elem2) { diffCount = 0; elemComparedCount = 0; trace(""); trace("elementEquals -> "); trace("elem1: " + elem1); trace("elem2: " + elem2 + "\n"); compare(new ArrayList(), elem1, elem2, new HashMap()); trace("elementEquals <- diffs: " + diffCount + ", elements: " + elemComparedCount + "\n"); return diffCount == 0; } private void compare(List path, MethodElement elem1, MethodElement elem2, Map comparedElemMap) { path.add(elem1); elemComparedCount++; compare_(path, elem1, elem2, comparedElemMap); path.remove(path.size() - 1); } private void compare_(List path, MethodElement elem1, MethodElement elem2, Map comparedElemMap) { if (localDebug) { trace("path: " + pathToString(path)); } boolean topLevel = elem1 == lib1 && elem2 == lbi2; boolean checkEqualSize = compareOption == 0 && !topLevel; List contentList1 = elem1.eContents(); List contentList2 = elem2.eContents(); int sz1 = contentList1.size(); int sz2 = contentList2.size(); if ( checkEqualSize && sz1 != sz2) { String msg = "sz1 != sz2: " + sz1 + " != " + sz2; logWarning(msg, path, elem1, elem2); } int elemCount1 = 0; HashMap elementMap = new HashMap(); HashMap elementNameMap = useNameAs2ndId ? new HashMap() : null; HashMap elementChildMap = useNameAs2ndId ? new HashMap() : null; for (int i=0; i < sz1; i++) { Object obj = contentList1.get(i); if (toCheckObject(obj)) { elemCount1++; String guid = ((MethodElement) obj).getGuid(); String name = ((MethodElement) obj).getName(); if (! comparedElemMap.containsKey(guid)) { elementMap.put(guid, obj); if (useNameAs2ndId) { Object objInMap = elementNameMap.get(name); if (objInMap == null) { elementNameMap.put(name, obj); } else { List list = null; if (objInMap instanceof List) { list = (List) objInMap; } else { list = new ArrayList(); list.add(objInMap); } list.add(obj); } List childList = null; if (obj instanceof MethodPackage) { childList = ((MethodPackage) obj).eContents(); } if (childList != null && ! childList.isEmpty()) { MethodElement ch0 = (MethodElement) childList.get(0); elementChildMap.put(ch0.getGuid(), obj); } } comparedElemMap.put(guid, obj); } } } int elemCount2 = 0; for (int i=0; i < sz2; i++) { Object obj = contentList2.get(i); if (toCheckObject(obj)) { elemCount2++; } } if (elemCount1 < elemCount2 || elemCount1 > elemCount2 && checkEqualSize) { String msg = "elemCount1 != elemCount2: " + elemCount1 + " != " + elemCount2; logDiff(msg, path, elem1, elem2); if (!greedy) { return; } } if (! (elem1 instanceof MethodLibrary)) { List properties = elem1.getInstanceProperties(); for (int i = 0; i < properties.size(); i++) { EProperty ep = (EProperty) properties.get(i); EStructuralFeature feature = ep.getEStructuralFeature(); if (skipFeatureSet.contains(feature.getName())) { continue; } Object val1 = "1 ... ?"; Object val2 = "2 ... ?"; try { val1 = elem1.eGet(feature); val2 = elem2.eGet(feature); } catch (Throwable e) { } if (! featureValueEquals(val1, val2)) { if (feature.getName() != "guid" || !useNameAs2ndId) { String msg = "Diff values in feature: " + feature.getName(); logDiff(msg, path, elem1, elem2, val1, val2); if (!greedy) { return; } } } } } //Compare for contained elements for (int i=0; i < sz2; i++) { Object obj = contentList2.get(i); if (! toCheckObject(obj)) { continue; } MethodElement subElem2 = (MethodElement) obj; MethodElement subElem1 = (MethodElement) elementMap.get(subElem2.getGuid()); if (useNameAs2ndId && subElem1 == null) { Object subObj1 = elementNameMap.get(subElem2.getName()); if (subObj1 instanceof MethodElement) { subElem1 = (MethodElement) subObj1; } else if (subObj1 instanceof List) { logWarning("subObj1 is list: " + subObj1.toString(), path, subElem1, subElem2); } if (subElem1 == null && subElem2 instanceof MethodPackage) { List childList = ((MethodPackage) subElem2).eContents(); if (childList != null && ! childList.isEmpty()) { MethodElement ch0 = (MethodElement) childList.get(0); subElem1 = (MethodElement) elementChildMap.get(ch0.getGuid()); } } } if (subElem1 == null) { logDiff("subElem1 == null" , path, subElem1, subElem2); if (!greedy) { return; } continue; } compare(path, subElem1, subElem2, comparedElemMap); if (diffCount > 0 && !greedy) { return; } } } private boolean featureValueEquals(Object val1, Object val2) { if (val1 == null) { return val2 == null; } if (val2 == null) { return val1 == null; } if (val1 instanceof MethodElement) { if (! (val1 instanceof MethodElement)) { return false; } boolean b = ((MethodElement) val1).getGuid().equals(((MethodElement) val1).getGuid()); if (!b && useNameAs2ndId) { b = ((MethodElement) val1).getName().equals(((MethodElement) val1).getName()); } return b; } int option = 0; //0: not to compare list values //1: compare list values //2: compare size strictly if (option > 0 && val1 instanceof List) { if (! (val2 instanceof List)) { return false; } List l1 = (List) val1; List l2 = (List) val2; if (l1.size() < l2.size()) { return false; } if (option > 1 && l1.size() > l2.size()) { return false; } boolean me = l1.size() == 0 ? false : l1.get(0) instanceof MethodElement; if (me) { Map map = new HashMap(); Map nameMap = useNameAs2ndId ? new HashMap() : null; for (int i=0; i<l1.size(); i++) { MethodElement e1 = (MethodElement) l1.get(i); map.put(e1.getGuid(), e1); if (useNameAs2ndId) { nameMap.put(e1.getName(), e1); } } for (int i=0; i<l2.size(); i++) { MethodElement e2 = (MethodElement) l2.get(i); boolean found = map.containsKey(e2.getGuid()) || useNameAs2ndId && nameMap.containsKey(e2.getName()); if (!found) { return false; } } } else { for (int i=0; i<l1.size(); i++) { if (! (featureValueEquals(l1.get(i), l2.get(i)))) { return false; } } } } if (val1 instanceof String) { if (!(val2 instanceof String)) { return false; } String str1 = (String) val1; String str2 = (String) val2; if (skipRef) { str1 = replaceRefWithDummy(str1); str2 = replaceRefWithDummy(str2); } if (trim) { str1 = str1.trim(); str2 = str2.trim(); if (useNameAs2ndId && str1.length() > 250 && str2.length() > 250) { str1 = str1.substring(0, 250); str2 = str2.substring(0, 250); } } return str1.equals(str2); } return true; } private int incDiffCount() { return ++diffCount; } private String getDiffPrompt() { return "D_" + diffCount + "> "; } private void logWarning(String msg, List path, MethodElement elem1, MethodElement elem2) { log(msg, path, elem1, elem2, false); trace(""); } private void logDiff(String msg, List path, MethodElement elem1, MethodElement elem2) { log(msg, path, elem1, elem2, true); trace(""); } private void log(String msg, List path, MethodElement elem1, MethodElement elem2, boolean diff) { if (diff) { incDiffCount(); } String prompt = diff ? getDiffPrompt() : "Warning> "; trace(prompt + "path: " + pathToString(path)); trace(prompt + "msg: " + msg); MethodElement elem0 = (MethodElement) path.get(path.size() - 1); if (elem0 != elem1) { trace(prompt + getElemString(elem0, "elem0")); } trace(prompt + getElemString(elem1, "elem1")); trace(prompt + getElemString(elem2, "elem2")); } private String getElemString(MethodElement elem, String label) { String str = label; if (elem == null) { str += " -> null"; } else { str += " -> type: " + getClassLastName(elem) + ", name: " + elem.getName() + ", guid: " + elem.getGuid(); } return str; } private String getClassLastName(Object obj) { String str = obj.getClass().getName(); int ix = str.lastIndexOf(".") + 1; return str.substring(ix); } private void logDiff(String msg, List path, MethodElement elem1, MethodElement elem2, Object val1, Object val2) { log(msg, path, elem1, elem2, true); String prompt = getDiffPrompt(); trace(prompt + "val1: " + val1); trace(prompt + "val2: " + val2); trace(""); } private static String pathToString(List path) { StringBuffer buf = new StringBuffer(); for (int i=0; i<path.size(); i++) { MethodElement elem = (MethodElement) path.get(i); if (elem instanceof MethodLibrary) { continue; } if (buf.length() > 0) { buf.append(":"); } buf.append(elem.getName()); } return buf.toString(); } public int getDiffCount() { return diffCount; } public int getElemComparedCount() { return elemComparedCount; } private boolean toCheckObject(Object obj) { if (! (obj instanceof MethodElement)) { return false; } if (useNameAs2ndId && obj instanceof Diagram) { return false; } if (obj instanceof GraphNode) { return false; } return true; } private String replaceRefWithDummy(String source) { String ret = replaceRefWithDummy(source, ResourceScanner.p_src_ref, emptyString); ret = replaceRefWithDummy(ret, ResourceScanner.p_href_ref, emptyString); ret = replaceRefWithDummy(ret, emptyLine, emptyString); if (false && !source.equals(ret)) { trace("LD> source: " + source); trace("LD> ret: " + ret); } return ret; } private String replaceRefWithDummy(String source, Pattern pattern, String replace) { StringBuffer sb = new StringBuffer(); Matcher m = pattern.matcher(source); while (m.find()) { String text = m.group(); m.appendReplacement(sb, replace); } m.appendTail(sb); return sb.toString(); } }