All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openprovenance.prov.validation.Merger Maven / Gradle / Ivy

The newest version!
package org.openprovenance.prov.validation;

import java.util.HashSet;
import java.util.List;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Set;

import javax.xml.datatype.XMLGregorianCalendar;

import org.openprovenance.prov.model.Activity;
import org.openprovenance.prov.model.Identifiable;
import org.openprovenance.prov.model.QualifiedName;
import org.openprovenance.prov.model.Statement;
import org.openprovenance.prov.model.StatementOrBundle.Kind;
import org.openprovenance.prov.model.Type;
import org.openprovenance.prov.model.WasEndedBy;
import org.openprovenance.prov.model.WasStartedBy;
import org.openprovenance.prov.model.ProvFactory;
import org.openprovenance.prov.model.ProvUtilities;
import org.openprovenance.prov.model.exception.InvalidCaseException;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
 * Class merging two statements of the same type. Can't this be streamlined and
 * can avoid code repetition?
 *
 * problem is newElement needs static type of its argument!
 */

public class Merger {

	static Logger logger = LogManager.getLogger(Merger.class);
	final private ProvUtilities u;
	@SuppressWarnings("unused")
	final private ProvFactory p;
	final public Hashtable> qualifiedNameMismatch;

	final private Indexer indexer;

	public Merger(ProvFactory p, ProvUtilities u,
				  Hashtable> qualifiedNameMismatch,
				  Indexer indexer) {
		this.u = u;
		this.p = p;
		this.qualifiedNameMismatch = qualifiedNameMismatch;
		this.indexer = indexer;
	}

	public  boolean checkSameQualifiedName(QualifiedName fromArgument,
																QualifiedName toArgument,
																T from,
																T to) {
		logger.debug("        checkSameQualifiedName(): QNames are  "
				+ fromArgument + " " + toArgument);
		String uri = toArgument.getUri();
		String uri2 = fromArgument.getUri();
		if (uri.equals(uri2)) {
			if (fromArgument.getLocalPart().equals(toArgument.getLocalPart())
					&& fromArgument.getNamespaceURI()
					.equals(toArgument.getNamespaceURI())
					&& fromArgument.getPrefix().equals(toArgument.getPrefix()))
				return true;
			List l = qualifiedNameMismatch.get(uri);
			if (l == null) {
				l = new LinkedList<>();
				l.add(to);
				qualifiedNameMismatch.put(uri, l);
			}
			l.add(from);
			return false;
		}
		return true;
	}

	QualifiedName getRef(Statement o, int i)  {
		return (QualifiedName) u.getter(o, i);
	}

	XMLGregorianCalendar getTime(Statement o, int i) {
		return (XMLGregorianCalendar) u.getter(o, i);
	}

	public  T merge(T from, T to) {
		return mergeGenericUnwrapped(from, to);

	}


	static   public int getFirstTimeIndex(Statement s)  {
		final Kind kind = s.getKind();
		switch (kind) {
			case PROV_ACTIVITY: return 1;
			case PROV_AGENT: return 1;
			case PROV_ALTERNATE: return 2;
			case PROV_ASSOCIATION: return 4;
			case PROV_ATTRIBUTION: return 3;
			case PROV_BUNDLE:
				throw new InvalidCaseException("ProvUtilities.getFirstTimeIndex() for " + kind);
			case PROV_COMMUNICATION: return 3;
			case PROV_DELEGATION: return 4;
			case PROV_DERIVATION: return 6;
			case PROV_DICTIONARY_INSERTION:
				throw new InvalidCaseException("ProvUtilities.getFirstTimeIndex() for " + kind);
			case PROV_DICTIONARY_MEMBERSHIP:
				throw new InvalidCaseException("ProvUtilities.getFirstTimeIndex() for " + kind);
			case PROV_DICTIONARY_REMOVAL:
				throw new InvalidCaseException("ProvUtilities.getFirstTimeIndex() for " + kind);
			case PROV_END: return 4;
			case PROV_ENTITY: return 1;
			case PROV_GENERATION: return 3;
			case PROV_INFLUENCE: return 3;
			case PROV_INVALIDATION: return 3;
			case PROV_MEMBERSHIP: return 2;
			case PROV_MENTION: return 3;
			case PROV_SPECIALIZATION: return 2;
			case PROV_START: return 4;
			case PROV_USAGE: return 3;
			default:
				throw new InvalidCaseException("ProvUtilities.getFirstTimeIndex() for " + kind);
		}
	}



