| //------------------------------------------------------------------------------ |
| // 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.emf.ecore.sdo.EProperty; |
| 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; |
| |
| /** |
| * 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(); |
| } |
| |
| } |