/** * */ package org.openprovenance.prov.sql; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import org.apache.commons.collections.bag.HashBag; import org.apache.log4j.Logger; import org.openprovenance.prov.model.StatementOrBundle; import org.openprovenance.prov.model.Document; /** * @author Trung Dong Huynh <tdh@ecs.soton.ac.uk> * */ public class DocumentEquality { static Logger logger = Logger.getLogger(DocumentEquality.class); private boolean mergeDuplicates; public DocumentEquality() { this.mergeDuplicates = false; } public DocumentEquality(boolean mergeDuplicates) { this.mergeDuplicates = mergeDuplicates; } private boolean collectionEqual(Collection<?> c1, Collection<?> c2) { Collection<?> bag1; Collection<?> bag2; if (mergeDuplicates) { bag1 = new HashSet<Object>(c1); bag2 = new HashSet<Object>(c2); } else { bag1 = new HashBag(c1); bag2 = new HashBag(c2); } return bag1.equals(bag2); } @SuppressWarnings("unchecked") private boolean statementEqual(StatementOrBundle r1, StatementOrBundle r2) { // If one of the statemens is a named bundle if (r1 instanceof Bundle || r2 instanceof Bundle) { if (r1 instanceof Bundle && r2 instanceof Bundle) { Bundle b1 = (Bundle) r1; Bundle b2 = (Bundle) r2; if (!b1.getId().equals(b2.getId())) return false; List<?> stmts1 = b1.getStatement(); List<?> stmts2 = b2.getStatement(); return statementListEqual((List<StatementOrBundle>) stmts1, (List<StatementOrBundle>) stmts2); } } // Two normal statements Class<?> class1 = r1.getClass(); if (class1 != r2.getClass()) { return false; } Method[] allMethods = class1.getDeclaredMethods(); for (Method m : allMethods) { String methodName = m.getName(); if (methodName.startsWith("get") && (!methodName.equals("getAll"))) { try { Object attr1 = m.invoke(r1); Object attr2 = m.invoke(r2); if (attr1 == null && attr2 == null) continue; // Try the standard check first. This will also fail the // test if either attributes is null. if (attr1.equals(attr2)) continue; if (attr1 instanceof Collection<?> && attr1 instanceof Collection<?>) if (collectionEqual((Collection<?>) attr1, (Collection<?>) attr2)) continue; // the two attributes are not equal String attrName = methodName.substring(3); logger.debug("The following " + attrName + " attributes are not the same"); logger.debug(attr1); logger.debug(attr2); return false; } catch (Exception e) { // Any exception means no equality logger.debug(e); return false; } } } return true; } private boolean statementListEqual(List<StatementOrBundle> stmts1, List<StatementOrBundle> stmts2) { if (stmts1.size() != stmts2.size()) { return false; } // Cloning the lists to avoid modification of the originals List<StatementOrBundle> list1 = new ArrayList<StatementOrBundle>(stmts1); List<StatementOrBundle> list2 = new ArrayList<StatementOrBundle>(stmts2); for (StatementOrBundle stmt1 : list1) { // Try to find the same in list2 StatementOrBundle found = null; for (StatementOrBundle stmt2 : list2) { if (statementEqual(stmt1, stmt2)) { found = stmt2; break; } } if (found == null) { logger.debug("Cannot find the following statement in the second document"); logger.debug(stmt1); return false; } list2.remove(found); } return true; } public boolean check(Document d1, Document d2) { return statementListEqual(d1.getStatementOrBundle(), d2.getStatementOrBundle()); } }