View Javadoc
1   /*
2    * Copyright 2013-2023 Medical Information Systems Research Group (https://medical.zcu.cz),
3    * Department of Computer Science and Engineering, University of West Bohemia.
4    * Address: Univerzitni 8, 306 14 Plzen, Czech Republic.
5    *
6    * This file is part of Sparkle project.
7    *
8    * Sparkle is free software: you can redistribute it and/or modify
9    * it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License.
11   *
12   * Sparkle is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with Sparkle. If not, see <http://www.gnu.org/licenses/>.
19   */
20  package cz.zcu.mre.sparkle.tools.sparqlParser;
21  
22  import cz.zcu.mre.sparkle.gui.query.modifiers.GroupGraphPatternPane;
23  import cz.zcu.mre.sparkle.gui.query.triplePane.TriplePane;
24  import cz.zcu.mre.sparkle.tools.Saveable.LoadException;
25  import cz.zcu.mre.sparkle.tools.SparqlParser;
26  import org.antlr.v4.runtime.tree.ParseTree;
27  import java.util.List;
28  import java.util.Stack;
29  import static cz.zcu.mre.sparkle.tools.sparqlParser.SparqlParserUtils.getTreeChild;
30  
31  /**
32   * Instance of this class walks through where part of query (it's used during
33   * query loading from text file).
34   *
35   * @author Josef Kazak
36   * @author Petr Vcelak (vcelak@kiv.zcu.cz)
37   */
38  public class SparqlParserWhereClauseWalker {
39  
40      private final ParseTree root;
41      private final GroupGraphPatternPane basePane;
42  
43      public SparqlParserWhereClauseWalker(ParseTree root, GroupGraphPatternPane basePane) {
44          this.root = root;
45          this.basePane = basePane;
46      }
47  
48      /**
49       * Walks through where clause of query (it saved in ParseTree). Walking is
50       * DFS way.
51       *
52       * @throws cz.zcu.mre.sparkle.tools.Saveable.LoadException on load.
53       */
54      public void processWhereClauseDFS() throws LoadException {
55          Stack<GroupGraphPatternPane> groupGraphStack = new Stack<>(); //stack of query groups
56          groupGraphStack.push(this.basePane);
57          Stack<SparqlParserNode> nodeStack = new Stack<>(); //current path in parse tree
58          nodeStack.push(new SparqlParserNode(this.root));
59  
60          while (!nodeStack.isEmpty()) {
61              SparqlParserNode currentNode = nodeStack.peek();
62              ParseTree nodeData = currentNode.getNodeData();
63  
64              if (nodeData instanceof SparqlParser.TriplesBlockContext) { //triples
65                  loadTriplesBlock(nodeStack, nodeData, groupGraphStack.peek());
66  
67              } else if ((nodeData instanceof SparqlParser.GroupGraphPatternContext
68                      && ((SparqlParser.GroupGraphPatternContext) nodeData).subSelect() == null)
69                      || (nodeData instanceof SparqlParser.TriplesTemplateContext
70                      && nodeData.getParent() instanceof SparqlParser.QuadsNotTriplesContext)) { //groups
71  
72                  processGroupGraph(nodeData, currentNode, groupGraphStack, nodeStack);
73  
74              } else if (nodeData instanceof SparqlParser.ExpressionContext) {
75                  ParseTree parentNode = nodeData.getParent();
76  
77                  if (parentNode instanceof SparqlParser.ConstraintContext) { //filters
78                      processFilter(nodeData.getText(), groupGraphStack, nodeStack);
79  
80                  } else if (parentNode instanceof SparqlParser.BuiltInCallContext) {
81                      processFilter(parentNode.getText(), groupGraphStack, nodeStack);
82  
83                  } else {
84                      getTreeChild(currentNode, nodeStack);
85                  }
86  
87              } else if (nodeData instanceof SparqlParser.InlineDataContext) {
88                  groupGraphStack.peek().addValuesPane().load(nodeData);
89                  nodeStack.pop();
90  
91              } else if (nodeData instanceof SparqlParser.RegexExpressionContext) { //can be more "expression" in a regex => this
92                  processFilter(nodeData.getText(), groupGraphStack, nodeStack);
93  
94              } else if (nodeData instanceof SparqlParser.TriplesSameSubjectContext) { //vars in construct query and in QuadData
95                  ParseTree parentNode = nodeData.getParent();
96  
97                  if (parentNode instanceof SparqlParser.ConstructTriplesContext) {
98                      obtainConstructTriples(nodeStack, parentNode, groupGraphStack.peek());
99                      nodeStack.pop();
100 
101                 } else {
102                     loadTriplesBlock(nodeStack, nodeData, groupGraphStack.peek());
103                 }
104             } else if (nodeData instanceof SparqlParser.SubSelectContext
105                     && nodeStack.size() > 1) { //root in SUBQUERY is SparqlParserContextHelp.SubSelectContext
106 
107                 processSubSelect(nodeData, groupGraphStack, nodeStack);
108 
109             } else if (nodeData instanceof SparqlParser.BindContext) {
110                 groupGraphStack.peek().addBind().load(nodeData);
111                 nodeStack.pop();
112 
113             } else { //uninsteresting node -> getting next node
114                 getTreeChild(currentNode, nodeStack);
115 
116             }
117         }
118     }
119 
120     /**
121      * It decides on treatment of group graph (eg. {?a ?b ?c}) when relevant
122      * node was found.
123      *
124      * @param currentNodeData current ParseTree node of where clause
125      * @param currentNode wrapper of currentNodeData
126      * @param groupGraphStack contains "group graph deep" of query
127      * @param stack shows current position in tree of where clause
128      *
129      * @throws cz.zcu.mre.sparkle.tools.Saveable.LoadException
130      */
131     private void processGroupGraph(ParseTree currentNodeData, SparqlParserNode currentNode,
132             Stack<GroupGraphPatternPane> groupGraphStack, Stack<SparqlParserNode> stack) throws LoadException {
133 
134         ParseTree parentNode = currentNodeData.getParent();
135         if (!(parentNode instanceof SparqlParser.WhereClauseContext)) {
136 
137             if (parentNode instanceof SparqlParser.GroupOrUnionGraphPatternContext) {
138                 stack.pop();
139                 SparqlParserNode parentGraphNode = stack.peek();
140                 stack.push(currentNode);
141 
142                 if (parentNode.getChildCount() > 1 && parentGraphNode.notExistsUnvisitedNode()) {
143                     processGraphPattern(currentNodeData, currentNode, groupGraphStack, stack);
144                 } else {
145                     processGraphPattern(null, currentNode, groupGraphStack, stack);
146                 }
147 
148             } else {
149                 processGraphPattern(currentNodeData, currentNode, groupGraphStack, stack);
150             }
151 
152         } else {
153             getTreeChild(currentNode, stack);
154         }
155     }
156 
157     /**
158      * It walks through construct clause of query.
159      *
160      * @param stack shows current position in tree of where clause.
161      * @param nodeData current ParseTree node of where clause.
162      * @param groupGraphPattern "group graph deep" of query.
163      *
164      * @throws cz.zcu.mre.sparkle.tools.Saveable.LoadException
165      */
166     private void obtainConstructTriples(Stack<SparqlParserNode> stack, ParseTree nodeData,
167             GroupGraphPatternPane groupGraphPattern) throws LoadException {
168         Stack<SparqlParserNode> constructNodeStack = new Stack<>(); // More triples in construct
169         constructNodeStack.push(new SparqlParserNode(nodeData));
170 
171         while (!constructNodeStack.isEmpty()) {
172             SparqlParserNode constructCurrentNode = constructNodeStack.peek();
173             ParseTree constructNodeData = constructCurrentNode.getNodeData();
174             if (constructNodeData instanceof SparqlParser.TriplesSameSubjectContext) { // Triples
175                 loadTriplesBlock(constructNodeStack, constructNodeData, groupGraphPattern);
176             } else { // Uninsteresting node -> getting next node
177                 getTreeChild(constructCurrentNode, constructNodeStack);
178             }
179         }
180 
181         stack.pop();
182     }
183 
184     /**
185      * Method sets obtained filters.
186      *
187      * @param filterText filter expression
188      * @param groupGraphStack contains "group graph deep" of query
189      * @param nodeStack shows current position in tree of where clause
190      */
191     private void processFilter(String filterText, Stack<GroupGraphPatternPane> groupGraphStack,
192             Stack<SparqlParserNode> nodeStack) throws LoadException {
193 
194         groupGraphStack.peek().loadFilter(filterText);
195         nodeStack.pop();
196     }
197 
198     /**
199      * Method adds new SUBSELECT.
200      *
201      * @param nodeData SUBSELECT root node
202      * @param groupGraphStack contains "group graph deep" of query
203      * @param nodeStack shows current position in tree of where clause
204      */
205     private void processSubSelect(ParseTree nodeData, Stack<GroupGraphPatternPane> groupGraphStack,
206             Stack<SparqlParserNode> nodeStack) throws LoadException {
207 
208         groupGraphStack.peek().loadSubSelect(nodeData);
209         nodeStack.pop();
210     }
211 
212     /**
213      * It creates new GroupGraphPatternPane (if not exists).
214      *
215      * @param currentNodeData current ParseTree node of where clause
216      * @param node wrapper of currentNodeData
217      * @param groupGraphStack contains "group graph deep" of query
218      * @param stack shows current position in tree of where clause
219      *
220      * @throws cz.zcu.mre.sparkle.tools.Saveable.LoadException
221      */
222     private void processGraphPattern(ParseTree currentNodeData, SparqlParserNode node,
223             Stack<GroupGraphPatternPane> groupGraphStack, Stack<SparqlParserNode> stack) throws LoadException {
224 
225         if (node.getVisitedNodeState()) {
226             if (node.notExistsUnvisitedNode()) {
227                 groupGraphStack.pop();
228             }
229         } else {
230             GroupGraphPatternPane groupGraph = groupGraphStack.peek().addGroupGraphPatternPane();
231             groupGraph.load(currentNodeData);
232             groupGraphStack.push(groupGraph);
233         }
234 
235         getTreeChild(node, stack);
236     }
237 
238     /**
239      * Method loads triples and subtriples.
240      *
241      * @param stack shows current position in tree of where clause
242      * @param nodeData current ParseTree node of where clause
243      * @param pane shows triple position in query
244      *
245      * @throws cz.zcu.mre.sparkle.tools.Saveable.LoadException
246      */
247     private void loadTriplesBlock(Stack<SparqlParserNode> stack, ParseTree nodeData, GroupGraphPatternPane pane)
248             throws LoadException {
249 
250         SparqlParserTriplesWalker triplesWalker = new SparqlParserTriplesWalker(nodeData);
251         triplesWalker.visitBlock();
252         List<SparqlParserTriple> tripleList = triplesWalker.getObtainedTriples();
253         TriplePane tp = null;
254 
255         for (SparqlParserTriple triple : tripleList) {
256             if (triple.isSubTriple()) {
257                 if (tp != null) {
258                     tp.addSubTriple(false).load(triple);
259                 }
260             } else {
261                 tp = pane.addTriple(null, false);
262                 tp.load(triple);
263             }
264         }
265 
266         stack.pop();
267     }
268 }