Skip to content

Method: enterObjWithOutAttr(SoqlParser.ObjWithOutAttrContext)

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