	static   public int getLastIndex(Statement s)  {
		final Kind kind = s.getKind();
		switch (kind) {
			case PROV_ACTIVITY: return 3;
			case PROV_AGENT: return 1;
			case PROV_ALTERNATE: return 2;
			case PROV_ASSOCIATION: return 4;
			case PROV_ATTRIBUTION: return 3;
			case PROV_BUNDLE:
				throw new InvalidCaseException("ProvUtilities.getLastIndex() for " + kind);
			case PROV_COMMUNICATION: return 3;
			case PROV_DELEGATION: return 4;
			case PROV_DERIVATION: return 6;
			case PROV_DICTIONARY_INSERTION:
				throw new InvalidCaseException("ProvUtilities.getLastIndex() for " + kind);
			case PROV_DICTIONARY_MEMBERSHIP:
				throw new InvalidCaseException("ProvUtilities.getLastIndex() for " + kind);
			case PROV_DICTIONARY_REMOVAL:
				throw new InvalidCaseException("ProvUtilities.getLastIndex() for " + kind);
			case PROV_END: return 5;
			case PROV_ENTITY: return 1;
			case PROV_GENERATION: return 4;
			case PROV_INFLUENCE: return 3;
			case PROV_INVALIDATION: return 4;
			case PROV_MEMBERSHIP: return 2;
			case PROV_MENTION: return 3;
			case PROV_SPECIALIZATION: return 2;
			case PROV_START: return 5;
			case PROV_USAGE: return 4;
			default:
				throw new InvalidCaseException("ProvUtilities.getLastIndex() for " + kind);
		}
	}



	private  T mergeGenericUnwrapped(T from, T to) {


		QualifiedName toId=null;
		QualifiedName fromId=null;

		// checking Id (argument 0)
		if (from instanceof Identifiable) {
			toId = (QualifiedName) u.getter(to, 0);
			fromId = (QualifiedName) u.getter(from, 0);

			logger.debug("     mergeGenericUnwrapped():  " + fromId + " " + toId);

			if (!unify(fromId, toId)) {
				logger.debug("$$$$$$$$$$$$$$$$$$$$ mergeGenericUnwrapped failedtoUnify  "
						+ toId + " " + toId);
				return null;
			}

			checkSameQualifiedName(fromId, toId, from, to);
		}

		boolean success = true;




		for (int i = getFirstTimeIndex(from); i <= getLastIndex(from) - 1; i++) {
			XMLGregorianCalendar fromTime = getTime(from, i);
			XMLGregorianCalendar toTime = getTime(to, i);
			success = success && unify(fromTime, toTime);
		}

		for (int i = 1; i < getFirstTimeIndex(from); i++) {
			QualifiedName fromArgument = getRef(from, i);
			if (fromArgument != null) {
				QualifiedName toArgument = getRef(to, i);
				if (toArgument == null) {
					// u.setter(to, i, fromArgument);
					throw new NullPointerException(
							"toArgument should never be null");
				} else {
					if (fromArgument.getUri()
							.equals(toArgument.getUri())) {
						checkSameQualifiedName(fromArgument,
								toArgument, from, to);
					} else {
						if (!unify(fromArgument, toArgument)) {
							logger.debug("$$$$$$$$$$$$$$$$$$$$ mergeGenericUnwrapped failed Unification  ");
							return null;
						}
					}
				}
			}
		}


		if (from instanceof Identifiable) {
			mergeTypeAttributes(from, to, toId, fromId);
		}

		if (success) {
			logger.debug("     mergeGenericUnwrapped(): successful merge  "
					+ unificationSubstitution);
			return to;
		}
		return null;
	}

