package org.simantics.interop.test; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map.Entry; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.Statement; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.utils.datastructures.BijectionMap; public class GraphChanges { private Resource r1; private Resource r2; private List deletions; private List additions; private List modifications; private BijectionMap comparable; public static class Modification { Resource leftSub; Resource rightSub; Statement leftStm; Statement rightStm; int hashCode; public Modification(Resource leftSub, Resource rightSub, Statement leftStm, Statement rightStm) { this.leftSub = leftSub; this.rightSub = rightSub; this.leftStm = leftStm; this.rightStm = rightStm; if (leftSub.getResourceId() < 0) System.out.println(); hashCode = leftSub.hashCode() + rightSub.hashCode(); if (leftStm != null) hashCode += leftStm.hashCode(); if (rightStm != null) hashCode += rightStm.hashCode(); } public boolean isLeftAsserted() { return !leftSub.equals(leftStm.getSubject()); } public boolean isRightAsserted() { return !rightSub.equals(rightStm.getSubject()); } public Resource getLeftSub() { return leftSub; } public void setLeftSub(Resource leftSub) { this.leftSub = leftSub; } public Resource getRightSub() { return rightSub; } public void setRightSub(Resource rightSub) { this.rightSub = rightSub; } public Statement getLeftStm() { return leftStm; } public void setLeftStm(Statement leftStm) { this.leftStm = leftStm; } public Statement getRightStm() { return rightStm; } public void setRightStm(Statement rightStm) { this.rightStm = rightStm; } public Resource getPredicate() { if (leftStm != null) return leftStm.getPredicate(); return rightStm.getPredicate(); } @Override public boolean equals(Object obj) { if (obj.getClass() != this.getClass()) return false; Modification other = (Modification)obj; if (!leftSub.equals(other.leftSub)) return false; if (!rightSub.equals(other.rightSub)) return false; if (leftStm != null) { if (!leftStm.equals(other.leftStm)) return false; } else if (other.leftStm != null) return false; if (rightStm != null) { if (!rightStm.equals(other.rightStm)) return false; } else if (other.rightStm != null) return false; return true; } @Override public int hashCode() { return hashCode; } } public GraphChanges(Resource r1, Resource r2, List deletions, List additions, List modifications, BijectionMap comparable) { super(); this.r1 = r1; this.r2 = r2; this.deletions = deletions; this.additions = additions; this.modifications = modifications; this.comparable = comparable; } public Resource getResource1() { return r1; } public Resource getResource2() { return r2; } public List getAdditions() { return additions; } public List getDeletions() { return deletions; } public List getModifications() { return modifications; } public BijectionMap getComparable() { return comparable; } public String toString(ReadGraph graph) throws DatabaseException { StringBuilder sb = new StringBuilder(); sb.append("Del:\n"); for (Statement stm : deletions) { sb.append(toString(graph, stm)); sb.append("\n"); } sb.append("Add:\n"); for (Statement stm : additions) { sb.append(toString(graph, stm)); sb.append("\n"); } sb.append("Mod:\n"); for (Modification mod :modifications) { sb.append(toString(graph, mod)); sb.append("\n"); } return sb.toString(); } public static String toString(ReadGraph graph, Statement stm) throws DatabaseException{ return NameUtils.getSafeName(graph, stm.getSubject()) + " "+ NameUtils.getSafeName(graph, stm.getPredicate()) + " " + NameUtils.getSafeName(graph, stm.getObject()) + " (" + stm.getSubject() + " " +stm.getPredicate() + " " + stm.getObject(); } public static String toString(ReadGraph graph, Modification mod) throws DatabaseException{ StringBuilder sb = new StringBuilder(); { Statement stm = mod.getLeftStm(); if (stm != null) { sb.append(NameUtils.getSafeName(graph, mod.getLeftSub()) + " "+ NameUtils.getSafeName(graph, stm.getPredicate()) + " " + truncate(NameUtils.getSafeName(graph, stm.getObject())) + " (" + mod.getLeftSub() + " " + stm.getSubject() + " " +stm.getPredicate() + " " + stm.getObject() + ")\n"); } else { sb.append(NameUtils.getSafeName(graph, mod.getLeftSub()) + " "+ mod.getLeftSub() + " N/A\n"); } } { Statement stm = mod.getRightStm(); if (stm != null) { sb.append(NameUtils.getSafeName(graph, mod.getRightSub()) + " "+ NameUtils.getSafeName(graph, stm.getPredicate()) + " " + truncate(NameUtils.getSafeName(graph, stm.getObject())) + " (" + mod.getRightSub() + " " + stm.getSubject() + " " +stm.getPredicate() + " " + stm.getObject() + ")"); } else { sb.append(NameUtils.getSafeName(graph, mod.getRightSub()) + " "+ mod.getRightSub() + " N/A"); } } return sb.toString(); } public static String truncate(String s) { if (s.length() < 100) return s; return s.substring(0, 100)+"..."; } public String comparableToString(ReadGraph graph) throws DatabaseException { StringBuilder sb = new StringBuilder(); sb.append("Comparable:\n"); for (Entry entry : comparable.getEntries()) { sb.append(NameUtils.getSafeName(graph, entry.getKey()) + " "+ NameUtils.getSafeName(graph, entry.getValue()) + " (" + entry.getKey() + " " +entry.getValue() + ")\n"); } return sb.toString(); } /** * Adapts GraphChanges between A1 and B to A2 and B, when A2 is copy of A1. * @param changes changes between A1 and B * @param bijection changes between A1 and A2. Expected to be a bijection, so no additions or removals. * @return * @throws DatabaseException */ public static GraphChanges adapt(Session session, GraphChanges changes, GraphChanges bijection) throws DatabaseException{ if (!changes.getResource1().equals(bijection.getResource1())) throw new DatabaseException("Left side model must be the same"); //if (bijection.getDeletions().size() > 0 || bijection.getAdditions().size() > 0) // throw new DatabaseException("Bijection change is not a bijection, it contains additions and/or removals"); BijectionMap comparable = new BijectionMap(); for (Entry entry : changes.comparable.getEntries()) { if (entry.getKey().equals(entry.getValue())) { comparable.map(entry.getKey(), entry.getValue()); } else { Resource nl = bijection.getComparable().getRight(entry.getKey()); if (nl == null) { // Matching literal resources are not included in bijection map. // TODO : should we check that the resource is literal? // throw new DatabaseException("Did not find matching resource from bijection for " + entry.getKey()); continue; } comparable.map(nl, entry.getValue()); } } List deletions = new ArrayList(changes.getDeletions().size()); // Removals must be adapted from A1 to A2. List additions = new ArrayList(changes.getAdditions()); // Additions can be copied, since they are statements in B. session.syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { for (Statement del : changes.getDeletions()) { Resource s1 = del.getSubject(); Resource s2 = bijection.getComparable().getRight(s1); Resource p1 = del.getPredicate(); Resource p2 = bijection.getComparable().getRight(p1); if (p2 == null) p2 = p1; Resource o1 = del.getObject(); Resource o2 = bijection.getComparable().getRight(o1); if (s2 == null || p2 == null || o2 == null) { throw new DatabaseException("Did not find matching statement from bijection for (" + s1 +","+p1+","+o1+"), got (" + s2 +","+p2+","+o2+")"); } Statement stm2 = null; for (Statement s : graph.getStatements(s2, p2)) { if (s.getObject().equals(o2)) { stm2 = s; break; } } if (stm2 == null) { throw new DatabaseException("Did not find matching statement from bijection for (" + s1 +","+p1+","+o1+"), got (" + s2 +","+p2+","+o2+"), but it is not in DB!"); } deletions.add(stm2); } } }); List modifications = new ArrayList(changes.getModifications().size()); session.syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { for (Modification mod : changes.getModifications()) { Statement del = mod.leftStm; Resource sub1 = mod.getLeftSub(); Resource sub2 = bijection.getComparable().getRight(sub1); if (sub2 == null) { throw new DatabaseException("Did not find matching resource from bijection for " + sub1); } Resource s1 = del.getSubject(); Resource s2 = bijection.getComparable().getRight(s1); Resource p1 = del.getPredicate(); Resource p2 = bijection.getComparable().getRight(p1); if (p2 == null) p2 = p1; Resource o1 = del.getObject(); Resource o2 = bijection.getComparable().getRight(o1); if (mod.isLeftAsserted()) { if (s2 == null) s2 = s1; if (o2 == null) o2 = o1; } if (s2 == null || p2 == null) { throw new DatabaseException("Did not find matching statement from bijection for (" + s1 +","+p1+","+o1+"), got (" + s2 +","+p2+","+o2+")"); } Collection stms = graph.getStatements(s2, p2); Statement stm2 = null; if (o2 == null) { // Matching literal resources are not included in bijection map. if (stms.size() == 1) { stm2 = stms.iterator().next(); } else { throw new DatabaseException("Did not find matching statement from bijection for (" + s1 +","+p1+","+o1+"), got (" + s2 +","+p2+","+o2+")"); } } else { for (Statement s : stms) { if (s.getObject().equals(o2)) { stm2 = s; break; } } } if (stm2 == null) { throw new DatabaseException("Did not find matching statement from bijection for (" + s1 +","+p1+","+o1+"), got (" + s2 +","+p2+","+o2+"), but it is not in DB!"); } Modification mod2 = new Modification(sub2, mod.rightSub, stm2, mod.rightStm); modifications.add(mod2); } } }); return new GraphChanges(bijection.r2, changes.r2, deletions, additions, modifications, comparable); } }