Skip to content

Method: enterObjWithOutAttr(SoqlParser.ObjWithOutAttrContext)

1: /**
2: * Copyright (C) 2022 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
5: * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
6: * version.
7: * <p>
8: * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10: * details. You should have received a copy of the GNU General Public License along with this program. If not, see
11: * <http://www.gnu.org/licenses/>.
12: */
13: package cz.cvut.kbss.jopa.query.soql;
14:
15: import cz.cvut.kbss.jopa.model.MetamodelImpl;
16: import cz.cvut.kbss.jopa.model.metamodel.*;
17: import org.antlr.v4.runtime.ParserRuleContext;
18: import org.antlr.v4.runtime.tree.ErrorNode;
19: import org.antlr.v4.runtime.tree.ParseTree;
20: import org.antlr.v4.runtime.tree.TerminalNode;
21: import org.slf4j.Logger;
22: import org.slf4j.LoggerFactory;
23:
24: import java.util.*;
25: import java.util.stream.Collectors;
26:
27: public class SoqlQueryListener implements SoqlListener {
28:
29: private static final Logger LOG = LoggerFactory.getLogger(SoqlQueryListener.class);
30:
31: private final MetamodelImpl metamodel;
32:
33: private String soql;
34: private String sparql;
35:
36: private String typeDef = "SELECT";
37:
38: // keeps pointer at created object of SoqlAttribute while processing other necessary rules
39: private SoqlAttribute attrPointer;
40:
41: private final List<SoqlAttribute> attributes;
42:
43: // keeps index of first object of SoqlAttribute after OR operator
44: private final Set<SoqlAttribute> objectOfNextOr;
45:
46: private final List<SoqlOrderParameter> orderAttributes;
47:
48: private final List<SoqlGroupParameter> groupAttributes;
49:
50: private final Map<String, String> objectTypes;
51:
52: private boolean isSelectedParamDistinct = false;
53:
54: private boolean isSelectedParamCount = false;
55:
56: private boolean isInObjectIdentifierExpression = false;
57:
58: private String projectedVariable;
59:
60: private String rootVariable = "?x";
61:
62:
63: public SoqlQueryListener(MetamodelImpl metamodel) {
64: this.metamodel = Objects.requireNonNull(metamodel);
65: this.attributes = new ArrayList<>();
66: this.objectOfNextOr = new HashSet<>();
67: this.orderAttributes = new ArrayList<>();
68: this.groupAttributes = new ArrayList<>();
69: this.objectTypes = new HashMap<>();
70: }
71:
72: @Override
73: public void enterQuerySentence(SoqlParser.QuerySentenceContext ctx) {
74: }
75:
76: @Override
77: public void exitQuerySentence(SoqlParser.QuerySentenceContext ctx) {
78: this.soql = ctx.getText();
79: buildSparqlQueryString();
80: }
81:
82: @Override
83: public void enterSelectStatement(SoqlParser.SelectStatementContext ctx) {
84: }
85:
86: @Override
87: public void exitSelectStatement(SoqlParser.SelectStatementContext ctx) {
88: }
89:
90: @Override
91: public void enterParams(SoqlParser.ParamsContext ctx) {
92: }
93:
94: @Override
95: public void exitParams(SoqlParser.ParamsContext ctx) {
96: }
97:
98: @Override
99: public void enterParam(SoqlParser.ParamContext ctx) {
100: }
101:
102: @Override
103: public void exitParam(SoqlParser.ParamContext ctx) {
104: }
105:
106: @Override
107: public void enterJoinedParams(SoqlParser.JoinedParamsContext ctx) {
108: SoqlNode firstNode = linkContextNodes(ctx);
109: SoqlAttribute myAttr = new SoqlAttribute(firstNode);
110: pushNewAttribute(myAttr);
111: }
112:
113: private void pushNewAttribute(SoqlAttribute myAttr) {
114: attributes.add(myAttr);
115: this.attrPointer = myAttr;
116: }
117:
118: @Override
119: public void exitJoinedParams(SoqlParser.JoinedParamsContext ctx) {
120: }
121:
122: @Override
123: public void enterParamComma(SoqlParser.ParamCommaContext ctx) {
124: }
125:
126: @Override
127: public void exitParamComma(SoqlParser.ParamCommaContext ctx) {
128: }
129:
130: @Override
131: public void enterDistinctParam(SoqlParser.DistinctParamContext ctx) {
132: }
133:
134: @Override
135: public void exitDistinctParam(SoqlParser.DistinctParamContext ctx) {
136: }
137:
138: @Override
139: public void enterSelectedParam(SoqlParser.SelectedParamContext ctx) {
140: }
141:
142: @Override
143: public void exitSelectedParam(SoqlParser.SelectedParamContext ctx) {
144: if (!isSelectedParamCount) {
145: this.projectedVariable = ctx.getText();
146: }
147: }
148:
149: @Override
150: public void enterCount(SoqlParser.CountContext ctx) {
151: isSelectedParamCount = true;
152: }
153:
154: @Override
155: public void exitCount(SoqlParser.CountContext ctx) {
156: this.projectedVariable = ctx.getChild(1).getText();
157: }
158:
159: @Override
160: public void enterObject(SoqlParser.ObjectContext ctx) {
161: }
162:
163: @Override
164: public void exitObject(SoqlParser.ObjectContext ctx) {
165: }
166:
167: @Override
168: public void enterObjWithAttr(SoqlParser.ObjWithAttrContext ctx) {
169: String owner = getOwnerFromParam(ctx);
170: String attribute = getAttributeFromParam(ctx);
171: // objectNode.attributeNode
172: SoqlNode objectNode = new AttributeNode(owner);
173: SoqlNode attributeNode = new AttributeNode(objectNode, attribute);
174: objectNode.setChild(attributeNode);
175: if (isIdentifier(objectNode, attributeNode)) {
176: this.isInObjectIdentifierExpression = true;
177: if (projectedVariable.equals(objectNode.getValue())) {
178: attrPointer.setProjected(true);
179: }
180: } else {
181: setIris(objectNode);
182: SoqlAttribute myAttr = new SoqlAttribute(objectNode);
183: pushNewAttribute(myAttr);
184: }
185: }
186:
187: private boolean isIdentifier(SoqlNode objectNode, SoqlNode attributeNode) {
188: if (!objectTypes.containsKey(objectNode.getValue())) {
189: return false;
190: }
191: final String objectName = objectTypes.get(objectNode.getValue());
192: EntityTypeImpl<?> entityType = getEntityType(objectName);
193: if (entityType == null) {
194: return false;
195: }
196: return entityType.getIdentifier().getName().equals(attributeNode.getValue());
197: }
198:
199: @Override
200: public void exitObjWithAttr(SoqlParser.ObjWithAttrContext ctx) {
201: }
202:
203: @Override
204: public void enterObjWithOutAttr(SoqlParser.ObjWithOutAttrContext ctx) {
205: }
206:
207: @Override
208: public void exitObjWithOutAttr(SoqlParser.ObjWithOutAttrContext ctx) {
209: }
210:
211: @Override
212: public void enterAttribute(SoqlParser.AttributeContext ctx) {
213: }
214:
215: @Override
216: public void exitAttribute(SoqlParser.AttributeContext ctx) {
217: }
218:
219: @Override
220: public void enterTypeDef(SoqlParser.TypeDefContext ctx) {
221: typeDef = ctx.getChild(0).getText();
222: }
223:
224: @Override
225: public void exitTypeDef(SoqlParser.TypeDefContext ctx) {
226: }
227:
228: @Override
229: public void enterDistinct(SoqlParser.DistinctContext ctx) {
230: if (SoqlConstants.DISTINCT.equals(ctx.getChild(0).getText())) {
231: isSelectedParamDistinct = true;
232: }
233: }
234:
235: @Override
236: public void exitDistinct(SoqlParser.DistinctContext ctx) {
237: }
238:
239: @Override
240: public void enterWhereClauseWrapper(SoqlParser.WhereClauseWrapperContext ctx) {
241: }
242:
243: @Override
244: public void exitWhereClauseWrapper(SoqlParser.WhereClauseWrapperContext ctx) {
245: }
246:
247: @Override
248: public void enterConditionalExpression(SoqlParser.ConditionalExpressionContext ctx) {
249: }
250:
251: @Override
252: public void exitConditionalExpression(SoqlParser.ConditionalExpressionContext ctx) {
253: }
254:
255: @Override
256: public void enterConditionalTerm(SoqlParser.ConditionalTermContext ctx) {
257: }
258:
259: @Override
260: public void exitConditionalTerm(SoqlParser.ConditionalTermContext ctx) {
261: final ParserRuleContext parentCtx = ctx.getParent();
262: if (parentCtx.getChildCount() > 1 && !parentCtx.getChild(0).equals(ctx)) {
263: objectOfNextOr.add(attrPointer);
264: }
265: }
266:
267: @Override
268: public void enterConditionalFactor(SoqlParser.ConditionalFactorContext ctx) {
269: }
270:
271: @Override
272: public void exitConditionalFactor(SoqlParser.ConditionalFactorContext ctx) {
273: if (ctx.getChildCount() > 1) {
274: attrPointer.setNot(true);
275: }
276: }
277:
278: @Override
279: public void enterSimpleConditionalExpression(SoqlParser.SimpleConditionalExpressionContext ctx) {
280: }
281:
282: @Override
283: public void exitSimpleConditionalExpression(SoqlParser.SimpleConditionalExpressionContext ctx) {
284: }
285:
286: @Override
287: public void enterInExpression(SoqlParser.InExpressionContext ctx) {
288: }
289:
290: @Override
291: public void exitInExpression(SoqlParser.InExpressionContext ctx) {
292: assert ctx.getChildCount() > 2;
293: if (isInObjectIdentifierExpression) {
294: pushNewAttribute(createSyntheticAttributeForEntityId());
295: }
296: final ParseTree value = resolveInExpressionValue(ctx);
297: if (ctx.getChild(1).getText().equals(SoqlConstants.NOT)) {
298: attrPointer.setOperator(InOperator.notIn());
299: } else {
300: attrPointer.setOperator(InOperator.in());
301: }
302: attrPointer.setValue(value.getText());
303: this.isInObjectIdentifierExpression = false;
304: }
305:
306: private SoqlAttribute createSyntheticAttributeForEntityId() {
307: return new SoqlAttribute(
308: attrPointer.getFirstNode().hasChild() ? attrPointer.getFirstNode().getChild() :
309: new AttributeNode(rootVariable.substring(1)));
310: }
311:
312: private ParseTree resolveInExpressionValue(SoqlParser.InExpressionContext ctx) {
313: final ParseTree lastToken = ctx.getChild(ctx.getChildCount() - 1);
314: if (")".equals(lastToken.getText())) {
315: return ctx.getChild(ctx.getChildCount() - 2);
316: }
317: return lastToken;
318: }
319:
320: @Override
321: public void enterInItem(SoqlParser.InItemContext ctx) {
322: }
323:
324: @Override
325: public void exitInItem(SoqlParser.InItemContext ctx) {
326: }
327:
328: @Override
329: public void enterLiteral(SoqlParser.LiteralContext ctx) {
330: }
331:
332: @Override
333: public void exitLiteral(SoqlParser.LiteralContext ctx) {
334: }
335:
336: @Override
337: public void enterLikeExpression(SoqlParser.LikeExpressionContext ctx) {
338: }
339:
340: @Override
341: public void exitLikeExpression(SoqlParser.LikeExpressionContext ctx) {
342: if (ctx.getChildCount() > 2 && ctx.getChild(1).getText().equals(SoqlConstants.NOT)) {
343: attrPointer.setOperator(LikeOperator.notLike());
344: ParseTree whereClauseValue = ctx.getChild(3);
345: attrPointer.setValue(whereClauseValue.getText());
346: } else {
347: attrPointer.setOperator(LikeOperator.like());
348: ParseTree whereClauseValue = ctx.getChild(2);
349: attrPointer.setValue(whereClauseValue.getText());
350: }
351: this.isInObjectIdentifierExpression = false;
352: }
353:
354: @Override
355: public void enterComparisonExpression(SoqlParser.ComparisonExpressionContext ctx) {
356: }
357:
358: @Override
359: public void exitComparisonExpression(SoqlParser.ComparisonExpressionContext ctx) {
360: String operator = ctx.getChild(1).getText();
361:
362: ParseTree whereClauseValue = ctx.getChild(2);
363:
364: if (isInObjectIdentifierExpression) {
365: assert Objects.equals(operator, "=");
366: if (attrPointer.isProjected()) {
367: this.rootVariable = SoqlUtils.soqlVariableToSparqlVariable(whereClauseValue.getText());
368: }
369: if (attributes.size() > 1) {
370: final String varName = whereClauseValue.getText();
371: attrPointer.getFirstNode().getChild().setValue(
372: varName.charAt(0) == SoqlConstants.VARIABLE_PREFIX ? varName.substring(1) : varName);
373: }
374: } else {
375: attrPointer.setOperator(new ComparisonOperator(operator));
376: attrPointer.setValue(whereClauseValue.getText());
377: }
378: this.isInObjectIdentifierExpression = false;
379: }
380:
381: @Override
382: public void enterTables(SoqlParser.TablesContext ctx) {
383: }
384:
385: @Override
386: public void exitTables(SoqlParser.TablesContext ctx) {
387: }
388:
389: @Override
390: public void enterTable(SoqlParser.TableContext ctx) {
391: }
392:
393: @Override
394: public void exitTable(SoqlParser.TableContext ctx) {
395: }
396:
397: @Override
398: public void enterTableName(SoqlParser.TableNameContext ctx) {
399: }
400:
401: @Override
402: public void exitTableName(SoqlParser.TableNameContext ctx) {
403: }
404:
405: @Override
406: public void enterTableWithName(SoqlParser.TableWithNameContext ctx) {
407: String table = ctx.getChild(0).getChild(0).getText();
408: String objectName = ctx.getChild(1).getChild(0).getText();
409: objectTypes.put(objectName, table);
410: SoqlNode node = new AttributeNode(table);
411: setObjectIri(node);
412: SoqlAttribute myAttr = new SoqlAttribute(node);
413: pushNewAttribute(myAttr);
414: }
415:
416: @Override
417: public void exitTableWithName(SoqlParser.TableWithNameContext ctx) {
418: }
419:
420: @Override
421: public void enterWhereClauseValue(SoqlParser.WhereClauseValueContext ctx) {
422: }
423:
424: @Override
425: public void exitWhereClauseValue(SoqlParser.WhereClauseValueContext ctx) {
426: }
427:
428: @Override
429: public void enterWhereClauseParam(SoqlParser.WhereClauseParamContext ctx) {
430: }
431:
432: @Override
433: public void exitWhereClauseParam(SoqlParser.WhereClauseParamContext ctx) {
434: }
435:
436: @Override
437: public void enterStringExpression(SoqlParser.StringExpressionContext ctx) {
438:
439: }
440:
441: @Override
442: public void exitStringExpression(SoqlParser.StringExpressionContext ctx) {
443:
444: }
445:
446: @Override
447: public void enterFunctionsReturningStrings(SoqlParser.FunctionsReturningStringsContext ctx) {
448:
449: }
450:
451: @Override
452: public void exitFunctionsReturningStrings(SoqlParser.FunctionsReturningStringsContext ctx) {
453: final String functionName = ctx.getChild(0).getText();
454: final FunctionNode node = new FunctionNode(attrPointer.getFirstNode(), functionName);
455: attrPointer.setFirstNode(node);
456: }
457:
458: @Override
459: public void enterSimpleArithmeticExpression(SoqlParser.SimpleArithmeticExpressionContext ctx) {
460:
461: }
462:
463: @Override
464: public void exitSimpleArithmeticExpression(SoqlParser.SimpleArithmeticExpressionContext ctx) {
465:
466: }
467:
468: @Override
469: public void enterArithmeticTerm(SoqlParser.ArithmeticTermContext ctx) {
470:
471: }
472:
473: @Override
474: public void exitArithmeticTerm(SoqlParser.ArithmeticTermContext ctx) {
475:
476: }
477:
478: @Override
479: public void enterArithmeticFactor(SoqlParser.ArithmeticFactorContext ctx) {
480:
481: }
482:
483: @Override
484: public void exitArithmeticFactor(SoqlParser.ArithmeticFactorContext ctx) {
485:
486: }
487:
488: @Override
489: public void enterArithmeticPrimary(SoqlParser.ArithmeticPrimaryContext ctx) {
490:
491: }
492:
493: @Override
494: public void exitArithmeticPrimary(SoqlParser.ArithmeticPrimaryContext ctx) {
495:
496: }
497:
498: @Override
499: public void enterFunctionsReturningNumerics(SoqlParser.FunctionsReturningNumericsContext ctx) {
500: }
501:
502: @Override
503: public void exitFunctionsReturningNumerics(SoqlParser.FunctionsReturningNumericsContext ctx) {
504: final String functionName = ctx.getChild(0).getText();
505: final FunctionNode node = new FunctionNode(attrPointer.getFirstNode(), functionName);
506: attrPointer.setFirstNode(node);
507: }
508:
509: @Override
510: public void enterOrderByClause(SoqlParser.OrderByClauseContext ctx) {
511: }
512:
513: @Override
514: public void exitOrderByClause(SoqlParser.OrderByClauseContext ctx) {
515: }
516:
517: @Override
518: public void enterOrderByFullFormComma(SoqlParser.OrderByFullFormCommaContext ctx) {
519: }
520:
521: @Override
522: public void exitOrderByFullFormComma(SoqlParser.OrderByFullFormCommaContext ctx) {
523: }
524:
525: @Override
526: public void enterOrderByFullForm(SoqlParser.OrderByFullFormContext ctx) {
527: }
528:
529: @Override
530: public void exitOrderByFullForm(SoqlParser.OrderByFullFormContext ctx) {
531: }
532:
533: @Override
534: public void enterOrderByParam(SoqlParser.OrderByParamContext ctx) {
535: SoqlNode firstNode = linkContextNodes(ctx);
536: String orderingBy = getOrderingBy(ctx.getParent());
537: SoqlOrderParameter orderParam = new SoqlOrderParameter(firstNode, orderingBy);
538: boolean attrSet = false;
539: for (SoqlAttribute attr : attributes) {
540: if (attr.getAsParam().equals(orderParam.getAsParam())) {
541: orderParam.setAttribute(attr);
542: attrSet = true;
543: }
544: }
545: if (!attrSet) {
546: SoqlAttribute myAttr = new SoqlAttribute(firstNode);
547: myAttr.setValue(orderParam.getAsValue(rootVariable));
548: myAttr.setOrderBy(true);
549: attributes.add(1, myAttr);
550: orderParam.setAttribute(myAttr);
551: }
552: orderAttributes.add(orderParam);
553: }
554:
555: @Override
556: public void exitOrderByParam(SoqlParser.OrderByParamContext ctx) {
557: }
558:
559: @Override
560: public void enterGroupByClause(SoqlParser.GroupByClauseContext ctx) {
561: }
562:
563: @Override
564: public void exitGroupByClause(SoqlParser.GroupByClauseContext ctx) {
565: }
566:
567: @Override
568: public void enterGroupByParamComma(SoqlParser.GroupByParamCommaContext ctx) {
569: }
570:
571: @Override
572: public void exitGroupByParamComma(SoqlParser.GroupByParamCommaContext ctx) {
573: }
574:
575: @Override
576: public void enterGroupByParam(SoqlParser.GroupByParamContext ctx) {
577: SoqlNode firstNode = linkContextNodes(ctx);
578: SoqlGroupParameter groupParam = new SoqlGroupParameter(firstNode);
579: boolean attrSet = false;
580: for (SoqlAttribute attr : attributes) {
581: if (attr.getAsParam().equals(groupParam.getAsParam())) {
582: groupParam.setAttribute(attr);
583: attrSet = true;
584: }
585: }
586: if (!attrSet) {
587: SoqlAttribute myAttr = new SoqlAttribute(firstNode);
588: myAttr.setValue(groupParam.getAsValue(rootVariable));
589: myAttr.setGroupBy(true);
590: attributes.add(1, myAttr);
591: groupParam.setAttribute(myAttr);
592: }
593: groupAttributes.add(groupParam);
594: }
595:
596: private SoqlNode linkContextNodes(ParserRuleContext ctx) {
597: SoqlNode firstNode = new AttributeNode(getOwnerFromParam(ctx));
598: SoqlNode currentNode = firstNode;
599: for (int i = 2; i < ctx.getChildCount(); i += 2) {
600: SoqlNode prevNode = currentNode;
601: currentNode = new AttributeNode(prevNode, ctx.getChild(i).getText());
602: prevNode.setChild(currentNode);
603: }
604: setIris(firstNode);
605: if (currentNode.getIri().isEmpty()) {
606: currentNode.getParent().setChild(null);
607: this.isInObjectIdentifierExpression = true;
608: }
609: return firstNode;
610: }
611:
612: @Override
613: public void exitGroupByParam(SoqlParser.GroupByParamContext ctx) {
614: }
615:
616: @Override
617: public void enterInputParameter(SoqlParser.InputParameterContext ctx) {
618:
619: }
620:
621: @Override
622: public void exitInputParameter(SoqlParser.InputParameterContext ctx) {
623:
624: }
625:
626: @Override
627: public void visitTerminal(TerminalNode terminalNode) {
628: }
629:
630: @Override
631: public void visitErrorNode(ErrorNode errorNode) {
632: }
633:
634: @Override
635: public void enterEveryRule(ParserRuleContext parserRuleContext) {
636: }
637:
638: @Override
639: public void exitEveryRule(ParserRuleContext parserRuleContext) {
640: }
641:
642: //Methods to help parse tree
643: private String getOwnerFromParam(ParserRuleContext ctx) {
644: return ctx.getChild(0).getChild(0).getText();
645: }
646:
647: private String getAttributeFromParam(ParserRuleContext ctx) {
648: return ctx.getChild(2).getChild(0).getText();
649: }
650:
651: private String getOrderingBy(ParserRuleContext ctx) {
652: return ctx.getChildCount() > 1 ? ctx.getChild(1).getText() : "";
653: }
654:
655: private void setObjectIri(SoqlNode node) {
656: EntityTypeImpl<?> entityType = getEntityType(node.getValue());
657: if (entityType == null) {
658: return;
659: }
660: node.setIri(entityType.getIRI().toString());
661: if (node.hasChild()) {
662: setAllNodesIris(entityType, node.getChild());
663: }
664: }
665:
666: private EntityTypeImpl<?> getEntityType(String name) {
667: for (EntityType<?> type : metamodel.getEntities()) {
668: EntityTypeImpl<?> entityType = (EntityTypeImpl<?>) type;
669: if (entityType.getName().equals(name)) {
670: return entityType;
671: }
672: }
673: return null;
674: }
675:
676: private void setAllNodesIris(EntityType<?> entityType, SoqlNode node) {
677: if (entityType.getIdentifier().getName().equals(node.getValue())) {
678: return;
679: }
680: final Attribute<?, ?> abstractAttribute = entityType.getAttribute(node.getValue());
681: //not implemented case of 3 or more fragments (chained SoqlNodes)
682: node.setIri(abstractAttribute.getIRI().toString());
683: if (node.hasChild()) {
684: final Type<?> type = resolveBindableType(abstractAttribute);
685: if (type.getPersistenceType() != Type.PersistenceType.ENTITY) {
686: return;
687: }
688: setAllNodesIris((EntityType<?>) type, node.getChild());
689: }
690: }
691:
692: private static Type<?> resolveBindableType(Attribute<?, ?> att) {
693: if (att.isCollection()) {
694: return ((PluralAttribute<?, ?, ?>) att).getElementType();
695: } else {
696: return ((SingularAttribute<?, ?>) att).getType();
697: }
698: }
699:
700: private void setIris(SoqlNode firstNode) {
701: if (!objectTypes.containsKey(firstNode.getValue())) {
702: return;
703: }
704: String objectName = objectTypes.get(firstNode.getValue());
705: EntityTypeImpl<?> entityType = getEntityType(objectName);
706: if (entityType == null) {
707: return;
708: }
709: if (firstNode.hasChild()) {
710: setAllNodesIris(entityType, firstNode.getChild());
711: }
712: }
713:
714: public String getSparqlQuery() {
715: assert sparql != null;
716: return sparql;
717: }
718:
719: //Methods to build new Query
720: private void buildSparqlQueryString() {
721: if (attributes.isEmpty()) {
722: return;
723: }
724: StringBuilder newQueryBuilder = new StringBuilder(typeDef);
725: if (isSelectedParamCount) {
726: newQueryBuilder.append(getCountPart());
727: } else {
728: if (isSelectedParamDistinct) {
729: newQueryBuilder.append(' ').append(SoqlConstants.DISTINCT);
730: }
731: newQueryBuilder.append(' ').append(rootVariable).append(' ');
732: }
733: newQueryBuilder.append("WHERE { ");
734: newQueryBuilder.append(processSupremeAttributes());
735: if (!objectOfNextOr.isEmpty()) {
736: newQueryBuilder.append("{ ");
737: }
738: newQueryBuilder.append(processAttributes());
739: if (!objectOfNextOr.isEmpty()) {
740: newQueryBuilder.append("} ");
741: }
742: newQueryBuilder.append('}');
743: if (!groupAttributes.isEmpty()) {
744: newQueryBuilder.append(' ').append(buildGrouping());
745: }
746: if (!orderAttributes.isEmpty()) {
747: newQueryBuilder.append(' ').append(buildOrdering());
748: }
749: sparql = newQueryBuilder.toString();
750: LOG.trace("Translated SOQL query '{}' to SPARQL '{}'.", soql, sparql);
751: }
752:
753: private StringBuilder getCountPart() {
754: StringBuilder countPart = new StringBuilder(" (COUNT(");
755: if (isSelectedParamDistinct) {
756: countPart.append(SoqlConstants.DISTINCT).append(' ');
757: }
758: countPart.append(rootVariable).append(") AS ?count) ");
759: return countPart;
760: }
761:
762: private StringBuilder processSupremeAttributes() {
763: StringBuilder attributesPart = new StringBuilder();
764: final Iterator<SoqlAttribute> it = attributes.iterator();
765: while (it.hasNext()) {
766: final SoqlAttribute current = it.next();
767: if (current.isInstanceOf() || current.isOrderBy() || current.isGroupBy()) {
768: attributesPart.append(processAttribute(current));
769: it.remove();
770: }
771: }
772: return attributesPart;
773: }
774:
775: private StringBuilder processAttributes() {
776: StringBuilder attributesPart = new StringBuilder();
777: ArrayList<SoqlAttribute> toFilter = new ArrayList<>();
778: ArrayList<SoqlAttribute> toInvFilter = new ArrayList<>();
779: for (SoqlAttribute myAttr : attributes) {
780: if (objectOfNextOr.contains(myAttr)) {
781: StringBuilder orPart = new StringBuilder();
782: orPart.append(processAllFilters(toFilter, toInvFilter));
783: toFilter.clear();
784: toInvFilter.clear();
785: orPart.append("} UNION { ");
786: attributesPart.append(orPart);
787: }
788: if (myAttr.isNot()) {
789: toInvFilter.add(myAttr);
790: } else {
791: if (myAttr.requiresFilter()) {
792: toFilter.add(myAttr);
793: }
794: final String bgp = processAttribute(myAttr);
795: if (attributesPart.indexOf(bgp) == -1) {
796: attributesPart.append(bgp);
797: }
798: }
799: }
800: attributesPart.append(processAllFilters(toFilter, toInvFilter));
801: return attributesPart;
802: }
803:
804: private StringBuilder processAllFilters(ArrayList<SoqlAttribute> toFilter, ArrayList<SoqlAttribute> toInvFilter) {
805: StringBuilder part = new StringBuilder();
806: if (!toFilter.isEmpty()) {
807: part.append(processFilter(toFilter));
808: }
809: if (!toInvFilter.isEmpty()) {
810: part.append(processInvFilter(toInvFilter));
811: }
812: return part;
813: }
814:
815: private String processFilter(ArrayList<SoqlAttribute> toFilter) {
816: StringBuilder buildFilter = new StringBuilder();
817: if (toFilter.isEmpty()) {
818: return "";
819: }
820: buildFilter.append("FILTER (");
821: buildFilter.append(toFilter.stream().map(SoqlAttribute::getFilterExpressions).flatMap(Collection::stream)
822: .collect(Collectors.joining(" && ")));
823: buildFilter.append(") ");
824: return buildFilter.toString();
825: }
826:
827: private String processInvFilter(ArrayList<SoqlAttribute> toInvFilter) {
828: StringBuilder buildInvFilter = new StringBuilder();
829: ArrayList<SoqlAttribute> toFilter = new ArrayList<>();
830: if (toInvFilter.isEmpty()) {
831: return "";
832: }
833: buildInvFilter.append("FILTER NOT EXISTS { ");
834: for (SoqlAttribute attr : toInvFilter) {
835: buildInvFilter.append(processAttribute(attr));
836: if (attr.requiresFilter()) {
837: toFilter.add(attr);
838: }
839: }
840: buildInvFilter.append(processFilter(toFilter)).append("} ");
841: return buildInvFilter.toString();
842: }
843:
844: private String processAttribute(SoqlAttribute attr) {
845: return attr.getBasicGraphPattern(rootVariable);
846: }
847:
848: private String buildOrdering() {
849: StringBuilder sb = new StringBuilder("ORDER BY");
850: for (SoqlOrderParameter orderParam : orderAttributes) {
851: sb.append(' ').append(orderParam.getOrderByPart());
852: }
853: return sb.toString();
854: }
855:
856: private String buildGrouping() {
857: StringBuilder sb = new StringBuilder("GROUP BY");
858: for (SoqlGroupParameter groupParam : groupAttributes) {
859: sb.append(' ').append(groupParam.getGroupByPart());
860: }
861: return sb.toString();
862: }
863: }