	public boolean unify(XMLGregorianCalendar fromTime,
						 XMLGregorianCalendar toTime)
			throws NullPointerException {
		boolean success=true;
		if (fromTime == null) {
			throw new NullPointerException("fromArgument should never be null");
		} else {

			if (toTime == null) {
				// u.setter(to, i, fromTime); //TODO no setter
				throw new NullPointerException("toArgument should never be null");
			} else {
				if (fromTime instanceof VarTime) {
					VarTime vFromTime = (VarTime) fromTime;
					if (!unify(vFromTime, toTime)) {
						logger.debug("$$$$$$$$$$$$$$$$$$$$ mergeGenericUnwrapped failedtoUnify  "
								+ vFromTime + " " + toTime);
						success = false;
					}
				} else {
					if (toTime instanceof VarTime) {
						VarTime vToTime = (VarTime) toTime;
						if (!unify(vToTime, fromTime)) {
							logger.debug("$$$$$$$$$$$$$$$$$$$$ mergeGenericUnwrapped failedtoUnify 2 "
									+ vToTime + " " + fromTime);
							success = false;
						}
					} else {
						// we are sure we are dealing with a time constant
						if (fromTime.equals(toTime)) {
						} else {
							logger.debug("$$$$$$$$$$$$$$$$$$$$ mergeGenericUnwrapped incompatible time  ");
							success = false;
						}
					}
				}
			}
		}
		return success;
	}

	private  void mergeTypeAttributes(T from, T to,
														   QualifiedName toId, QualifiedName fromId) {
		logger.debug("        mergeAttributes " + toId + " " + fromId);
		String toUri = toId.getUri();
		String fromUri = fromId.getUri();
		Set toTypes=indexer.getTypeTable(to).get(toUri);
		if (toTypes==null) {
			toTypes=new HashSet();
			indexer.getTypeTable(to).put(toUri,toTypes);
		}
		Set fromTypes=indexer.getTypeTable(from).get(fromUri);
		if (fromTypes==null) {
			fromTypes=new HashSet();
			indexer.getTypeTable(from).put(fromUri,fromTypes);
		}

		if (!(toTypes.equals(fromTypes))) {
			Set toCopy=new HashSet(toTypes);
			toTypes.addAll(fromTypes);
			fromTypes.addAll(toCopy);
		}
	}

	Hashtable unificationSubstitution = new Hashtable();
	boolean updated = false;

	void unificationSubstitutionPut(VarQName key, QualifiedName val) {
		QualifiedName old = unificationSubstitution.put(new VarQNameWrapper(key), val);
		if ((old == null) || (!(equalQName(old, val)))) {
			updated = true;
		}
	}

	void unificationSubstitutionPut(VarQNameWrapper key, QualifiedName val) {
		QualifiedName old = unificationSubstitution.put(key, val);
		if ((old == null) || (!(equalQName(old, val)))) {
			updated = true;
		}
	}

	private void unificationSubstitutionPut(VarTime key,
											XMLGregorianCalendar val) {
		XMLGregorianCalendar old = unificationTimeSubstitution.put(key, val);
		if ((old == null) || (!(old.equals(val))))
			updated = true;

	}

	public void updateSubstitutionTable(VarQName id1, QualifiedName id2) {
		for (VarQNameWrapper key : unificationSubstitution.keySet()) {
			QualifiedName value = unificationSubstitution.get(key);
			if (equalQName(id1, value)) {
				unificationSubstitutionPut(key, id2);
			}
		}
	}

	private void updateSubstitutionTable(VarTime time1,
										 XMLGregorianCalendar time2) {
		for (VarTime key : unificationTimeSubstitution.keySet()) {
			XMLGregorianCalendar value = unificationTimeSubstitution.get(key);
			if (equalTime(value, time1)) {
				unificationSubstitutionPut(key, time2);
			}
		}
	}

	Hashtable unificationTimeSubstitution = new Hashtable();

