View Javadoc
1   package de.tud.plt.r43ples.core;
2   
3   import org.apache.jena.query.*;
4   import org.apache.jena.rdf.model.Model;
5   import org.apache.jena.util.FileUtils;
6   import de.tud.plt.r43ples.exception.InternalErrorException;
7   import de.tud.plt.r43ples.existentobjects.Branch;
8   import de.tud.plt.r43ples.existentobjects.ChangeSet;
9   import de.tud.plt.r43ples.existentobjects.Revision;
10  import de.tud.plt.r43ples.existentobjects.ThreeWayMergeCommit;
11  import de.tud.plt.r43ples.iohelper.Helper;
12  import de.tud.plt.r43ples.iohelper.JenaModelManagement;
13  import de.tud.plt.r43ples.management.Config;
14  import de.tud.plt.r43ples.optimization.ChangeSetPath;
15  import de.tud.plt.r43ples.triplestoreInterface.TripleStoreInterfaceSingleton;
16  import org.apache.logging.log4j.LogManager;
17  import org.apache.logging.log4j.Logger;
18  
19  /**
20   * Collection of information for creating a new three way merge commit.
21   *
22   * @author Stephan Hensel
23   */
24  public class ThreeWayMergeCommitDraft extends MergeCommitDraft {
25  
26      /** The logger. **/
27      private Logger logger = LogManager.getLogger(ThreeWayMergeCommitDraft.class);
28  
29      /** The used source branch. **/
30      private Branch usedSourceBranch;
31      /** The used target branch. **/
32      private Branch usedTargetBranch;
33  
34  
35      /**
36       * The constructor.
37       * Creates a three way merge commit draft by using the corresponding meta information.
38       *
39       * @param graphName the graph name
40       * @param branchNameFrom the branch name (from)
41       * @param branchNameInto the branch name (into)
42       * @param user the user
43       * @param message the message
44       * @param triples the triples of the query WITH part
45       * @param type the query type (FORCE, AUTO, MANUAL)
46       * @param with states if the WITH part is available
47       * @throws InternalErrorException
48       */
49      protected ThreeWayMergeCommitDraft(String graphName, String branchNameFrom, String branchNameInto, String user, String message, String triples, MergeTypes type, boolean with) throws InternalErrorException {
50          super(graphName, branchNameFrom, branchNameInto, user, message, MergeActions.MERGE, triples, type, with);
51          this.usedSourceBranch = getRevisionGraph().getBranch(getBranchNameFrom(), true);
52          this.usedTargetBranch = getRevisionGraph().getBranch(getBranchNameInto(), true);
53      }
54  
55      /**
56       * Tries to create a new commit draft as a new commit in the triple store.
57       * If possible it will create the corresponding revision and the meta data.
58       *
59       * @return the commit (has attribute which indicates if the commit was executed or not)
60       */
61      protected ThreeWayMergeCommit createCommitInTripleStore() throws InternalErrorException {
62  
63          Revision fromRevision = this.usedSourceBranch.getLeafRevision();
64  
65          Revision intoRevision = this.usedTargetBranch.getLeafRevision();
66  
67          // Get the default SDG URI of the revision graph
68          String usedSDDURI = getRevisionGraph().getSDG();
69  
70          // Get the common revision with shortest path
71          Revision commonRevision = this.getPathCalculationInterface().getCommonRevisionWithShortestPath(fromRevision, intoRevision);
72  
73          // Create the revision progress for from and into
74          String namedGraphUriFrom = getUriCalculator().getTemporaryRevisionProgressFromURI(getRevisionGraph());
75          String namedGraphUriInto = getUriCalculator().getTemporaryRevisionProgressIntoURI(getRevisionGraph());
76          String namedGraphUriDiff = getUriCalculator().getTemporaryDifferenceModelURI(getRevisionGraph());
77          String uriA = "http://eatld.et.tu-dresden.de/branch-from";
78          String uriB = "http://eatld.et.tu-dresden.de/branch-into";
79  
80          ChangeSetPath changesetsFromCommontoSourceBranch = this.getPathCalculationInterface().getChangeSetsBetweenStartAndTargetRevision(commonRevision, fromRevision);
81          ChangeSetPath changesetsFromCommontoTargetBranch = this.getPathCalculationInterface().getChangeSetsBetweenStartAndTargetRevision(commonRevision, intoRevision);
82  
83  
84          createRevisionProgresses(
85                  changesetsFromCommontoSourceBranch, namedGraphUriFrom, uriA,
86                  changesetsFromCommontoTargetBranch, namedGraphUriInto, uriB,
87                  commonRevision);
88  
89          // Create difference model
90          createDifferenceTripleModel(namedGraphUriDiff, namedGraphUriFrom, uriA, namedGraphUriInto, uriB,
91                  usedSDDURI);
92  
93          // The created revision
94          Revision revision;
95  
96          // Differ between the different merge queries
97          if ((getType() != null) && (getType().equals(MergeTypes.AUTO)) && !isWith()) {
98              logger.debug("AUTO MERGE query detected");
99              // Create the merged revision
100             revision = createMergedRevision(namedGraphUriDiff, MergeQueryTypeEnum.AUTO, fromRevision);
101             return addMetaInformation(revision, namedGraphUriDiff, commonRevision, fromRevision, intoRevision);
102         } else if ((getType() != null) && (getType().equals(MergeTypes.MANUAL)) && isWith()) {
103             logger.debug("MANUAL MERGE query detected");
104             // Create the merged revision
105             revision = createMergedRevision(namedGraphUriDiff, MergeQueryTypeEnum.MANUAL, fromRevision);
106             return addMetaInformation(revision, namedGraphUriDiff, commonRevision, fromRevision, intoRevision);
107         } else if ((getType() == null) && isWith()) {
108             logger.debug("MERGE WITH query detected");
109             // Create the merged revision
110             revision = createMergedRevision(namedGraphUriDiff, MergeQueryTypeEnum.WITH, fromRevision);
111             return addMetaInformation(revision, namedGraphUriDiff, commonRevision, fromRevision, intoRevision);
112         } else if ((getType() == null) && !isWith()) {
113             logger.debug("MERGE query detected");
114             // Check if difference model contains conflicts
115             String queryASK = String.format("ASK { %n" + "	GRAPH <%s> { %n"
116                     + " 	?ref <http://eatld.et.tu-dresden.de/mmo#isConflicting> \"true\"^^<http://www.w3.org/2001/XMLSchema#boolean> . %n"
117                     + "	} %n" + "}", namedGraphUriDiff);
118             if (getTripleStoreInterface().executeAskQuery(queryASK)) {
119                 // Difference model contains conflicts
120                 // Return the conflict model to the client
121                 String conflictModel = Helper.getContentOfGraph(namedGraphUriDiff, "text/turtle");
122                 return new ThreeWayMergeCommit(getRevisionGraph(), null,null, null, null, fromRevision, null, intoRevision, null, null, null, true, conflictModel, namedGraphUriDiff);
123             } else {
124                 // Difference model contains no conflicts
125                 // Create the merged revision
126                 revision = createMergedRevision(namedGraphUriDiff, MergeQueryTypeEnum.COMMON, fromRevision);
127                 return addMetaInformation(revision, namedGraphUriDiff, commonRevision, fromRevision, intoRevision);
128             }
129         } else {
130             throw new InternalErrorException("This is not a valid MERGE query");
131         }
132     }
133 
134     /**
135      * Adds meta information for commit and revision to the revision graph.
136      *
137      * <img src="{@docRoot}../../doc/revision management description/r43ples-threewaymerge.png" />
138      *
139      * @param generatedRevision the generated revision
140      * @param namedGraphUriDiff the named graph URI of the difference model
141      * @param commonRevision the common revision of this merge
142      * @param usedSourceRevision the used source revision (from)
143      * @param usedTargetRevision the used target revision (into)
144      * @return the created commit
145      * @throws InternalErrorException
146      */
147     private ThreeWayMergeCommit addMetaInformation(Revision generatedRevision, String namedGraphUriDiff, Revision commonRevision, Revision usedSourceRevision, Revision usedTargetRevision) throws InternalErrorException {
148 
149         String commitURI = getUriCalculator().getNewThreeWayMergeCommitURI(getRevisionGraph(), generatedRevision.getRevisionIdentifier());;
150 
151         String personUri = Helper.getUserURI(getUser());
152 
153         // Create a new commit (activity)
154         StringBuilder queryContent = new StringBuilder(1000);
155         queryContent.append(String.format(
156                 "<%s> a rmo:ThreeWayMergeCommit, rmo:MergeCommit, rmo:BasicMergeCommit, rmo:Commit; "
157                         + "	rmo:wasAssociatedWith <%s> ;"
158                         + "	rmo:commitMessage \"%s\" ;"
159                         + "	rmo:timeStamp \"%s\"^^xsd:dateTime ; %n"
160                         + " rmo:generated <%s> ;"
161                         + " rmo:hasChangeSet <%s> ;"
162                         + " rmo:hasChangeSet <%s> ;"
163                         + " rmo:usedSourceRevision <%s> ;"
164                         + " rmo:usedSourceBranch <%s> ;"
165                         + " rmo:usedTargetRevision <%s> ;"
166                         + " rmo:usedTargetBranch <%s> .",
167                 commitURI, personUri, getMessage(), getTimeStamp(),
168                 generatedRevision.getRevisionURI(), generatedRevision.getChangeSets().get(0).getChangeSetURI(), generatedRevision.getChangeSets().get(1).getChangeSetURI(), usedSourceRevision.getRevisionURI(), usedSourceBranch.getReferenceURI(),
169                 usedTargetRevision.getRevisionURI(), usedTargetBranch.getReferenceURI()));
170 
171         // Create revision meta data for additional change set
172         queryContent.append(String.format(
173                   "<%1$s> rmo:succeedingRevision <%2$s> . %n"
174                 + "<%2$s> rmo:wasDerivedFrom <%3$s> .",
175                 generatedRevision.getChangeSets().get(1).getChangeSetURI(), generatedRevision.getRevisionURI(), usedSourceRevision.getRevisionURI()));
176 
177         String query = Config.prefixes
178                 + String.format("INSERT DATA { GRAPH <%s> { %s } }", getRevisionGraph().getRevisionGraphUri(),
179                 queryContent.toString());
180 
181         getTripleStoreInterface().executeUpdateQuery(query);
182 
183         // Move branch to new revision
184         moveBranchReference(getRevisionGraph().getRevisionGraphUri(), usedTargetBranch.getReferenceURI(), usedTargetRevision.getRevisionURI(), generatedRevision.getRevisionURI());
185         updateReferencedFullGraph(usedTargetBranch.getFullGraphURI(), generatedRevision.getChangeSets().get(0));
186         // Update the target branch object
187         usedTargetBranch = getRevisionGraph().getBranch(getBranchNameInto(), true);
188 
189         return new ThreeWayMergeCommit(getRevisionGraph(), commitURI, getUser(), getTimeStamp(), getMessage(),
190                 usedSourceRevision, usedSourceBranch, usedTargetRevision, usedTargetBranch, generatedRevision,
191                 commonRevision, false, null, namedGraphUriDiff);
192     }
193 
194     /**
195      * Create a merged revision with meta data
196      *
197      * @param graphNameDifferenceTripleModel the graph name of the difference triple model
198      * @param type the merge query type
199      * @param usedSourceRevision the used source revision
200      * @return the created revision
201      * @throws InternalErrorException
202      */
203     private Revision createMergedRevision(String graphNameDifferenceTripleModel, MergeQueryTypeEnum type, Revision usedSourceRevision) throws InternalErrorException {
204 
205         // Create an empty temporary graph which will contain the merged full content
206         String graphNameOfMerged = getUriCalculator().getTemporaryMergedURI(getRevisionGraph());
207         getTripleStoreInterface().executeUpdateQuery(String.format("DROP SILENT GRAPH <%s>", graphNameOfMerged));
208         getTripleStoreInterface().executeCreateGraph(graphNameOfMerged);
209 
210         // Get the full graph name of branch A
211         String graphNameOfBranchA = usedSourceBranch.getFullGraphURI();
212         // Get the full graph name of branch B
213         String graphNameOfBranchB = usedTargetBranch.getFullGraphURI();
214 
215         if (type.equals(MergeQueryTypeEnum.MANUAL)) {
216             // Manual merge query
217             Helper.executeINSERT(graphNameOfMerged, getTriples());
218         } else {
219             // Copy graph B to temporary merged graph
220             String queryCopy = String.format("COPY <%s> TO <%s>", graphNameOfBranchB, graphNameOfMerged);
221             getTripleStoreInterface().executeUpdateQuery(queryCopy);
222 
223             // Get the triples from branch A which should be added to/deleted from the merged revision
224             String triplesToAdd = "";
225             String triplesToDelete = "";
226 
227             // Get all difference groups
228             String queryDifferenceGroup = Config.prefixes + String.format(
229                     "SELECT ?differenceCombinationURI ?automaticResolutionState ?tripleStateA ?tripleStateB ?conflict %n"
230                             + "WHERE { GRAPH <%s> { %n"
231                             + "	?differenceCombinationURI a mmo:DifferenceGroup ; %n"
232                             + "		mmo:automaticResolutionState ?automaticResolutionState ; %n"
233                             + "		mmo:hasTripleStateA ?tripleStateA ; %n"
234                             + "		mmo:hasTripleStateB ?tripleStateB ; %n"
235                             + "		mmo:isConflicting ?conflict . %n"
236                             + "} }", graphNameDifferenceTripleModel);
237 
238             // Iterate over all difference groups
239             ResultSet resultSetDifferenceGroups = getTripleStoreInterface().executeSelectQuery(queryDifferenceGroup);
240             while (resultSetDifferenceGroups.hasNext()) {
241                 QuerySolution qsCurrentDifferenceGroup = resultSetDifferenceGroups.next();
242 
243                 String currentDifferencGroupURI = qsCurrentDifferenceGroup.getResource("?differenceCombinationURI").toString();
244                 String currentDifferencGroupAutomaticResolutionState = qsCurrentDifferenceGroup.getResource("?automaticResolutionState").toString();
245 //				Currently not needed
246 //				String currentDifferencGroupTripleStateA = qsCurrentDifferenceGroup.getResource("?tripleStateA").toString();
247 //				String currentDifferencGroupTripleStateB = qsCurrentDifferenceGroup.getResource("?tripleStateB").toString();
248                 boolean currentDifferencGroupConflict = qsCurrentDifferenceGroup.getLiteral("?conflict").getBoolean();
249 
250                 // Get all differences (triples) of current difference group
251                 String queryDifference = Config.prefixes + String.format(
252                         "SELECT ?s ?p ?o %n"
253                                 + "WHERE { GRAPH <%s> { %n"
254                                 + "	<%s> a mmo:DifferenceGroup ; %n"
255                                 + "		mmo:hasDifference ?blankDifference . %n"
256                                 + "	?blankDifference a mmo:Difference ; %n"
257                                 + "		mmo:hasTriple ?triple . %n"
258                                 + "	?triple rdf:subject ?s . %n"
259                                 + "	?triple rdf:predicate ?p . %n"
260                                 + "	?triple rdf:object ?o . %n"
261                                 + "} }", graphNameDifferenceTripleModel, currentDifferencGroupURI);
262 
263                 // Iterate over all differences (triples)
264                 ResultSet resultSetDifferences = getTripleStoreInterface().executeSelectQuery(queryDifference);
265                 while (resultSetDifferences.hasNext()) {
266                     QuerySolution qsCurrentDifference = resultSetDifferences.next();
267 
268                     String subject = "<" + qsCurrentDifference.getResource("?s").toString() + ">";
269                     String predicate = "<" + qsCurrentDifference.getResource("?p").toString() + ">";
270 
271                     // Differ between literal and resource
272                     String object;
273                     if (qsCurrentDifference.get("?o").isLiteral()) {
274                         object = "\"" + qsCurrentDifference.getLiteral("?o").toString() + "\"";
275                     } else {
276                         object = "<" + qsCurrentDifference.getResource("?o").toString() + ">";
277                     }
278 
279                     if (	type.equals(MergeQueryTypeEnum.AUTO) ||
280                             type.equals(MergeQueryTypeEnum.COMMON) ||
281                             (type.equals(MergeQueryTypeEnum.WITH) && !currentDifferencGroupConflict) ) {
282                         // MERGE AUTO or common MERGE query
283                         if (currentDifferencGroupAutomaticResolutionState.equals(MergeTripleStateEnum.ADDED.getSdRepresentation())) {
284                             // Triple should be added
285                             triplesToAdd += subject + " " + predicate + " " + object + " . \n";
286                         } else {
287                             // Triple should be deleted
288                             triplesToDelete += subject + " " + predicate + " " + object + " . \n";
289                         }
290                     } else {
291                         // MERGE WITH query - conflicting triple
292                         Model model = JenaModelManagement.readNTripleStringToJenaModel(getTriples());
293                         // Create ASK query which will check if the model contains the specified triple
294                         String queryAsk = String.format(
295                                 "ASK { %n"
296                                         + " %s %s %s %n"
297                                         + "}", subject, predicate, object);
298                         Query query = QueryFactory.create(queryAsk);
299                         QueryExecution qe = QueryExecutionFactory.create(query, model);
300                         boolean resultAsk = qe.execAsk();
301                         qe.close();
302                         model.close();
303                         if (resultAsk) {
304                             // Model contains the specified triple
305                             // Triple should be added
306                             triplesToAdd += subject + " " + predicate + " " + object + " . \n";
307                         } else {
308                             // Triple should be deleted
309                             triplesToDelete += subject + " " + predicate + " " + object + " . \n";
310                         }
311                     }
312                 }
313                 // Update the merged graph
314                 // Insert triplesToAdd
315                 Helper.executeINSERT(graphNameOfMerged, triplesToAdd);
316                 // Delete triplesToDelete
317                 Helper.executeDELETE(graphNameOfMerged, triplesToDelete);
318             }
319         }
320 
321         // Calculate the add and delete sets
322 
323         // Get all added triples for both changesets
324         String queryAddedTriplesA = String.format(
325                 "CONSTRUCT {?s ?p ?o} %n"
326                         + "WHERE { %n"
327                         + "	GRAPH <%s> { ?s ?p ?o } %n"
328                         + "	FILTER NOT EXISTS { "
329                         + "		GRAPH <%s> { ?s ?p ?o } %n"
330                         + "	} %n"
331                         + "}", graphNameOfMerged, graphNameOfBranchA);
332 
333         String addedTriplesA = getTripleStoreInterface().executeConstructQuery(queryAddedTriplesA, FileUtils.langNTriple);
334 
335         String queryAddedTriplesB = String.format(
336                 "CONSTRUCT {?s ?p ?o} %n"
337                         + "WHERE { %n"
338                         + "	GRAPH <%s> { ?s ?p ?o } %n"
339                         + "	FILTER NOT EXISTS { %n"
340                         + "		GRAPH <%s> { ?s ?p ?o } %n"
341                         + "	} %n"
342                         + "}", graphNameOfMerged, graphNameOfBranchB);
343 
344         String addedTriplesB = getTripleStoreInterface().executeConstructQuery(queryAddedTriplesB, FileUtils.langNTriple);
345 
346         // Get all deleted triples for both changesets
347         String queryDeletedTriplesA = String.format(
348                 "CONSTRUCT {?s ?p ?o} %n"
349                         + "WHERE { %n"
350                         + "	GRAPH <%s> { ?s ?p ?o } %n"
351                         + "	FILTER NOT EXISTS { %n"
352                         + "		GRAPH <%s> { ?s ?p ?o } %n"
353                         + "	} %n"
354                         + "}", graphNameOfBranchA, graphNameOfMerged);
355 
356         String deletedTriplesA = getTripleStoreInterface().executeConstructQuery(queryDeletedTriplesA, FileUtils.langNTriple);
357 
358         String queryDeletedTriplesB = String.format(
359                 "CONSTRUCT {?s ?p ?o} %n"
360                         + "WHERE { %n"
361                         + "	GRAPH <%s> { ?s ?p ?o } %n"
362                         + "	FILTER NOT EXISTS { %n"
363                         + "		GRAPH <%s> { ?s ?p ?o } %n"
364                         + "	} %n"
365                         + "}", graphNameOfBranchB, graphNameOfMerged);
366 
367         String deletedTriplesB = getTripleStoreInterface().executeConstructQuery(queryDeletedTriplesB, FileUtils.langNTriple);
368 
369         // Create the merge revision and a change set
370         RevisionDraft revisionDraft = new RevisionDraft(getUriCalculator(), getRevisionGraph(), usedTargetBranch,
371                 addedTriplesB, deletedTriplesB, false);
372         Revision generatedRevision = revisionDraft.createInTripleStore();
373 
374         ChangeSetDraft changeSetDraftA = new ChangeSetDraft(getUriCalculator(), getRevisionGraph(),
375                 usedSourceBranch.getLeafRevision(), generatedRevision.getRevisionIdentifier(), generatedRevision.getRevisionURI(), usedSourceBranch.getReferenceURI(),
376                 addedTriplesA, deletedTriplesA, false, false);
377         ChangeSet changeSetA = changeSetDraftA.createInTripleStore();
378 
379         generatedRevision.addChangeSet(changeSetA);
380         return generatedRevision;
381     }
382 
383     /**
384      * Create the revision progresses for both branches.
385      *
386      * @param pathFrom the path with all revisions from start revision to target revision of the from branch
387      * @param graphNameRevisionProgressFrom the graph name of the revision progress of the from branch
388      * @param uriFrom the URI of the revision progress of the from branch
389      * @param pathInto the linked list with all revisions from start revision to target revision of the into branch
390      * @param graphNameRevisionProgressInto the graph name of the revision progress of the into branch
391      * @param uriInto the URI of the revision progress of the into branch
392      * @throws InternalErrorException
393      */
394     protected void createRevisionProgresses(ChangeSetPath pathFrom, String graphNameRevisionProgressFrom, String uriFrom,
395                                             ChangeSetPath pathInto, String graphNameRevisionProgressInto, String uriInto,
396                                             Revision commonRevision) throws InternalErrorException {
397         logger.info("Create the revision progress of branch from and into.");
398 
399         if (!((pathFrom.getRevisionPath().size() > 0) && (pathInto.getRevisionPath().size() > 0))) {
400             throw new InternalErrorException("Revision path contains no revisions.");
401         }
402 
403         // Get the full graph name of common revision or create full revision graph of common revision
404         FullGraph fullGraph = new FullGraph(this.getRevisionGraph(), commonRevision);
405 
406         // Create revision progress of branch from
407         createRevisionProgress(fullGraph, pathFrom, graphNameRevisionProgressFrom, uriFrom);
408 
409         // Create revision progress of branch into
410         createRevisionProgress(fullGraph, pathInto, graphNameRevisionProgressInto, uriInto);
411 
412         // Drop the temporary full graph
413         fullGraph.purge();
414     }
415 
416     /**
417      * Create the revision progress.
418      *
419      * @param startingFullGraph the full graph name of the revision of path
420      * @param path the path with all revisions from start revision to target revision
421      * @param graphNameRevisionProgress the graph name where the revision progress should be stored
422      * @param uri the URI where the revision progress should be stored
423      * @throws InternalErrorException
424      */
425     protected void createRevisionProgress(FullGraph startingFullGraph, ChangeSetPath path, String graphNameRevisionProgress, String uri) throws InternalErrorException {
426         logger.info("Create the revision progress of " + uri + " in graph " + graphNameRevisionProgress + ".");
427 
428         TripleStoreInterfaceSingleton.get().executeUpdateQuery(String.format("DROP SILENT GRAPH <%s>", graphNameRevisionProgress));
429         TripleStoreInterfaceSingleton.get().executeUpdateQuery(String.format("CREATE GRAPH  <%s>", graphNameRevisionProgress));
430 
431         // Create the initial content
432         logger.info("Create the initial content.");
433         String queryInitial = Config.prefixes + String.format(
434                 "INSERT { GRAPH <%s> { %n"
435                         + "	<%s> a mmo:RevisionProgress; %n"
436                         + "		mmo:Original [ %n"
437                         + "			rdf:subject ?s ; %n"
438                         + "			rdf:predicate ?p ; %n"
439                         + "			rdf:object ?o ; %n"
440                         + "			rmo:references <%s> %n"
441                         + "		] %n"
442                         + "} } WHERE { %n"
443                         + "	GRAPH <%s> %n"
444                         + "		{ ?s ?p ?o . } %n"
445                         + "}", graphNameRevisionProgress, uri, startingFullGraph.getRevision().getRevisionURI(), startingFullGraph.getFullGraphUri());
446 
447         // Execute the query which generates the initial content
448         TripleStoreInterfaceSingleton.get().executeUpdateQuery(queryInitial);
449 
450         // Update content by current add and delete set - remove old entries
451         while (path.getRevisionPath().size() > 0) {
452             ChangeSet changeSet = path.getRevisionPath().removeLast();
453             logger.info("Update content by current add and delete set of changeset " + changeSet + " - remove old entries.");
454             // Get the ADD and DELETE set URIs
455             String addSetURI = changeSet.getAddSetURI();
456             String deleteSetURI = changeSet.getDeleteSetURI();
457 
458             if ((addSetURI != null) && (deleteSetURI != null)) {
459 
460                 // Update the revision progress with the data of the current revision ADD set
461 
462                 // Delete old entries (original)
463                 String queryRevision = Config.prefixes + String.format(
464                         "DELETE { GRAPH <%s> { %n"
465                                 + "	<%s> mmo:Original ?blank . %n"
466                                 + "	?blank rdf:subject ?s . %n"
467                                 + "	?blank rdf:predicate ?p . %n"
468                                 + "	?blank rdf:object ?o . %n"
469                                 + "	?blank rmo:references ?revision . %n"
470                                 + "} } %n"
471                                 + "WHERE { "
472                                 + "		GRAPH <%s> { %n"
473                                 + "			<%s> mmo:Original ?blank . %n"
474                                 + "			?blank rdf:subject ?s . %n"
475                                 + "			?blank rdf:predicate ?p . %n"
476                                 + "			?blank rdf:object ?o . %n"
477                                 + "			?blank rmo:references ?revision . %n"
478                                 + "		} %n"
479                                 + "		GRAPH <%s> { %n"
480                                 + "			?s ?p ?o %n"
481                                 + "		} %n"
482                                 + "};", graphNameRevisionProgress, uri, graphNameRevisionProgress, uri, addSetURI);
483 
484                 queryRevision += "\n";
485 
486                 // Delete old entries (added)
487                 queryRevision += String.format(
488                         "DELETE { GRAPH <%s> { %n"
489                                 + "	<%s> mmo:Added ?blank . %n"
490                                 + "	?blank rdf:subject ?s . %n"
491                                 + "	?blank rdf:predicate ?p . %n"
492                                 + "	?blank rdf:object ?o . %n"
493                                 + "	?blank rmo:references ?revision . %n"
494                                 + "} } %n"
495                                 + "WHERE { "
496                                 + "		GRAPH <%s> { %n"
497                                 + "			<%s> mmo:Added ?blank . %n"
498                                 + "			?blank rdf:subject ?s . %n"
499                                 + "			?blank rdf:predicate ?p . %n"
500                                 + "			?blank rdf:object ?o . %n"
501                                 + "			?blank rmo:references ?revision . %n"
502                                 + "		} %n"
503                                 + "		GRAPH <%s> { %n"
504                                 + "			?s ?p ?o %n"
505                                 + "		} %n"
506                                 + "};", graphNameRevisionProgress, uri, graphNameRevisionProgress, uri, addSetURI);
507 
508                 queryRevision += "\n";
509 
510                 // Delete old entries (deleted)
511                 queryRevision += String.format(
512                         "DELETE { GRAPH <%s> { %n"
513                                 + "	<%s> mmo:Deleted ?blank . %n"
514                                 + "	?blank rdf:subject ?s . %n"
515                                 + "	?blank rdf:predicate ?p . %n"
516                                 + "	?blank rdf:object ?o . %n"
517                                 + "	?blank rmo:references ?revision . %n"
518                                 + "} } %n"
519                                 + "WHERE { "
520                                 + "		GRAPH <%s> { %n"
521                                 + "			<%s> mmo:Deleted ?blank . %n"
522                                 + "			?blank rdf:subject ?s . %n"
523                                 + "			?blank rdf:predicate ?p . %n"
524                                 + "			?blank rdf:object ?o . %n"
525                                 + "			?blank rmo:references ?revision . %n"
526                                 + "		} %n"
527                                 + "		GRAPH <%s> { %n"
528                                 + "			?s ?p ?o %n"
529                                 + "		} %n"
530                                 + "};", graphNameRevisionProgress, uri, graphNameRevisionProgress, uri, addSetURI);
531 
532                 queryRevision += "\n";
533 
534                 // Insert new entries (added)
535                 queryRevision += String.format(
536                         "INSERT { GRAPH <%s> {%n"
537                                 + "	<%s> a mmo:RevisionProgress; %n"
538                                 + "		mmo:Added [ %n"
539                                 + "			rdf:subject ?s ; %n"
540                                 + "			rdf:predicate ?p ; %n"
541                                 + "			rdf:object ?o ; %n"
542                                 + "			rmo:references <%s> %n"
543                                 + "		] %n"
544                                 + "} } WHERE { %n"
545                                 + "	GRAPH <%s> %n"
546                                 + "		{ ?s ?p ?o . } %n"
547                                 + "};", graphNameRevisionProgress, uri, changeSet.getChangeSetURI(), addSetURI);
548 
549                 queryRevision += "\n \n";
550 
551                 // Update the revision progress with the data of the current revision DELETE set
552 
553                 // Delete old entries (original)
554                 queryRevision += String.format(
555                         "DELETE { GRAPH <%s> { %n"
556                                 + "	<%s> mmo:Original ?blank . %n"
557                                 + "	?blank rdf:subject ?s . %n"
558                                 + "	?blank rdf:predicate ?p . %n"
559                                 + "	?blank rdf:object ?o . %n"
560                                 + "	?blank rmo:references ?revision . %n"
561                                 + "} } %n"
562                                 + "WHERE { "
563                                 + "		GRAPH <%s> { %n"
564                                 + "			<%s> mmo:Original ?blank . %n"
565                                 + "			?blank rdf:subject ?s . %n"
566                                 + "			?blank rdf:predicate ?p . %n"
567                                 + "			?blank rdf:object ?o . %n"
568                                 + "			?blank rmo:references ?revision . %n"
569                                 + "		} %n"
570                                 + "		GRAPH <%s> { %n"
571                                 + "			?s ?p ?o %n"
572                                 + "		} %n"
573                                 + "};", graphNameRevisionProgress, uri, graphNameRevisionProgress, uri, deleteSetURI);
574 
575                 queryRevision += "\n";
576 
577                 // Delete old entries (added)
578                 queryRevision += String.format(
579                         "DELETE { GRAPH <%s> { %n"
580                                 + "	<%s> mmo:Added ?blank . %n"
581                                 + "	?blank rdf:subject ?s . %n"
582                                 + "	?blank rdf:predicate ?p . %n"
583                                 + "	?blank rdf:object ?o . %n"
584                                 + "	?blank rmo:references ?revision . %n"
585                                 + "} } %n"
586                                 + "WHERE { "
587                                 + "		GRAPH <%s> { %n"
588                                 + "			<%s> mmo:Added ?blank . %n"
589                                 + "			?blank rdf:subject ?s . %n"
590                                 + "			?blank rdf:predicate ?p . %n"
591                                 + "			?blank rdf:object ?o . %n"
592                                 + "			?blank rmo:references ?revision . %n"
593                                 + "		} %n"
594                                 + "		GRAPH <%s> { %n"
595                                 + "			?s ?p ?o %n"
596                                 + "		} %n"
597                                 + "};", graphNameRevisionProgress, uri, graphNameRevisionProgress, uri, deleteSetURI);
598 
599                 queryRevision += "\n";
600 
601                 // Delete old entries (deleted)
602                 queryRevision += String.format(
603                         "DELETE { GRAPH <%s> { %n"
604                                 + "	<%s> mmo:Deleted ?blank . %n"
605                                 + "	?blank rdf:subject ?s . %n"
606                                 + "	?blank rdf:predicate ?p . %n"
607                                 + "	?blank rdf:object ?o . %n"
608                                 + "	?blank rmo:references ?revision . %n"
609                                 + "} } %n"
610                                 + "WHERE { "
611                                 + "		GRAPH <%s> { %n"
612                                 + "			<%s> mmo:Deleted ?blank . %n"
613                                 + "			?blank rdf:subject ?s . %n"
614                                 + "			?blank rdf:predicate ?p . %n"
615                                 + "			?blank rdf:object ?o . %n"
616                                 + "			?blank rmo:references ?revision . %n"
617                                 + "		} %n"
618                                 + "		GRAPH <%s> { %n"
619                                 + "			?s ?p ?o %n"
620                                 + "		} %n"
621                                 + "};", graphNameRevisionProgress, uri, graphNameRevisionProgress, uri, deleteSetURI);
622 
623                 queryRevision += "\n";
624 
625                 // Insert new entries (deleted)
626                 queryRevision += String.format(
627                         "INSERT { GRAPH <%s> { %n"
628                                 + "	<%s> a mmo:RevisionProgress; %n"
629                                 + "		mmo:Deleted [ %n"
630                                 + "			rdf:subject ?s ; %n"
631                                 + "			rdf:predicate ?p ; %n"
632                                 + "			rdf:object ?o ; %n"
633                                 + "			rmo:references <%s> %n"
634                                 + "		] %n"
635                                 + "} } WHERE { %n"
636                                 + "	GRAPH <%s> %n"
637                                 + "		{ ?s ?p ?o . } %n"
638                                 + "}", graphNameRevisionProgress, uri, changeSet.getChangeSetURI(), deleteSetURI);
639 
640                 // Execute the query which updates the revision progress by the current revision
641                 TripleStoreInterfaceSingleton.get().executeUpdateQuery(queryRevision);
642 
643             } else {
644                 //TODO Error management - is needed when a ADD or DELETE set is not referenced in the current implementation this error should not occur
645                 logger.error("ADD or DELETE set of " + changeSet.getChangeSetURI() + "does not exists.");
646             }
647             logger.info("Revision progress was created.");
648         }
649     }
650 
651     /**
652      * Create the difference triple model which contains all differing triples and store it in named graph
653      *
654      * @param graphNameDifferenceTripleModel the graph name where the generated difference triple model should be stored
655      * @param graphNameRevisionProgressA the graph name of the revision progress of branch A
656      * @param uriA the URI of the revision progress of branch A
657      * @param graphNameRevisionProgressB the graph name of the revision progress of branch B
658      * @param uriB the URI of the revision progress of branch B
659      * @param uriSDG the URI of the SDG to use
660      */
661     private void createDifferenceTripleModel(String graphNameDifferenceTripleModel, String graphNameRevisionProgressA, String uriA, String graphNameRevisionProgressB, String uriB, String uriSDG) {
662 
663         logger.info("Create the difference triple model");
664         TripleStoreInterfaceSingleton.get().executeUpdateQuery(String.format("DROP SILENT GRAPH <%s>", graphNameDifferenceTripleModel));
665         TripleStoreInterfaceSingleton.get().executeUpdateQuery(String.format("CREATE GRAPH  <%s>", graphNameDifferenceTripleModel));
666 
667         // Templates for revision A and B
668         String sparqlTemplateRevisionA = String.format(
669                 "	GRAPH <%s> { %n"
670                         + "		<%s> <%s> ?blankA . %n"
671                         + "			?blankA rdf:subject ?s . %n"
672                         + "			?blankA rdf:predicate ?p . %n"
673                         + "			?blankA rdf:object ?o . %n"
674                         + "			?blankA rmo:references ?revisionA . %n"
675                         + "	} %n", graphNameRevisionProgressA, uriA, "%s");
676         String sparqlTemplateRevisionB = String.format(
677                 "	GRAPH <%s> { %n"
678                         + "		<%s> <%s> ?blankB . %n"
679                         + "			?blankB rdf:subject ?s . %n"
680                         + "			?blankB rdf:predicate ?p . %n"
681                         + "			?blankB rdf:object ?o . %n"
682                         + "			?blankB rmo:references ?revisionB . %n"
683                         + "	} %n", graphNameRevisionProgressB, uriB, "%s");
684 
685         String sparqlTemplateNotExistsRevisionA = String.format(
686                 "FILTER NOT EXISTS { %n"
687                         + "	GRAPH <%s> { %n"
688                         + "		<%s> ?everything ?blankA . %n"
689                         + "			?blankA rdf:subject ?s . %n"
690                         + "			?blankA rdf:predicate ?p . %n"
691                         + "			?blankA rdf:object ?o . %n"
692                         + "			?blankA rmo:references ?revisionA . %n"
693                         + "	} %n"
694                         + "}", graphNameRevisionProgressA, uriA);
695 
696         String sparqlTemplateNotExistsRevisionB = String.format(
697                 "FILTER NOT EXISTS { %n"
698                         + "	GRAPH <%s> { %n"
699                         + "		<%s> ?everything ?blankB . %n"
700                         + "			?blankB rdf:subject ?s . %n"
701                         + "			?blankB rdf:predicate ?p . %n"
702                         + "			?blankB rdf:object ?o . %n"
703                         + "			?blankB rmo:references ?revisionB . %n"
704                         + "	} %n"
705                         + "}", graphNameRevisionProgressB, uriB);
706 
707         // Get all structural definitions which are generating differences
708         String queryDifferingSD = String.format(
709                 "PREFIX mmo: <http://eatld.et.tu-dresden.de/mmo#> %n"
710                         + "PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#> %n"
711                         + "SELECT ?combinationURI ?tripleStateA ?tripleStateB ?conflict ?automaticResolutionState %n"
712                         + "WHERE { GRAPH <%s> { %n"
713                         + "	<%s> a mmo:StructuralDefinitionGroup ;"
714                         + "		mmo:hasStructuralDefinition ?combinationURI ."
715                         + "	?combinationURI a mmo:StructuralDefinition ; %n"
716                         + "		mmo:hasTripleStateA ?tripleStateA ; %n"
717                         + "		mmo:hasTripleStateB ?tripleStateB ; %n"
718                         + "		mmo:isConflicting ?conflict ; %n"
719                         + "		mmo:automaticResolutionState ?automaticResolutionState . %n"
720                         + "} } %n", Config.sdg_graph, uriSDG);
721 
722         // Iterate over all differing combination URIs
723         ResultSet resultSetDifferences = TripleStoreInterfaceSingleton.get().executeSelectQuery(queryDifferingSD);
724         while (resultSetDifferences.hasNext()) {
725             QuerySolution qs = resultSetDifferences.next();
726 
727             String currentDifferenceCombinationURI = qs.getResource("?combinationURI").toString();
728             String currentTripleStateA = qs.getResource("?tripleStateA").toString();
729             String currentTripleStateB = qs.getResource("?tripleStateB").toString();
730             // Will return an integer value because virtuoso stores boolean internal as integer
731             String currentConflictState = qs.getLiteral("?conflict").toString();
732             // TDB returns boolean value without "" -> add it to use it in the next query correctly
733             if (currentConflictState.equals("true^^http://www.w3.org/2001/XMLSchema#boolean")) {
734                 currentConflictState = "\"true\"^^<http://www.w3.org/2001/XMLSchema#boolean>";
735             } else {
736                 currentConflictState = "\"false\"^^<http://www.w3.org/2001/XMLSchema#boolean>";
737             }
738             String currentAutomaticResolutionState = qs.getResource("?automaticResolutionState").toString();
739 
740             String querySelectPart = "SELECT ?s ?p ?o %s %s %n";
741             String sparqlQueryRevisionA = null;
742             String sparqlQueryRevisionB = null;
743 
744             // A
745             if (currentTripleStateA.equals(MergeTripleStateEnum.ADDED.getSdRepresentation())) {
746                 // In revision A the triple was added
747                 querySelectPart = String.format(querySelectPart, "?revisionA", "%s");
748                 sparqlQueryRevisionA = String.format(sparqlTemplateRevisionA, MergeTripleStateEnum.ADDED.getDgRepresentation());
749             } else if (currentTripleStateA.equals(MergeTripleStateEnum.DELETED.getSdRepresentation())) {
750                 // In revision A the triple was deleted
751                 querySelectPart = String.format(querySelectPart, "?revisionA", "%s");
752                 sparqlQueryRevisionA = String.format(sparqlTemplateRevisionA, MergeTripleStateEnum.DELETED.getDgRepresentation());
753             } else if (currentTripleStateA.equals(MergeTripleStateEnum.ORIGINAL.getSdRepresentation())) {
754                 // In revision A the triple is original
755                 querySelectPart = String.format(querySelectPart, "?revisionA", "%s");
756                 sparqlQueryRevisionA = String.format(sparqlTemplateRevisionA, MergeTripleStateEnum.ORIGINAL.getDgRepresentation());
757             } else if (currentTripleStateA.equals(MergeTripleStateEnum.NOTINCLUDED.getSdRepresentation())) {
758                 // In revision A the triple is not included
759                 querySelectPart = String.format(querySelectPart, "", "%s");
760                 sparqlQueryRevisionA = sparqlTemplateNotExistsRevisionA;
761             }
762 
763             // B
764             if (currentTripleStateB.equals(MergeTripleStateEnum.ADDED.getSdRepresentation())) {
765                 // In revision B the triple was added
766                 querySelectPart = String.format(querySelectPart, "?revisionB");
767                 sparqlQueryRevisionB = String.format(sparqlTemplateRevisionB, MergeTripleStateEnum.ADDED.getDgRepresentation());
768             } else if (currentTripleStateB.equals(MergeTripleStateEnum.DELETED.getSdRepresentation())) {
769                 // In revision B the triple was deleted
770                 querySelectPart = String.format(querySelectPart, "?revisionB");
771                 sparqlQueryRevisionB = String.format(sparqlTemplateRevisionB, MergeTripleStateEnum.DELETED.getDgRepresentation());
772             } else if (currentTripleStateB.equals(MergeTripleStateEnum.ORIGINAL.getSdRepresentation())) {
773                 // In revision B the triple is original
774                 querySelectPart = String.format(querySelectPart, "?revisionB");
775                 sparqlQueryRevisionB = String.format(sparqlTemplateRevisionB, MergeTripleStateEnum.ORIGINAL.getDgRepresentation());
776             } else if (currentTripleStateB.equals(MergeTripleStateEnum.NOTINCLUDED.getSdRepresentation())) {
777                 // In revision B the triple is not included
778                 querySelectPart = String.format(querySelectPart, "");
779                 sparqlQueryRevisionB = sparqlTemplateNotExistsRevisionB;
780             }
781 
782             // Concatenated SPARQL query
783             String query = String.format(
784                     Config.prefixes
785                             + "%s"
786                             + "WHERE { %n"
787                             + "%s"
788                             + "%s"
789                             + "} %n", querySelectPart, sparqlQueryRevisionA, sparqlQueryRevisionB);
790 
791             // Iterate over all triples
792             ResultSet resultSetTriples = TripleStoreInterfaceSingleton.get().executeSelectQuery(query);
793             while (resultSetTriples.hasNext()) {
794                 QuerySolution qsQuery = resultSetTriples.next();
795 
796                 String subject = qsQuery.getResource("?s").toString();
797                 String predicate = qsQuery.getResource("?p").toString();
798 
799                 // Differ between literal and resource
800                 String object = "";
801                 if (qsQuery.get("?o").isLiteral()) {
802                     object = "\"" + qsQuery.getLiteral("?o").toString() + "\"";
803                 } else {
804                     object = "<" + qsQuery.getResource("?o").toString() + ">";
805                 }
806 
807                 // Create the references A and B part of the query
808                 String referencesAB = ". %n";
809                 if (!currentTripleStateA.equals(MergeTripleStateEnum.NOTINCLUDED.getSdRepresentation()) && !currentTripleStateB.equals(MergeTripleStateEnum.NOTINCLUDED.getSdRepresentation())) {
810                     referencesAB = String.format(
811                             "			mmo:referencesA <%s> ; %n"
812                                     + "			mmo:referencesB <%s> %n", qsQuery.getResource("?revisionA").toString(),
813                             qsQuery.getResource("?revisionB").toString());
814                 } else if (currentTripleStateA.equals(MergeTripleStateEnum.NOTINCLUDED.getSdRepresentation()) && !currentTripleStateB.equals(MergeTripleStateEnum.NOTINCLUDED.getSdRepresentation())) {
815                     referencesAB = String.format(
816                             "			mmo:referencesB <%s> %n", qsQuery.getResource("?revisionB").toString());
817                 } else if (!currentTripleStateA.equals(MergeTripleStateEnum.NOTINCLUDED.getSdRepresentation()) && currentTripleStateB.equals(MergeTripleStateEnum.NOTINCLUDED.getSdRepresentation())) {
818                     referencesAB = String.format(
819                             "			mmo:referencesA <%s> %n", qsQuery.getResource("?revisionA").toString());
820                 }
821 
822                 String queryTriple = Config.prefixes + String.format(
823                         "INSERT DATA { GRAPH <%s> {%n"
824                                 + "	<%s> a mmo:DifferenceGroup ; %n"
825                                 + "	mmo:hasTripleStateA <%s> ; %n"
826                                 + "	mmo:hasTripleStateB <%s> ; %n"
827                                 + "	mmo:isConflicting %s ; %n"
828                                 + "	mmo:automaticResolutionState <%s> ; %n"
829                                 + "	mmo:hasDifference [ %n"
830                                 + "		a mmo:Difference ; %n"
831                                 + "			mmo:hasTriple [ %n"
832                                 + "				rdf:subject <%s> ; %n"
833                                 + "				rdf:predicate <%s> ; %n"
834                                 + "				rdf:object %s %n"
835                                 + "			] ; %n"
836                                 + "%s"
837                                 + "	] . %n"
838                                 + "} }", graphNameDifferenceTripleModel,
839                         currentDifferenceCombinationURI,
840                         currentTripleStateA,
841                         currentTripleStateB,
842                         currentConflictState,
843                         currentAutomaticResolutionState,
844                         subject,
845                         predicate,
846                         object,
847                         referencesAB);
848 
849                 TripleStoreInterfaceSingleton.get().executeUpdateQuery(queryTriple);
850             }
851         }
852     }
853 
854 }