View Javadoc
1   package de.tud.plt.r43ples.core;
2   
3   import de.tud.plt.r43ples.exception.InternalErrorException;
4   import de.tud.plt.r43ples.exception.QueryErrorException;
5   import de.tud.plt.r43ples.existentobjects.ChangeSet;
6   import de.tud.plt.r43ples.existentobjects.RevisionGraph;
7   import de.tud.plt.r43ples.existentobjects.SemanticChange;
8   import de.tud.plt.r43ples.iohelper.Helper;
9   import de.tud.plt.r43ples.iohelper.JenaModelManagement;
10  import de.tud.plt.r43ples.management.Config;
11  import de.tud.plt.r43ples.management.R43plesRequest;
12  import de.tud.plt.r43ples.triplestoreInterface.TripleStoreInterface;
13  import de.tud.plt.r43ples.triplestoreInterface.TripleStoreInterfaceSingleton;
14  import de.tud.plt.r43ples.webservice.Endpoint;
15  import org.apache.jena.query.QuerySolution;
16  import org.apache.jena.query.ResultSet;
17  import org.apache.jena.query.ResultSetFactory;
18  import org.apache.jena.rdf.model.Model;
19  import org.apache.jena.rdf.model.Statement;
20  import org.apache.jena.rdf.model.StmtIterator;
21  import org.apache.logging.log4j.LogManager;
22  import org.apache.logging.log4j.Logger;
23  
24  import java.io.ByteArrayInputStream;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.LinkedList;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  
32  
33  /**
34   * Aggregation of atomic changes to high level ones based upon the provided aggregation rules as SPIN.
35   * Example query: AGG GRAPH <http://test.com/r43ples-dataset-hlc-aggregation> REVISION "1" TO REVISION "2"
36   * Currently the implementation only works if the end revision is the direct succeeding revision of the start revision.
37   *
38   * @author Stephan Hensel
39   */
40  public class AggregationDraft {
41  
42      /** The logger. **/
43      private Logger logger = LogManager.getLogger(SelectConstructAskQuery.class);
44  
45      /** The pattern modifier. **/
46      private final int patternModifier = Pattern.DOTALL + Pattern.MULTILINE + Pattern.CASE_INSENSITIVE;
47      /** The merge query pattern. **/
48      private final Pattern patternAggQuery = Pattern.compile(
49              "AGG\\s*GRAPH\\s*<(?<graph>[^>]*?)>\\s*REVISION\\s*\"(?<startRevisionIdentifier>[^\"]*?)\"\\s*TO\\s*REVISION\\s*\"(?<endRevisionIdentifier>[^\"]*?)\"\\s*",
50              patternModifier);
51  
52      /** The corresponding R43ples request. **/
53      private R43plesRequest request;
54      /** The start revision identifier. **/
55      private String startRevisionIdentifier;
56      /** The end revision identifier. **/
57      private String endRevisionIdentifier;
58      /** The change set between start and end revision. **/
59      private ChangeSet changeSetStartToEnd;
60      /** The graph name **/
61      private String graphName;
62      /** The revision graph. **/
63      private RevisionGraph revisionGraph;
64  
65      // Dependencies
66      /** The triplestore interface to use. **/
67      private TripleStoreInterface tripleStoreInterface;
68      /** The current URI calculator instance. */
69      private URICalculator uriCalculator;
70      /** The endpoint. **/
71      private Endpoint ep = new Endpoint();
72  
73  
74      /**
75       * The constructor.
76       *
77       * @param request the request received by R43ples
78       * @throws InternalErrorException
79       */
80      protected AggregationDraft(R43plesRequest request) throws InternalErrorException {
81          // Dependencies
82          this.tripleStoreInterface = TripleStoreInterfaceSingleton.get();
83          this.uriCalculator = new URICalculator();
84  
85          this.request = request;
86  
87          this.extractRequestInformation();
88      }
89  
90  
91      /**
92       * The constructor.
93       *
94       * @param revisionGraph the revision graph
95       * @param changeSetStartToEnd the change set between start and end revision
96       */
97      protected AggregationDraft(RevisionGraph revisionGraph, ChangeSet changeSetStartToEnd) {
98          // Dependencies
99          this.tripleStoreInterface = TripleStoreInterfaceSingleton.get();
100         this.uriCalculator = new URICalculator();
101 
102         this.revisionGraph = revisionGraph;
103         this.graphName = revisionGraph.getGraphName();
104         this.changeSetStartToEnd = changeSetStartToEnd;
105         this.startRevisionIdentifier = changeSetStartToEnd.getPriorRevision().getRevisionIdentifier();
106         this.endRevisionIdentifier = changeSetStartToEnd.getSucceedingRevision().getRevisionIdentifier();
107     }
108 
109     /**
110      * Triggers the aggregation process and writes meta information into revision graph.
111      *
112      * @return the list of created semantic changes
113      * @throws InternalErrorException
114      */
115     protected LinkedList<SemanticChange> aggregate() throws InternalErrorException {
116 
117         // New linked list to store all created semantic changes
118         LinkedList<SemanticChange> semanticChangesLinkedList = new LinkedList<>();
119 
120         // Get all available aggregation rules
121         String queryAggRules = String.format(
122                 Config.prefixes
123                         + "SELECT ?aggrule %n"
124                         + "WHERE { GRAPH <%s> { %n"
125                         + "	?aggrule a aero:HLCAggRule . %n"
126                         + "} } %n", Config.rules_graph);
127 
128         // Iterate over available aggregation rules
129         ResultSet resultSetAggRules = tripleStoreInterface.executeSelectQuery(queryAggRules);
130         while (resultSetAggRules.hasNext()) {
131             QuerySolution qs = resultSetAggRules.next();
132 
133             String aggRuleURI = qs.getResource("?aggrule").toString();
134 
135             // Get the SPIN query
136             String querySpinQuery = String.format(
137                     Config.prefixes
138                             + "SELECT ?query %n"
139                             + "WHERE { GRAPH <%s> { %n"
140                             + "	<%s> a aero:HLCAggRule ; %n"
141                             + "      aero:spinQuery ?query ."
142                             + "} } %n", Config.rules_graph, aggRuleURI);
143             ResultSet resultSetSpinRule = tripleStoreInterface.executeSelectQuery(querySpinQuery);
144             String spinURI = null;
145             while (resultSetSpinRule.hasNext()) {
146                 QuerySolution qsSpin = resultSetSpinRule.next();
147                 spinURI = qsSpin.getResource("?query").toString();
148             }
149             String spinQueryN3 = Helper.getAllRelatedElementsToURI(Config.rules_graph, spinURI);
150 
151             // Create the SPARQL query
152             String sparqlAggQuery = Helper.getSparqlSelectQueryFromSpin(spinQueryN3, spinURI);
153 
154             // Replace placeholder with current request information
155             sparqlAggQuery = sparqlAggQuery.replace("<http://NAMEDGRAPH#ADDSET-1-2>", "<" + changeSetStartToEnd.getAddSetURI() + ">");
156             sparqlAggQuery = sparqlAggQuery.replace("<http://NAMEDGRAPH#DELETESET-1-2>", "<" + changeSetStartToEnd.getDeleteSetURI() + ">");
157             sparqlAggQuery = sparqlAggQuery.replace("<http://NAMEDGRAPH#rev1>", "<" + graphName + "> REVISION \"" + startRevisionIdentifier + "\"");
158             sparqlAggQuery = sparqlAggQuery.replace("<http://NAMEDGRAPH#rev2>", "<" + graphName + "> REVISION \"" + endRevisionIdentifier + "\"");
159 
160             String sparqlResult = ep.sparql(sparqlAggQuery).getEntity().toString();
161 
162             ResultSet resultSet = ResultSetFactory.fromXML(new ByteArrayInputStream(sparqlResult.getBytes()));
163 
164             while (resultSet.hasNext()) {
165                 QuerySolution qsResult = resultSet.next();
166                 semanticChangesLinkedList.add(addMetaInformation(aggRuleURI, spinQueryN3, spinURI, qsResult));
167             }
168 
169         }
170 
171         return semanticChangesLinkedList;
172     }
173 
174     /**
175      * Adds meta information of aggregation to the revision graph.
176      *
177      * @param aggRuleURI the aggregation rule URI
178      * @param spinQueryN3 the SPIN query as N3
179      * @param spinURI the URI of the SPIN query
180      * @param qsResult the query result after execution of the SPIN query
181      *
182      * @return the created semantic change
183      * @throws InternalErrorException
184      */
185     private SemanticChange addMetaInformation(String aggRuleURI, String spinQueryN3, String spinURI, QuerySolution qsResult) throws InternalErrorException {
186         // Basic meta data
187         String semanticChangeURI = uriCalculator.getRandomURI(revisionGraph);
188         String additionsURI = uriCalculator.getRandomURI(revisionGraph);
189         String deletionsURI = uriCalculator.getRandomURI(revisionGraph);
190 
191         StringBuilder sb = new StringBuilder();
192         sb.append(String.format(
193                 "<%s> a rmo:SemanticChange ; %n"
194                         + "	rmo:additions <%s> ; %n"
195                         + "	rmo:deletions <%s> ; %n"
196                         + "	aero:usedRule <%s> . %n",
197                 semanticChangeURI, additionsURI, deletionsURI, aggRuleURI));
198 
199         // Variable meta data
200         HashMap<String,String> variableMap = Helper.getVariableMapFromSpin(spinQueryN3, spinURI);
201         HashMap<String,String> variableResultMap = new HashMap<>();
202 
203         Iterator ite = qsResult.varNames();
204         while (ite.hasNext()) {
205             String sparqlVariableURI = uriCalculator.getRandomURI(revisionGraph);
206             String varName = ite.next().toString();
207             String value;
208             try {
209                 value = "<" + qsResult.getResource("?" + varName).toString() + ">";
210             } catch (Exception e) {
211                 value = "\"" + qsResult.getLiteral("?" + varName).toString() + "\"";
212             }
213             variableResultMap.put(varName,value);
214             sb.append(String.format(
215                 "<%s> aero:hasVariables <%s> . %n" +
216                 "<%s> a aero:SPARQLVariable ; %n"
217                         + "	sp:varName \"%s\" ; %n"
218                         + "	aero:value %s ; %n"
219                         + "	aero:spinResource <%s> . %n",
220                 semanticChangeURI, sparqlVariableURI, sparqlVariableURI, varName, value, variableMap.get(varName)));
221 
222         }
223 
224         // TRIG data - Get additions and deletions and store them as TRIG
225 
226         // Get the additions and deletions SPIN queries
227         String queryAddDelSpinQueries = String.format(
228                 Config.prefixes
229                         + "SELECT ?queryAdd ?queryDel %n"
230                         + "WHERE { GRAPH <%s> { %n"
231                         + "	<%s> a aero:HLCAggRule ; %n"
232                         + "      aero:addSetDetectionQuery ?subQueryAdd ; %n"
233                         + "      aero:deleteSetDetectionQuery ?subQueryDel . %n"
234                         + " ?subQueryAdd <http://spinrdf.org/sp#query> ?queryAdd. %n"
235                         + " ?subQueryDel <http://spinrdf.org/sp#query> ?queryDel. %n"
236                         + "} } %n", Config.rules_graph, aggRuleURI);
237         ResultSet resultSetAddDelQueries = tripleStoreInterface.executeSelectQuery(queryAddDelSpinQueries);
238         String spinAddURI = null;
239         String spinDelURI = null;
240         while (resultSetAddDelQueries.hasNext()) {
241             QuerySolution qsSpin = resultSetAddDelQueries.next();
242             spinAddURI = qsSpin.getResource("?queryAdd").toString();
243             spinDelURI = qsSpin.getResource("?queryDel").toString();
244         }
245 
246         String sparqlAddQuery = Helper.getSparqlSelectQueryFromSpin(spinQueryN3, spinAddURI);
247         String sparqlDelQuery = Helper.getSparqlSelectQueryFromSpin(spinQueryN3, spinDelURI);
248 
249         // Get the WHERE part of the queries and replace the variables with query results to get the involved triples
250         Pattern patternWherePart = Pattern.compile(
251                 "(?s:.)*WHERE\\s*\\{\\s*GRAPH(?s:.)*\\{(?<where>(?s:.)*)?\\}\\s*\\}",
252                 patternModifier);
253 
254         assert sparqlAddQuery != null;
255         Matcher mAdd = patternWherePart.matcher(sparqlAddQuery);
256         assert sparqlDelQuery != null;
257         Matcher mDel = patternWherePart.matcher(sparqlDelQuery);
258 
259         mAdd.find();
260         mDel.find();
261 
262         String triplesAdd = mAdd.group("where").trim();
263         String triplesDel = mDel.group("where").trim();
264 
265         if (!triplesAdd.endsWith("."))
266             triplesAdd = triplesAdd.concat(".");
267         if (!triplesDel.endsWith("."))
268             triplesDel = triplesDel.concat(".");
269 
270         for (String currentKey : variableResultMap.keySet()) {
271             triplesAdd = triplesAdd.replaceAll("\\?" + currentKey, variableResultMap.get(currentKey));
272             triplesDel = triplesDel.replaceAll("\\?" + currentKey, variableResultMap.get(currentKey));
273         }
274 
275         Model modelAdd = JenaModelManagement.readTurtleStringToJenaModel(triplesAdd);
276 
277         sb.append(String.format("<%s> a rmo:Set . %n", additionsURI));
278 
279         StmtIterator iteStmtAdd = modelAdd.listStatements();
280         while (iteStmtAdd.hasNext()) {
281             Statement statement = iteStmtAdd.next();
282             String statementURI = uriCalculator.getRandomURI(revisionGraph);
283             sb.append(String.format(
284                 "<%s> rmo:statements <%s> . %n" +
285                 "<%s> a rdf:Statement ; %n"
286                         + " rdf:subject <%s> ; %n"
287                         + " rdf:predicate <%s> ; %n"
288                         + " rdf:object <%s> . %n",
289                 additionsURI, statementURI, statementURI, statement.getSubject().toString(), statement.getPredicate(), statement.getObject()));
290         }
291 
292         Model modelDel = JenaModelManagement.readTurtleStringToJenaModel(triplesDel);
293 
294         sb.append(String.format("<%s> a rmo:Set . %n", deletionsURI));
295 
296         StmtIterator iteStmtDel = modelDel.listStatements();
297         while (iteStmtDel.hasNext()) {
298             Statement statement = iteStmtDel.next();
299             String statementURI = uriCalculator.getRandomURI(revisionGraph);
300             sb.append(String.format(
301                     "<%s> rmo:statements <%s> . %n" +
302                             "<%s> a rdf:Statement ; %n"
303                             + " rdf:subject <%s> ; %n"
304                             + " rdf:predicate <%s> ; %n"
305                             + " rdf:object <%s> . %n",
306                     deletionsURI, statementURI, statementURI, statement.getSubject().toString(), statement.getPredicate(), statement.getObject()));
307         }
308 
309         String queryRevision = Config.prefixes + String.format("INSERT DATA { GRAPH <%s> {%s} }", revisionGraph.getRevisionGraphUri(), sb.toString());
310 
311         tripleStoreInterface.executeUpdateQuery(queryRevision);
312 
313         return new SemanticChange(revisionGraph, semanticChangeURI);
314 
315     }
316 
317     /**
318      * Extracts the request information and stores it to local variables.
319      *
320      * @throws InternalErrorException
321      */
322     private void extractRequestInformation() throws InternalErrorException {
323         Matcher m = patternAggQuery.matcher(this.request.query_sparql);
324 
325         boolean foundEntry = false;
326 
327         while (m.find()) {
328             foundEntry = true;
329 
330             graphName = m.group("graph");
331             revisionGraph = new RevisionGraph(graphName);
332 
333             startRevisionIdentifier = m.group("startRevisionIdentifier");
334             endRevisionIdentifier = m.group("endRevisionIdentifier");
335 
336             logger.debug("graph: " + graphName);
337             logger.debug("startRevisionIdentifier: " + startRevisionIdentifier);
338             logger.debug("endRevisionIdentifier: " + endRevisionIdentifier);
339         }
340         if (!foundEntry) {
341             throw new QueryErrorException("Error in query: " + this.request.query_sparql);
342         }
343 
344         ArrayList<ChangeSet> changeSets = revisionGraph.getRevision(endRevisionIdentifier).getChangeSets();
345         for (ChangeSet changeSet : changeSets) {
346             if (changeSet.getPriorRevision().getRevisionIdentifier().equals(startRevisionIdentifier)) {
347                 changeSetStartToEnd = changeSet;
348             }
349         }
350 
351     }
352 
353 }