	private boolean unify(VarTime time1, XMLGregorianCalendar time2) {
		logger.debug("        unify(): unification for VarTime  " + time1 + " "
				+ time2);

		XMLGregorianCalendar entry1 = unificationTimeSubstitution.get(time1);
		XMLGregorianCalendar entry2 = (time2 instanceof VarTime) ? unificationTimeSubstitution.get((VarTime) time2)
				: null;

		XMLGregorianCalendar unifiedTime1 = (entry1 == null) ? time1 : entry1;
		XMLGregorianCalendar unifiedTime2 = (entry2 == null) ? time2 : entry2;

		if (equalTime(unifiedTime1, unifiedTime2)) {
			logger.debug("        unify()time: SAME " + unifiedTime1);
			return true;
		}

		if (unifiedTime1 instanceof VarTime) {
			unificationSubstitutionPut((VarTime) unifiedTime1, unifiedTime2);
			updateSubstitutionTable((VarTime) unifiedTime1, unifiedTime2);
			logger.debug("        unify()time: " + unificationTimeSubstitution);
			return true;
		} else {

			if (unifiedTime2 instanceof VarTime) {
				unificationSubstitutionPut((VarTime) unifiedTime2, unifiedTime1);
				updateSubstitutionTable((VarTime) unifiedTime2, unifiedTime1);
				logger.debug("        unify()time: " + unificationTimeSubstitution);
				return true;
			} else {
				logger.debug("        unify()time: UNIFICATION FAILURE "
						+ unificationTimeSubstitution);
				return false;
			}
		}
	}


	public void sameStartTime(String activityURI, Activity activity, WasStartedBy start) {
		logger.debug("      sameStartTime >>> " + activityURI + " " + start.getId());
		boolean flag=unify(activity.getStartTime(),start.getTime());
		if (!flag) {
			logger.debug("      failed to unify startTime ");
			indexer.addToFailedMerge(activity, start, activityURI);
		}
	}

	public void sameEndTime(String activityURI, Activity activity, WasEndedBy end) {
		logger.debug("      sameEndTime >>> " + activityURI + " " + end.getId());
		boolean flag=unify(activity.getEndTime(),end.getTime());
		if (!flag) {
			logger.debug("      failed to unify endTime ");
			indexer.addToFailedMerge(activity, end, activityURI);
		}
	}


	/**
	 * Method to work around a bug of
	 * com.sun.org.apache.xerces.internal.jaxp.datatype
	 * .XMLGregorianCalendarImpl.equals()
	 */
	boolean equalTime(XMLGregorianCalendar time1,
					  XMLGregorianCalendar time2) {
		if ((time1 instanceof VarTime) && (time2 instanceof VarTime))
			return time1.equals(time2);
		if ((time1 instanceof VarTime) || (time2 instanceof VarTime))
			return false;
		return time1.equals(time2);
	}

	boolean equalQName(QualifiedName qn1, QualifiedName qn2) {
		if ((qn1 instanceof VarQName) && (qn2 instanceof VarQName))
			return ((VarQName) qn1).getUri().equals(((VarQName) qn2).getUri());
		if ((qn1 instanceof VarQName) || (qn2 instanceof VarQName))
			return false;
		if (qn1.equals(qn2)) return true;
		return qn1.getUri().equals(qn2.getUri());
	}

	private boolean unify(QualifiedName id1, QualifiedName id2) {
		logger.debug("        unify(): unification for QNames  " + id1 + " "
				+ id2);

		QualifiedName entry1 = (id1 instanceof VarQName) ? unificationSubstitution.get((VarQName) id1)
				: null;
		QualifiedName entry2 = (id2 instanceof VarQName) ? unificationSubstitution.get((VarQName) id2)
				: null;

		QualifiedName unified_uri1 = (entry1 == null) ? id1 : entry1;
		QualifiedName unified_uri2 = (entry2 == null) ? id2 : entry2;

		if (indexer.existentialVariable(unified_uri1)) {
			VarQName vqn1 = (VarQName) unified_uri1;
			unificationSubstitutionPut(vqn1, unified_uri2);
			updateSubstitutionTable(vqn1, unified_uri2);
			logger.debug("        unify(): " + unificationSubstitution);
			return true;
		} else if (indexer.existentialVariable(unified_uri2)) {
			VarQName vqn2 = (VarQName) unified_uri2;
			unificationSubstitutionPut(vqn2, unified_uri1);
			updateSubstitutionTable(vqn2, unified_uri1);
			logger.debug("        unify(): " + unificationSubstitution);
			return true;
		} else if (equalQName(unified_uri1, unified_uri2)) {
			logger.debug("        unify(): SAME " + unified_uri1);
			return true;
		} else {
			logger.debug("        unify(): UNIFICATION FAILURE "
					+ unificationSubstitution);
			return false;
		}
	}

