Skip to content

Method: enterWhereClauses(SoqlParser.WhereClausesContext)

1: /**
2: * Copyright (C) 2022 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.query.soql;
16:
17: import cz.cvut.kbss.jopa.model.MetamodelImpl;
18: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
19: import cz.cvut.kbss.jopa.model.metamodel.EntityTypeImpl;
20: import cz.cvut.kbss.jopa.model.metamodel.SingularAttributeImpl;
21: import cz.cvut.kbss.jopa.model.metamodel.Type;
22: import org.antlr.v4.runtime.ParserRuleContext;
23: import org.antlr.v4.runtime.tree.ErrorNode;
24: import org.antlr.v4.runtime.tree.ParseTree;
25: import org.antlr.v4.runtime.tree.TerminalNode;
26:
27: import java.util.ArrayList;
28: import java.util.HashMap;
29:
30: public class SoqlQueryListener implements SoqlListener {
31:
32: private final MetamodelImpl metamodel;
33:
34: private String newQuery = "";
35:
36: private String typeDef = "SELECT";
37:
38: // keeps pointer at created object of SoqlAttribute while processing other neccessary rules
39: private SoqlAttribute attrPointer;
40:
41: private final ArrayList<SoqlAttribute> attributes;
42:
43: // keeps index of first object of SoqlAttribute after OR operator
44: private final ArrayList<SoqlAttribute> objectOfNextOr;
45:
46: private final ArrayList<SoqlOrderParameter> orderAttributes;
47:
48: private final ArrayList<SoqlGroupParameter> groupAttributes;
49:
50: private final HashMap<String, String> objectTypes;
51:
52: private boolean isSelectedParamDistinct = false;
53:
54: private boolean isSelectedParamCount = false;
55:
56:
57: public SoqlQueryListener(MetamodelImpl metamodel) {
58: super();
59: this.metamodel = metamodel;
60: this.attributes = new ArrayList<>();
61: this.objectOfNextOr = new ArrayList<>();
62: this.orderAttributes = new ArrayList<>();
63: this.groupAttributes = new ArrayList<>();
64: this.objectTypes = new HashMap<>();
65: }
66:
67: @Override
68: public void enterQuerySentence(SoqlParser.QuerySentenceContext ctx) {
69: }
70:
71: @Override
72: public void exitQuerySentence(SoqlParser.QuerySentenceContext ctx) {
73: buildString();
74: }
75:
76: @Override
77: public void enterSelectStatement(SoqlParser.SelectStatementContext ctx) {
78: }
79:
80: @Override
81: public void exitSelectStatement(SoqlParser.SelectStatementContext ctx) {
82: }
83:
84: @Override
85: public void enterParams(SoqlParser.ParamsContext ctx) {
86: }
87:
88: @Override
89: public void exitParams(SoqlParser.ParamsContext ctx) {
90: }
91:
92: @Override
93: public void enterParam(SoqlParser.ParamContext ctx) {
94: }
95:
96: @Override
97: public void exitParam(SoqlParser.ParamContext ctx) {
98: }
99:
100: @Override
101: public void enterJoinedParams(SoqlParser.JoinedParamsContext ctx) {
102: SoqlAttribute myAttr = new SoqlAttribute();
103: SoqlNode firstNode = new SoqlNode(getOwnerfromParam(ctx));
104: SoqlNode actualNode = firstNode;
105: for (int i = 2; i < ctx.getChildCount(); i += 2) {
106: SoqlNode prevNode = actualNode;
107: actualNode = new SoqlNode(prevNode, ctx.getChild(i).getText());
108: prevNode.setChild(actualNode);
109: }
110: setIris(firstNode);
111: myAttr.setFirstNode(firstNode);
112: attributes.add(myAttr);
113: attrPointer = myAttr;
114: }
115:
116: @Override
117: public void exitJoinedParams(SoqlParser.JoinedParamsContext ctx) {
118: }
119:
120: @Override
121: public void enterParamComma(SoqlParser.ParamCommaContext ctx) {
122: }
123:
124: @Override
125: public void exitParamComma(SoqlParser.ParamCommaContext ctx) {
126: }
127:
128: @Override
129: public void enterDistinctParam(SoqlParser.DistinctParamContext ctx) {
130: }
131:
132: @Override
133: public void exitDistinctParam(SoqlParser.DistinctParamContext ctx) {
134: }
135:
136: @Override
137: public void enterSelectedParam(SoqlParser.SelectedParamContext ctx) {
138: }
139:
140: @Override
141: public void exitSelectedParam(SoqlParser.SelectedParamContext ctx) {
142: }
143:
144: @Override
145: public void enterCount(SoqlParser.CountContext ctx) {
146: isSelectedParamCount = true;
147: }
148:
149: @Override
150: public void exitCount(SoqlParser.CountContext ctx) {
151: }
152:
153: @Override
154: public void enterObject(SoqlParser.ObjectContext ctx) {
155: }
156:
157: @Override
158: public void exitObject(SoqlParser.ObjectContext ctx) {
159: }
160:
161: @Override
162: public void enterObjWithAttr(SoqlParser.ObjWithAttrContext ctx) {
163: String owner = getOwnerfromParam(ctx);
164: String attribute = getAttributefromParam(ctx);
165: SoqlNode firstNode = new SoqlNode(owner);
166: SoqlNode lastNode = new SoqlNode(firstNode, attribute);
167: SoqlAttribute myAttr = new SoqlAttribute();
168: firstNode.setChild(lastNode);
169: setIris(firstNode);
170: myAttr.setFirstNode(firstNode);
171: attributes.add(myAttr);
172: attrPointer = myAttr;
173: }
174:
175: @Override
176: public void exitObjWithAttr(SoqlParser.ObjWithAttrContext ctx) {
177: }
178:
179: @Override
180: public void enterObjWithOutAttr(SoqlParser.ObjWithOutAttrContext ctx) {
181: }
182:
183: @Override
184: public void exitObjWithOutAttr(SoqlParser.ObjWithOutAttrContext ctx) {
185: }
186:
187: @Override
188: public void enterAttribute(SoqlParser.AttributeContext ctx) {
189: }
190:
191: @Override
192: public void exitAttribute(SoqlParser.AttributeContext ctx) {
193: }
194:
195: @Override
196: public void enterTypeDef(SoqlParser.TypeDefContext ctx) {
197: typeDef = ctx.getChild(0).getText();
198: }
199:
200: @Override
201: public void exitTypeDef(SoqlParser.TypeDefContext ctx) {
202: }
203:
204: @Override
205: public void enterDistinct(SoqlParser.DistinctContext ctx) {
206: if ("DISTINCT".equals(ctx.getChild(0).getText())) {
207: isSelectedParamDistinct = true;
208: }
209: }
210:
211: @Override
212: public void exitDistinct(SoqlParser.DistinctContext ctx) {
213: }
214:
215: @Override
216: public void enterLogOp(SoqlParser.LogOpContext ctx) {
217: }
218:
219: @Override
220: public void exitLogOp(SoqlParser.LogOpContext ctx) {
221: }
222:
223: @Override
224: public void enterWhereClauseWrapper(SoqlParser.WhereClauseWrapperContext ctx) {
225: }
226:
227: @Override
228: public void exitWhereClauseWrapper(SoqlParser.WhereClauseWrapperContext ctx) {
229: }
230:
231: @Override
232: public void enterTables(SoqlParser.TablesContext ctx) {
233: }
234:
235: @Override
236: public void exitTables(SoqlParser.TablesContext ctx) {
237: }
238:
239: @Override
240: public void enterTable(SoqlParser.TableContext ctx) {
241: }
242:
243: @Override
244: public void exitTable(SoqlParser.TableContext ctx) {
245: }
246:
247: @Override
248: public void enterTableName(SoqlParser.TableNameContext ctx) {
249: }
250:
251: @Override
252: public void exitTableName(SoqlParser.TableNameContext ctx) {
253: }
254:
255: @Override
256: public void enterTableWithName(SoqlParser.TableWithNameContext ctx) {
257: String table = ctx.getChild(0).getChild(0).getText();
258: String objectName = ctx.getChild(1).getChild(0).getText();
259: objectTypes.put(objectName, table);
260: SoqlAttribute myAttr = new SoqlAttribute();
261: SoqlNode node = new SoqlNode(table);
262: setObjectIri(node);
263: myAttr.setFirstNode(node);
264: myAttr.setOperator("");
265: myAttr.setValue("");
266: attributes.add(myAttr);
267: attrPointer = myAttr;
268: }
269:
270: @Override
271: public void exitTableWithName(SoqlParser.TableWithNameContext ctx) {
272: }
273:
274: @Override
275: public void enterWhereClauses(SoqlParser.WhereClausesContext ctx) {
276: }
277:
278: @Override
279: public void exitWhereClauses(SoqlParser.WhereClausesContext ctx) {
280: }
281:
282: @Override
283: public void enterWhereClauseOps(SoqlParser.WhereClauseOpsContext ctx) {
284: }
285:
286: @Override
287: public void exitWhereClauseOps(SoqlParser.WhereClauseOpsContext ctx) {
288: }
289:
290: @Override
291: public void enterWhereClause(SoqlParser.WhereClauseContext ctx) {
292: }
293:
294: @Override
295: public void exitWhereClause(SoqlParser.WhereClauseContext ctx) {
296: String logicalOperator = getOperators(ctx.getParent());
297: String operator = ctx.getChild(1).getText();
298:
299: ParseTree whereClauseValue = ctx.getChild(2);
300:
301: attrPointer.setOperator(operator);
302: attrPointer.setValue(whereClauseValue.getText());
303:
304: if ("OR".equals(logicalOperator)) {
305: objectOfNextOr.add(attrPointer);
306: }
307: }
308:
309: @Override
310: public void enterWhereClauseValue(SoqlParser.WhereClauseValueContext ctx) {
311: }
312:
313: @Override
314: public void exitWhereClauseValue(SoqlParser.WhereClauseValueContext ctx) {
315: }
316:
317: @Override
318: public void enterWhereClauseParam(SoqlParser.WhereClauseParamContext ctx) {
319: }
320:
321: @Override
322: public void exitWhereClauseParam(SoqlParser.WhereClauseParamContext ctx) {
323: }
324:
325: @Override
326: public void enterOrderByClause(SoqlParser.OrderByClauseContext ctx) {
327: }
328:
329: @Override
330: public void exitOrderByClause(SoqlParser.OrderByClauseContext ctx) {
331: }
332:
333: @Override
334: public void enterOrderByFullFormComma(SoqlParser.OrderByFullFormCommaContext ctx) {
335: }
336:
337: @Override
338: public void exitOrderByFullFormComma(SoqlParser.OrderByFullFormCommaContext ctx) {
339: }
340:
341: @Override
342: public void enterOrderByFullForm(SoqlParser.OrderByFullFormContext ctx) {
343: }
344:
345: @Override
346: public void exitOrderByFullForm(SoqlParser.OrderByFullFormContext ctx) {
347: }
348:
349: @Override
350: public void enterOrderByParam(SoqlParser.OrderByParamContext ctx) {
351: SoqlNode firstNode = new SoqlNode(getOwnerfromParam(ctx));
352: SoqlNode actualNode = firstNode;
353: for (int i = 2; i < ctx.getChildCount(); i += 2) {
354: SoqlNode prevNode = actualNode;
355: actualNode = new SoqlNode(prevNode, ctx.getChild(i).getText());
356: prevNode.setChild(actualNode);
357: }
358: setIris(firstNode);
359: String orderingBy = getOrderingBy(ctx.getParent());
360: SoqlOrderParameter orderParam = new SoqlOrderParameter(firstNode, orderingBy);
361: boolean attrSet = false;
362: for (SoqlAttribute attr : attributes) {
363: if (attr.getAsParam().equals(orderParam.getAsParam())) {
364: orderParam.setAttribute(attr);
365: attrSet = true;
366: }
367: }
368: if (!attrSet) {
369: SoqlAttribute myAttr = new SoqlAttribute();
370: myAttr.setFirstNode(firstNode);
371: myAttr.setOperator("");
372: myAttr.setValue(orderParam.getAsValue());
373: myAttr.setOrderBy(true);
374: attributes.add(1, myAttr);
375: orderParam.setAttribute(myAttr);
376: }
377: orderAttributes.add(orderParam);
378: }
379:
380: @Override
381: public void exitOrderByParam(SoqlParser.OrderByParamContext ctx) {
382: }
383:
384: @Override
385: public void enterGroupByClause(SoqlParser.GroupByClauseContext ctx) {
386: }
387:
388: @Override
389: public void exitGroupByClause(SoqlParser.GroupByClauseContext ctx) {
390: }
391:
392: @Override
393: public void enterGroupByParamComma(SoqlParser.GroupByParamCommaContext ctx) {
394: }
395:
396: @Override
397: public void exitGroupByParamComma(SoqlParser.GroupByParamCommaContext ctx) {
398: }
399:
400: @Override
401: public void enterGroupByParam(SoqlParser.GroupByParamContext ctx) {
402: SoqlNode firstNode = new SoqlNode(getOwnerfromParam(ctx));
403: SoqlNode actualNode = firstNode;
404: for (int i = 2; i < ctx.getChildCount(); i += 2) {
405: SoqlNode prevNode = actualNode;
406: actualNode = new SoqlNode(prevNode, ctx.getChild(i).getText());
407: prevNode.setChild(actualNode);
408: }
409: setIris(firstNode);
410: SoqlGroupParameter groupParam = new SoqlGroupParameter(firstNode);
411: boolean attrSet = false;
412: for (SoqlAttribute attr : attributes) {
413: if (attr.getAsParam().equals(groupParam.getAsParam())) {
414: groupParam.setAttribute(attr);
415: attrSet = true;
416: }
417: }
418: if (!attrSet) {
419: SoqlAttribute myAttr = new SoqlAttribute();
420: myAttr.setFirstNode(firstNode);
421: myAttr.setOperator("");
422: myAttr.setValue(groupParam.getAsValue());
423: myAttr.setGroupBy(true);
424: attributes.add(1, myAttr);
425: groupParam.setAttribute(myAttr);
426: }
427: groupAttributes.add(groupParam);
428: }
429:
430: @Override
431: public void exitGroupByParam(SoqlParser.GroupByParamContext ctx) {
432: }
433:
434: @Override
435: public void visitTerminal(TerminalNode terminalNode) {
436: }
437:
438: @Override
439: public void visitErrorNode(ErrorNode errorNode) {
440: }
441:
442: @Override
443: public void enterEveryRule(ParserRuleContext parserRuleContext) {
444: }
445:
446: @Override
447: public void exitEveryRule(ParserRuleContext parserRuleContext) {
448: }
449:
450: //Methods to help parse tree
451: private String getOwnerfromParam(ParserRuleContext ctx) {
452: return ctx.getChild(0).getChild(0).getText();
453: }
454:
455: private String getAttributefromParam(ParserRuleContext ctx) {
456: return ctx.getChild(2).getChild(0).getText();
457: }
458:
459: private String getOperators(ParserRuleContext ctx) {
460: String operator = "";
461: switch (ctx.getChildCount()) {
462: case 2:
463: if (ctx.getChild(0).getChildCount() > 0) {
464: operator = ctx.getChild(0).getChild(0).getText();
465: } else {
466: attrPointer.setNot(true);
467: }
468: break;
469: case 3:
470: attrPointer.setNot(true);
471: operator = ctx.getChild(0).getChild(0).getText();
472: break;
473: default:
474: attrPointer.setNot(false);
475: }
476: return operator;
477: }
478:
479: private String getOrderingBy(ParserRuleContext ctx) {
480: return ctx.getChildCount() > 1 ? ctx.getChild(1).getText() : "";
481: }
482:
483: private void setObjectIri(SoqlNode node) {
484: if (metamodel == null) {
485: return;
486: }
487: EntityTypeImpl<?> entityType = getEntityType(node.getValue());
488: if (entityType == null) {
489: return;
490: }
491: node.setIri(entityType.getIRI().toString());
492: if (node.hasNextChild()) {
493: setAllNodesIris(entityType, node.getChild());
494: }
495: }
496:
497: private EntityTypeImpl<?> getEntityType(String name) {
498: if (metamodel == null) {
499: return null;
500: }
501: for (EntityType<?> type : metamodel.getEntities()) {
502: EntityTypeImpl<?> entityType = (EntityTypeImpl<?>) type;
503: if (entityType.getName().equals(name)) {
504: return entityType;
505: }
506: }
507: return null;
508: }
509:
510: private EntityTypeImpl<?> getEntityType(Type<?> type) {
511: if (metamodel == null) {
512: return null;
513: }
514: for (EntityType<?> value : metamodel.getEntities()) {
515: EntityTypeImpl<?> entityType = (EntityTypeImpl<?>) value;
516: if (entityType.equals(type)) {
517: return entityType;
518: }
519: }
520: return null;
521: }
522:
523: private void setAllNodesIris(EntityTypeImpl<?> entityType, SoqlNode node) {
524: SingularAttributeImpl<?, ?> abstractAttribute = (SingularAttributeImpl<?, ?>) entityType.getAttribute(node.getValue());
525: //not implemented case of 3 or more fragments (chained SoqlNodes)
526: node.setIri(abstractAttribute.getIRI().toString());
527: if (node.hasNextChild()) {
528: Type<?> type = abstractAttribute.getType();
529: EntityTypeImpl<?> attrEntityType = getEntityType(type);
530: if (attrEntityType == null) {
531: return;
532: }
533: setAllNodesIris(attrEntityType, node.getChild());
534: }
535: }
536:
537: private void setIris(SoqlNode firstNode) {
538: if (!objectTypes.containsKey(firstNode.getValue())) {
539: return;
540: }
541: if (metamodel == null) {
542: return;
543: }
544: String objectName = objectTypes.get(firstNode.getValue());
545: EntityTypeImpl<?> entityType = getEntityType(objectName);
546: if (entityType == null) {
547: return;
548: }
549: if (firstNode.hasNextChild()) {
550: setAllNodesIris(entityType, firstNode.getChild());
551: }
552: }
553:
554: public String getSoqlQuery() {
555: return newQuery;
556: }
557:
558:
559: //Methods to build new Query
560: private void buildString() {
561: if (attributes.isEmpty()) {
562: return;
563: }
564: StringBuilder newQueryBuilder = new StringBuilder(typeDef);
565: if (isSelectedParamCount) {
566: newQueryBuilder.append(getCountPart());
567: } else {
568: if (isSelectedParamDistinct) {
569: newQueryBuilder.append(" ").append("DISTINCT");
570: }
571: newQueryBuilder.append(" ?x ");
572: }
573: newQueryBuilder.append("WHERE { ");
574: newQueryBuilder.append(processSupremeAttributes());
575: if (!objectOfNextOr.isEmpty()) {
576: newQueryBuilder.append("{ ");
577: }
578: newQueryBuilder.append(processAttributes());
579: if (!objectOfNextOr.isEmpty()) {
580: newQueryBuilder.append("} ");
581: }
582: newQueryBuilder.append("}");
583: if (!groupAttributes.isEmpty()) {
584: newQueryBuilder.append(" ").append(buildGrouping());
585: }
586: if (!orderAttributes.isEmpty()) {
587: newQueryBuilder.append(" ").append(buildOrdering());
588: }
589: newQuery = newQueryBuilder.toString();
590: }
591:
592: private StringBuilder getCountPart() {
593: StringBuilder countPart = new StringBuilder(" (COUNT(");
594: if (isSelectedParamDistinct) {
595: countPart.append("distinct ");
596: }
597: countPart.append("?x) AS ?count) ");
598: return countPart;
599: }
600:
601: private StringBuilder processSupremeAttributes() {
602: StringBuilder attributesPart = new StringBuilder();
603: SoqlAttribute pointer = attributes.get(0);
604: while (pointer.isObject() || pointer.isOrderBy() || pointer.isGroupBy()) {
605: attributesPart.append(processAttribute(pointer));
606: attributes.remove(pointer);
607: if (attributes.isEmpty()) {
608: break;
609: } else {
610: pointer = attributes.get(0);
611: }
612: }
613: return attributesPart;
614: }
615:
616: private StringBuilder processAttributes() {
617: StringBuilder attributesPart = new StringBuilder();
618: ArrayList<SoqlAttribute> toFilter = new ArrayList<>();
619: ArrayList<SoqlAttribute> toInvFilter = new ArrayList<>();
620: for (SoqlAttribute myAttr : attributes) {
621: if (objectOfNextOr.contains(myAttr)) {
622: StringBuilder orPart = new StringBuilder();
623: orPart.append(processAllFilters(toFilter, toInvFilter));
624: toFilter.clear();
625: toInvFilter.clear();
626: orPart.append("} UNION { ");
627: attributesPart.append(orPart);
628: }
629: if (myAttr.isNot()) {
630: toInvFilter.add(myAttr);
631: } else {
632: if (myAttr.isFilter()) {
633: toFilter.add(myAttr);
634: }
635: attributesPart.append(processAttribute(myAttr));
636: }
637: }
638: attributesPart.append(processAllFilters(toFilter, toInvFilter));
639: return attributesPart;
640: }
641:
642: private StringBuilder processAllFilters(ArrayList<SoqlAttribute> toFilter, ArrayList<SoqlAttribute> toInvFilter) {
643: StringBuilder part = new StringBuilder();
644: if (!toFilter.isEmpty()) {
645: part.append(processFilter(toFilter));
646: }
647: if (!toInvFilter.isEmpty()) {
648: part.append(processInvFilter(toInvFilter));
649: }
650: return part;
651: }
652:
653: private StringBuilder processFilter(ArrayList<SoqlAttribute> toFilter) {
654: StringBuilder buildFilter = new StringBuilder();
655: if (toFilter.isEmpty()) {
656: return buildFilter;
657: }
658: buildFilter.append("FILTER (");
659: for (SoqlAttribute attr : toFilter) {
660: if (toFilter.indexOf(attr) != 0) {
661: buildFilter.append(" && ");
662: }
663: buildFilter.append(attr.getFilter());
664: }
665: buildFilter.append(") ");
666: return buildFilter;
667: }
668:
669: private StringBuilder processInvFilter(ArrayList<SoqlAttribute> toInvFilter) {
670: StringBuilder buildInvFilter = new StringBuilder();
671: ArrayList<SoqlAttribute> toFilter = new ArrayList<>();
672: if (toInvFilter.isEmpty()) {
673: return buildInvFilter;
674: }
675: buildInvFilter.append("FILTER NOT EXISTS { ");
676: for (SoqlAttribute attr : toInvFilter) {
677: buildInvFilter.append(processAttribute(attr));
678: if (attr.isFilter()) {
679: toFilter.add(attr);
680: }
681: }
682: buildInvFilter.append(processFilter(toFilter)).append("} ");
683: return buildInvFilter;
684: }
685:
686: private StringBuilder processAttribute(SoqlAttribute attr) {
687: return new StringBuilder(attr.getTriplePattern());
688: }
689:
690: private StringBuilder buildOrdering() {
691: StringBuilder sb = new StringBuilder("ORDER BY");
692: for (SoqlOrderParameter orderParam : orderAttributes) {
693: sb.append(" ").append(orderParam.getOrderByPart());
694: }
695: return sb;
696: }
697:
698: private StringBuilder buildGrouping() {
699: StringBuilder sb = new StringBuilder("GROUP BY");
700: for (SoqlGroupParameter groupParam : groupAttributes) {
701: sb.append(" ").append(groupParam.getGroupByPart());
702: }
703: return sb;
704: }
705: }