blob: f3a41f80b17c574d51a45bd2bb594f95d2ed8a7b [file] [log] [blame]
//------------------------------------------------------------------------------
// 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();
}
}