	public QualifiedName substitute(QualifiedName id, Hashtable subst) {
		if (id instanceof VarQName) {
			QualifiedName res = subst.get(new VarQNameWrapper((VarQName) id));
			return res;
		} else {
			return null;
		}
	}

	public XMLGregorianCalendar substitute(XMLGregorianCalendar id,
										   Hashtable subst) {
		XMLGregorianCalendar res = subst.get(id);
		if (res != null) {
			logger.debug("SUBSTITUTE got " + res + ") for " + id);
			return res;
		} else {
			return null;
		}
	}

	public  T applySubstitution(T from,
													 Hashtable table,
													 Hashtable timeTable) {
		try {
			return applySubstitutionUnwrapped(from, table, timeTable);
		} catch (java.lang.NoSuchMethodException nsme) {
			nsme.printStackTrace();
		} catch (java.lang.IllegalAccessException iae) {
			iae.printStackTrace();
		} catch (java.lang.reflect.InvocationTargetException ite) {
			ite.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
		logger.debug("!!! applySubstitution PROBLEM: " + " " + from);
		throw new UnsupportedOperationException();
	}

	public  T applySubstitutionUnwrapped(T anEntry,
															  Hashtable table,
															  Hashtable timeTable)
			throws java.lang.NoSuchMethodException,
			java.lang.IllegalAccessException,
			java.lang.reflect.InvocationTargetException,
			InstantiationException,
			IllegalArgumentException {

		// checking Id (argument 0)
		QualifiedName anEntryId = (QualifiedName) u.getter(anEntry, 0);
		logger.debug("     applySubstitutionUnwrapped():  to " + anEntryId);

		QualifiedName isNew = substitute(anEntryId, table);
		if (isNew != null)
			u.setter(anEntry, 0, isNew);

		for (int i = getFirstTimeIndex(anEntry); i <= getLastIndex(anEntry) - 1; i++) {
			logger.debug("%%%%%% " + anEntry.getClass() + " " + i + " "
					+ (getLastIndex(anEntry) - 1));
			XMLGregorianCalendar anEntryTime = getTime(anEntry, i);
			XMLGregorianCalendar isNewTime = substitute(anEntryTime, timeTable);
			if (isNewTime != null) {
				u.setter(anEntry, i, isNewTime);
			} else {
				if (anEntryTime != null) {
					if (anEntryTime instanceof VarTime) { // TODO, should I
						// reset the variable?
						// logger.debug("$$$$$$$$$$$$$$$$$$$$ applySubstitutionUnwrapped resetting variable time  ");
						// u.setter(anEntry,i,null);
					}
				}
			}
		}

		for (int i = 1; i < getFirstTimeIndex(anEntry); i++) {
			QualifiedName anEntryArgument = getRef(anEntry, i);
			if (anEntryArgument != null) {

				QualifiedName anEntryId2 = anEntryArgument;
				QualifiedName isNew2 = substitute(anEntryId2, table);

				if (isNew2 != null) {
					//Class cl = u.getTypes().get(anEntry.getClass())[i];
					//Constructor cons = cl.getConstructor();
					//Ref r = (Ref) cons.newInstance();
					//TODO: used to be dynmic type
					//Ref r= new org.openprovenance.prov.org.openprovenance.prov.xml.IDRef();
					//r.setRef();

					//Was: 		    Ref r= p.newIDRef(isNew2);
					u.setter(anEntry, i, isNew2);
				}
			}
		}

		return anEntry;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy