1 package eu.fbk.knowledgestore.triplestore;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.concurrent.atomic.AtomicInteger;
9 import java.util.concurrent.atomic.AtomicReference;
10
11 import javax.annotation.Nullable;
12
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.ImmutableList;
15 import com.google.common.collect.Lists;
16 import com.google.common.collect.Maps;
17 import com.google.common.collect.Ordering;
18 import com.google.common.collect.Sets;
19
20 import org.openrdf.model.BNode;
21 import org.openrdf.model.Literal;
22 import org.openrdf.model.URI;
23 import org.openrdf.model.impl.URIImpl;
24 import org.openrdf.model.vocabulary.FN;
25 import org.openrdf.model.vocabulary.XMLSchema;
26 import org.openrdf.query.BindingSet;
27 import org.openrdf.query.Dataset;
28 import org.openrdf.query.QueryLanguage;
29 import org.openrdf.query.algebra.Add;
30 import org.openrdf.query.algebra.And;
31 import org.openrdf.query.algebra.ArbitraryLengthPath;
32 import org.openrdf.query.algebra.Avg;
33 import org.openrdf.query.algebra.BNodeGenerator;
34 import org.openrdf.query.algebra.BinaryValueOperator;
35 import org.openrdf.query.algebra.BindingSetAssignment;
36 import org.openrdf.query.algebra.Bound;
37 import org.openrdf.query.algebra.Clear;
38 import org.openrdf.query.algebra.Coalesce;
39 import org.openrdf.query.algebra.Compare;
40 import org.openrdf.query.algebra.Compare.CompareOp;
41 import org.openrdf.query.algebra.CompareAll;
42 import org.openrdf.query.algebra.CompareAny;
43 import org.openrdf.query.algebra.Copy;
44 import org.openrdf.query.algebra.Count;
45 import org.openrdf.query.algebra.Create;
46 import org.openrdf.query.algebra.Datatype;
47 import org.openrdf.query.algebra.DeleteData;
48 import org.openrdf.query.algebra.DescribeOperator;
49 import org.openrdf.query.algebra.Difference;
50 import org.openrdf.query.algebra.Distinct;
51 import org.openrdf.query.algebra.EmptySet;
52 import org.openrdf.query.algebra.Exists;
53 import org.openrdf.query.algebra.Extension;
54 import org.openrdf.query.algebra.ExtensionElem;
55 import org.openrdf.query.algebra.Filter;
56 import org.openrdf.query.algebra.FunctionCall;
57 import org.openrdf.query.algebra.Group;
58 import org.openrdf.query.algebra.GroupConcat;
59 import org.openrdf.query.algebra.GroupElem;
60 import org.openrdf.query.algebra.IRIFunction;
61 import org.openrdf.query.algebra.If;
62 import org.openrdf.query.algebra.In;
63 import org.openrdf.query.algebra.InsertData;
64 import org.openrdf.query.algebra.Intersection;
65 import org.openrdf.query.algebra.IsBNode;
66 import org.openrdf.query.algebra.IsLiteral;
67 import org.openrdf.query.algebra.IsNumeric;
68 import org.openrdf.query.algebra.IsResource;
69 import org.openrdf.query.algebra.IsURI;
70 import org.openrdf.query.algebra.Join;
71 import org.openrdf.query.algebra.Label;
72 import org.openrdf.query.algebra.Lang;
73 import org.openrdf.query.algebra.LangMatches;
74 import org.openrdf.query.algebra.LeftJoin;
75 import org.openrdf.query.algebra.Like;
76 import org.openrdf.query.algebra.ListMemberOperator;
77 import org.openrdf.query.algebra.Load;
78 import org.openrdf.query.algebra.LocalName;
79 import org.openrdf.query.algebra.MathExpr;
80 import org.openrdf.query.algebra.MathExpr.MathOp;
81 import org.openrdf.query.algebra.Max;
82 import org.openrdf.query.algebra.Min;
83 import org.openrdf.query.algebra.Modify;
84 import org.openrdf.query.algebra.Move;
85 import org.openrdf.query.algebra.MultiProjection;
86 import org.openrdf.query.algebra.Namespace;
87 import org.openrdf.query.algebra.Not;
88 import org.openrdf.query.algebra.Or;
89 import org.openrdf.query.algebra.Order;
90 import org.openrdf.query.algebra.OrderElem;
91 import org.openrdf.query.algebra.Projection;
92 import org.openrdf.query.algebra.ProjectionElem;
93 import org.openrdf.query.algebra.ProjectionElemList;
94 import org.openrdf.query.algebra.QueryModelNode;
95 import org.openrdf.query.algebra.QueryModelVisitor;
96 import org.openrdf.query.algebra.QueryRoot;
97 import org.openrdf.query.algebra.Reduced;
98 import org.openrdf.query.algebra.Regex;
99 import org.openrdf.query.algebra.SameTerm;
100 import org.openrdf.query.algebra.Sample;
101 import org.openrdf.query.algebra.Service;
102 import org.openrdf.query.algebra.SingletonSet;
103 import org.openrdf.query.algebra.Slice;
104 import org.openrdf.query.algebra.StatementPattern;
105 import org.openrdf.query.algebra.StatementPattern.Scope;
106 import org.openrdf.query.algebra.Str;
107 import org.openrdf.query.algebra.Sum;
108 import org.openrdf.query.algebra.TupleExpr;
109 import org.openrdf.query.algebra.UnaryTupleOperator;
110 import org.openrdf.query.algebra.Union;
111 import org.openrdf.query.algebra.ValueConstant;
112 import org.openrdf.query.algebra.ValueExpr;
113 import org.openrdf.query.algebra.Var;
114 import org.openrdf.query.algebra.ZeroLengthPath;
115 import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
116 import org.openrdf.query.parser.ParsedQuery;
117 import org.openrdf.queryrender.QueryRenderer;
118
119 final class SPARQLRenderer implements QueryRenderer {
120
121 private static final Map<String, String> NAMES;
122
123 static {
124 final Map<String, String> names = Maps.newHashMap();
125 names.put("RAND", "RAND");
126 names.put("TZ", "TZ");
127 names.put("NOW", "NOW");
128 names.put("UUID", "UUID");
129 names.put("STRUUID", "STRUUID");
130 names.put("MD5", "MD5");
131 names.put("SHA1", "SHA1");
132 names.put("SHA256", "SHA256");
133 names.put("SHA384", "SHA384");
134 names.put("SHA512", "SHA512");
135 names.put("STRLANG", "STRLANG");
136 names.put("STRDT", "STRDT");
137 names.put(FN.STRING_LENGTH.stringValue(), "STRLEN");
138 names.put(FN.SUBSTRING.stringValue(), "SUBSTR");
139 names.put(FN.UPPER_CASE.stringValue(), "UCASE");
140 names.put(FN.LOWER_CASE.stringValue(), "LCASE");
141 names.put(FN.STARTS_WITH.stringValue(), "STRSTARTS");
142 names.put(FN.ENDS_WITH.stringValue(), "STRENDS");
143 names.put(FN.CONTAINS.stringValue(), "CONTAINS");
144 names.put(FN.SUBSTRING_BEFORE.stringValue(), "STRBEFORE");
145 names.put(FN.SUBSTRING_AFTER.stringValue(), "STRAFTER");
146 names.put(FN.ENCODE_FOR_URI.stringValue(), "ENCODE_FOR_URI");
147 names.put(FN.CONCAT.stringValue(), "CONCAT");
148 names.put(FN.NAMESPACE + "matches", "REGEX");
149 names.put(FN.REPLACE.stringValue(), "REPLACE");
150 names.put(FN.NUMERIC_ABS.stringValue(), "ABS");
151 names.put(FN.NUMERIC_ROUND.stringValue(), "ROUND");
152 names.put(FN.NUMERIC_CEIL.stringValue(), "CEIL");
153 names.put(FN.NUMERIC_FLOOR.stringValue(), "FLOOR");
154 names.put(FN.YEAR_FROM_DATETIME.stringValue(), "YEAR");
155 names.put(FN.MONTH_FROM_DATETIME.stringValue(), "MONTH");
156 names.put(FN.DAY_FROM_DATETIME.stringValue(), "DAY");
157 names.put(FN.HOURS_FROM_DATETIME.stringValue(), "HOURS");
158 names.put(FN.MINUTES_FROM_DATETIME.stringValue(), "MINUTES");
159 names.put(FN.SECONDS_FROM_DATETIME.stringValue(), "SECONDS");
160 names.put(FN.TIMEZONE_FROM_DATETIME.stringValue(), "TIMEZONE");
161 NAMES = Collections.unmodifiableMap(names);
162
163 }
164
165 private final Map<String, String> prefixes;
166
167 private final boolean forceSelect;
168
169 public SPARQLRenderer(@Nullable final Map<String, String> prefixes,
170 @Nullable final boolean forceSelect) {
171 this.prefixes = prefixes != null ? prefixes : Collections.<String, String>emptyMap();
172 this.forceSelect = forceSelect;
173 }
174
175 @Override
176 public QueryLanguage getLanguage() {
177 return QueryLanguage.SPARQL;
178 }
179
180 @Override
181 public String render(final ParsedQuery query) {
182 return render(query.getTupleExpr(), query.getDataset());
183 }
184
185 public String render(final TupleExpr expr, final Dataset dataset) {
186 final Rendering rendering = new Rendering(expr, dataset);
187 final StringBuilder builder = new StringBuilder();
188 boolean newline = false;
189 if (!rendering.namespaces.isEmpty()) {
190 for (final String namespace : Ordering.natural().sortedCopy(rendering.namespaces)) {
191 final String prefix = this.prefixes.get(namespace);
192 if ("bif".equals(prefix) && "http://www.openlinksw.com/schema/sparql/extensions#" //
193 .equals(namespace)) {
194 continue;
195 }
196 builder.append("PREFIX ").append(prefix).append(": <");
197 escape(namespace, builder);
198 builder.append(">\n");
199 newline = true;
200 }
201 }
202 if (rendering.base != null) {
203 builder.append("BASE <");
204 escape(rendering.base, builder);
205 builder.append(">\n");
206 newline = true;
207 }
208 if (newline) {
209 builder.append("\n");
210 }
211 builder.append(rendering.body);
212 return builder.toString();
213 }
214
215
216
217 private static void escape(final String label, final StringBuilder builder) {
218 final int length = label.length();
219 for (int i = 0; i < length; ++i) {
220 final char c = label.charAt(i);
221 if (c == '\\') {
222 builder.append("\\\\");
223 } else if (c == '"') {
224 builder.append("\\\"");
225 } else if (c == '\n') {
226 builder.append("\\n");
227 } else if (c == '\r') {
228 builder.append("\\r");
229 } else if (c == '\t') {
230 builder.append("\\t");
231 }
232
233
234
235
236
237
238
239
240
241 else {
242 builder.append(Character.toString(c));
243 }
244 }
245 }
246
247 private static String sanitize(final String string) {
248 final int length = string.length();
249 final StringBuilder builder = new StringBuilder(length + 10);
250 for (int i = 0; i < length; ++i) {
251 final char ch = string.charAt(i);
252 if (Character.isLetter(ch) || ch == '_' || i > 0 && Character.isDigit(ch)) {
253 builder.append(ch);
254 } else {
255 builder.append("_");
256 }
257 }
258 return builder.toString();
259 }
260
261 private static <T> boolean equalOrNull(@Nullable final T first, @Nullable final T second) {
262 return first != null && first.equals(second) || first == null && second == null;
263 }
264
265 private static <T> T defaultIfNull(@Nullable final T value, @Nullable final T defaultValue) {
266 return value != null ? value : defaultValue;
267 }
268
269 private static List<StatementPattern> getBGP(final QueryModelNode n) {
270 if (n instanceof StatementPattern) {
271 return Collections.singletonList((StatementPattern) n);
272 } else if (!(n instanceof Join)) {
273 return null;
274 }
275 final Join j = (Join) n;
276 final List<StatementPattern> l = getBGP(j.getLeftArg());
277 final List<StatementPattern> r = getBGP(j.getRightArg());
278 if (l == null || r == null) {
279 return null;
280 }
281 if (l.isEmpty()) {
282 return r;
283 } else if (r.isEmpty()) {
284 return l;
285 } else if (!equalOrNull(l.get(0).getContextVar(), r.get(0).getContextVar())) {
286 return null;
287 } else {
288 final List<StatementPattern> s = new ArrayList<StatementPattern>(l.size() + r.size());
289 s.addAll(l);
290 s.addAll(r);
291 return s;
292 }
293 }
294
295 private static int getVarRefs(final QueryModelNode node, final String name) {
296 final AtomicInteger count = new AtomicInteger(0);
297 node.visit(new QueryModelVisitorBase<RuntimeException>() {
298
299 @Override
300 public void meet(final Var var) {
301 if (var.getName().equals(name)) {
302 count.set(count.get() + 1);
303 }
304 }
305
306 });
307 return count.get();
308 }
309
310 private static ValueExpr getVarExpr(final QueryModelNode node, final String name) {
311 final AtomicReference<ValueExpr> result = new AtomicReference<ValueExpr>(null);
312 node.visit(new QueryModelVisitorBase<RuntimeException>() {
313
314 @Override
315 protected void meetNode(final QueryModelNode node) throws RuntimeException {
316 if (result.get() == null) {
317 super.meetNode(node);
318 }
319 }
320
321 @Override
322 public void meet(final Var var) {
323 if (var.getName().equals(name) && var.getValue() != null) {
324 result.set(new ValueConstant(var.getValue()));
325 }
326 }
327
328 @Override
329 public void meet(final ExtensionElem node) throws RuntimeException {
330 if (node.getName().equals(name)) {
331 result.set(node.getExpr());
332 } else {
333 super.meet(node);
334 }
335 }
336
337 });
338 return result.get();
339 }
340
341 private final class Rendering implements QueryModelVisitor<RuntimeException> {
342
343 final TupleExpr root;
344
345 @Nullable
346 final Dataset dataset;
347
348 final String body;
349
350 String base;
351
352 private final StringBuilder builder;
353
354 private final Set<String> namespaces;
355
356 private int indent;
357
358 private Rendering(final TupleExpr node, @Nullable final Dataset dataset) {
359 this.root = new QueryRoot(Preconditions.checkNotNull(node));
360 this.dataset = dataset;
361 this.builder = new StringBuilder();
362 this.namespaces = Sets.newHashSet();
363 this.indent = 0;
364 emit(Query.create(this.root, this.dataset, SPARQLRenderer.this.forceSelect));
365 this.body = this.builder.toString();
366 this.builder.setLength(0);
367 }
368
369
370
371 private Rendering emitIf(final boolean condition, final Object object) {
372 if (condition) {
373 emit(object);
374 }
375 return this;
376 }
377
378 private Rendering emit(final Iterable<?> values, final String separator) {
379 boolean first = true;
380 for (final Object value : values) {
381 if (!first) {
382 emit(separator);
383 }
384 emit(value);
385 first = false;
386 }
387 return this;
388 }
389
390 @SuppressWarnings("unchecked")
391 private Rendering emit(final Object value) {
392 if (value instanceof String) {
393 return emit((String) value);
394 } else if (value instanceof QueryModelNode) {
395 emit((QueryModelNode) value);
396 } else if (value instanceof BNode) {
397 emit((BNode) value);
398 } else if (value instanceof URI) {
399 emit((URI) value);
400 } else if (value instanceof Literal) {
401 emit((Literal) value);
402 } else if (value instanceof List<?>) {
403 emit((List<StatementPattern>) value);
404 } else if (value instanceof Query) {
405 emit((Query) value);
406 }
407 return this;
408 }
409
410 private Rendering emit(final String string) {
411 this.builder.append(string);
412 return this;
413 }
414
415 private Rendering emit(final Literal literal) {
416 if (XMLSchema.INTEGER.equals(literal.getDatatype())) {
417 this.builder.append(literal.getLabel());
418 } else {
419 this.builder.append("\"");
420 escape(literal.getLabel(), this.builder);
421 this.builder.append("\"");
422
423 if (literal.getDatatype() != null && !literal.getDatatype().equals(XMLSchema.STRING)) {
424 this.builder.append("^^");
425 emit(literal.getDatatype());
426 } else if (literal.getLanguage() != null) {
427 this.builder.append("@").append(literal.getLanguage());
428 }
429 }
430 return this;
431 }
432
433 private Rendering emit(final BNode bnode) {
434 this.builder.append("_:").append(bnode.getID());
435 return this;
436 }
437
438 private Rendering emit(final URI uri) {
439 if (uri.getNamespace().equals("http://www.openlinksw.com/schema/sparql/extensions#")) {
440 this.builder.append("bif:").append(uri.getLocalName());
441 } else {
442 final String prefix = SPARQLRenderer.this.prefixes.get(uri.getNamespace());
443 if (prefix != null) {
444 if (this.namespaces != null) {
445 this.namespaces.add(uri.getNamespace());
446 }
447 this.builder.append(prefix).append(':').append(uri.getLocalName());
448 } else {
449 this.builder.append("<");
450 escape(uri.toString(), this.builder);
451 this.builder.append(">");
452 }
453 }
454 return this;
455 }
456
457 private Rendering emit(final List<StatementPattern> bgp) {
458 if (bgp.isEmpty()) {
459 return this;
460 }
461 final Var c = bgp.get(0).getContextVar();
462 if (c != null) {
463 emit("GRAPH ").emit(c).emit(" ").openBrace();
464 }
465 StatementPattern l = null;
466 for (final StatementPattern n : bgp) {
467 final Var s = n.getSubjectVar();
468 final Var p = n.getPredicateVar();
469 final Var o = n.getObjectVar();
470 if (l == null) {
471 emit(s).emit(" ").emit(p).emit(" ").emit(o);
472 } else if (!l.getSubjectVar().equals(n.getSubjectVar())) {
473 emit(" .").newline().emit(s).emit(" ").emit(p).emit(" ").emit(o);
474 } else if (!l.getPredicateVar().equals(n.getPredicateVar())) {
475 emit(" ;").newline().emit("\t").emit(p).emit(" ").emit(o);
476 } else if (!l.getObjectVar().equals(n.getObjectVar())) {
477 emit(" , ").emit(o);
478 }
479 l = n;
480 }
481 emit(" .");
482 if (c != null) {
483 closeBrace();
484 }
485 return this;
486 }
487
488 private Rendering emit(final Query query) {
489 if (query.root != this.root) {
490 openBrace();
491 }
492 if (query.form == Form.ASK) {
493 emit("ASK");
494 } else if (query.form == Form.CONSTRUCT) {
495 emit("CONSTRUCT ").openBrace().emit(query.construct).closeBrace();
496 } else if (query.form == Form.DESCRIBE) {
497 emit("DESCRIBE");
498 for (final ProjectionElem p : query.select) {
499 final ExtensionElem e = p.getSourceExpression();
500 emit(" ").emit(
501 e != null && e.getExpr() instanceof ValueConstant ? e.getExpr() : p);
502 }
503 } else if (query.form == Form.SELECT) {
504 emit("SELECT");
505 if (query.modifier != null) {
506 emit(" ").emit(query.modifier.toString().toUpperCase());
507 }
508 if (query.select.isEmpty()) {
509 int count = 0;
510 for (final String var : query.where.getBindingNames()) {
511 final ValueExpr expr = getVarExpr(query.where, var);
512 if (!var.startsWith("-")) {
513 if (expr == null) {
514 emit(" ?").emit(var);
515 } else {
516 emit(" (").emit(expr).emit(" AS ?").emit(var).emit(")");
517 }
518 ++count;
519 }
520 }
521 if (count == 0) {
522 emit(" *");
523 }
524 } else {
525 emit(" ").emit(query.select, " ");
526 }
527 }
528 if (query.from != null) {
529 for (final URI uri : query.from.getDefaultGraphs()) {
530 newline().emit("FROM ").emit(uri);
531 }
532 for (final URI uri : query.from.getNamedGraphs()) {
533 newline().emit("FROM NAMED ").emit(uri);
534 }
535 }
536 if (query.form != Form.DESCRIBE || !(query.where instanceof SingletonSet)) {
537 newline().emit("WHERE ").openBrace().emit(query.where).closeBrace();
538 }
539 if (!query.groupBy.isEmpty()) {
540 newline().emit("GROUP BY");
541 for (final ProjectionElem n : query.groupBy) {
542 emit(" ?").emit(n.getTargetName());
543 }
544 }
545 if (query.having != null) {
546 newline().emit("HAVING (").emit(query.having).emit(")");
547 }
548 if (!query.orderBy.isEmpty()) {
549 newline().emit("ORDER BY ").emit(query.orderBy, " ");
550 }
551 if (query.form != Form.ASK) {
552 if (query.offset != null) {
553 newline().emit("OFFSET " + query.offset);
554 }
555 if (query.limit != null) {
556 newline().emit("LIMIT " + query.limit);
557
558 }
559 }
560 if (query.root != this.root) {
561 closeBrace();
562 }
563 return this;
564 }
565
566 private Rendering emit(final QueryModelNode n) {
567 final QueryModelNode p = n.getParentNode();
568 final boolean braces = n instanceof TupleExpr && p != null
569 && !(p instanceof TupleExpr);
570 if (braces) {
571 openBrace();
572 }
573 n.visit(this);
574 if (braces) {
575 closeBrace();
576 }
577 return this;
578 }
579
580 private Rendering emit(final QueryModelNode node, final boolean parenthesis) {
581 if (parenthesis) {
582 if (node instanceof TupleExpr) {
583 openBrace();
584 } else {
585 emit("(");
586 }
587 }
588 emit(node);
589 if (parenthesis) {
590 if (node instanceof TupleExpr) {
591 closeBrace();
592 } else {
593 emit(")");
594 }
595 }
596 return this;
597 }
598
599 private Rendering openBrace() {
600 emit("{");
601 ++this.indent;
602 newline();
603 return this;
604 }
605
606 private Rendering closeBrace() {
607 --this.indent;
608 newline();
609 emit("}");
610 return this;
611 }
612
613 private Rendering newline() {
614 emit("\n");
615 for (int i = 0; i < this.indent; ++i) {
616 emit("\t");
617 }
618 return this;
619 }
620
621 private Rendering fail(final String message, final QueryModelNode node) {
622 throw new IllegalArgumentException("SPARQL rendering failed. " + message
623 + (node == null ? "null" : node.getClass().getSimpleName() + "\n" + node));
624 }
625
626
627
628 @Override
629 public void meet(final OrderElem n) {
630 emit(n.isAscending() ? "ASC(" : "DESC(").emit(n.getExpr()).emit(")");
631 }
632
633 @Override
634 public void meet(final ProjectionElemList node) {
635 emit(node.getElements(), " ");
636 }
637
638 @Override
639 public void meet(final ProjectionElem n) {
640
641 final String source = n.getSourceName();
642 final String target = n.getTargetName();
643 final ValueExpr expr = n.getSourceExpression() == null ? null : n
644 .getSourceExpression().getExpr();
645
646 if (target.startsWith("-")) {
647 if (expr != null) {
648 emit("(").emit(expr).emit(" AS ?").emit(sanitize(target)).emit(")");
649 }
650 } else if (expr != null) {
651 emit("(").emit(expr).emit(" AS ?").emit(target).emit(")");
652 } else if (!equalOrNull(source, target)) {
653 emit("(?").emit(source).emit(" AS ?").emit(target).emit(")");
654 } else {
655 emit("?").emit(target);
656 }
657 }
658
659 @Override
660 public void meet(final GroupElem n) {
661 final ProjectionElem e = new ProjectionElem();
662 e.setTargetName(n.getName());
663 e.setSourceName(n.getName());
664 e.setSourceExpression(new ExtensionElem(n.getOperator(), n.getName()));
665 meet(e);
666 }
667
668 @Override
669 public void meet(final DescribeOperator n) {
670 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
671 }
672
673 @Override
674 public void meet(final QueryRoot n) {
675 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
676 }
677
678 @Override
679 public void meet(final Projection n) {
680 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
681 }
682
683 @Override
684 public void meet(final MultiProjection n) {
685 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
686 }
687
688 @Override
689 public void meet(final Distinct n) {
690 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
691 }
692
693 @Override
694 public void meet(final Reduced n) {
695 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
696 }
697
698 @Override
699 public void meet(final Group n) {
700 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
701 }
702
703 @Override
704 public void meet(final Order n) {
705 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
706 }
707
708 @Override
709 public void meet(final Slice n) {
710 emit(Query.create(n, null, SPARQLRenderer.this.forceSelect));
711 }
712
713
714
715 @Override
716 public void meet(final EmptySet n) {
717 final QueryModelNode p = n.getParentNode();
718 if (p.getParentNode() != null && !(p.getParentNode() instanceof QueryRoot)) {
719 throw new IllegalArgumentException(
720 "Cannot translate EmptySet inside the body of a query / update operation");
721 }
722 emit("CONSTRUCT {} WHERE {}");
723 }
724
725 @Override
726 public void meet(final SingletonSet n) {
727
728 }
729
730 @Override
731 public void meet(final BindingSetAssignment n) {
732
733 final Set<String> names = n.getBindingNames();
734
735 if (names.isEmpty()) {
736 emit("VALUES {}");
737
738 } else if (names.size() == 1) {
739 final String name = names.iterator().next();
740 emit("VALUES ?").emit(name).emit(" ").openBrace();
741 boolean first = true;
742 for (final BindingSet bindings : n.getBindingSets()) {
743 emitIf(!first, " ").emit(defaultIfNull(bindings.getValue(name), "UNDEF"));
744 first = false;
745 }
746 closeBrace();
747
748 } else {
749 emit("VALUES (?").emit(names, " ?").emit(") ").openBrace();
750 boolean firstBinding = true;
751 for (final BindingSet bindings : n.getBindingSets()) {
752 if (!firstBinding) {
753 newline();
754 }
755 emit("(");
756 boolean first = true;
757 for (final String name : names) {
758 emitIf(!first, " ").emit(defaultIfNull(bindings.getValue(name), "UNDEF"));
759 first = false;
760 }
761 emit(")");
762 firstBinding = false;
763 }
764 closeBrace();
765 }
766 }
767
768 @Override
769 public void meet(final StatementPattern n) {
770 emit(getBGP(n));
771 }
772
773
774
775 @Override
776 public void meet(final Extension n) {
777 emit(n.getArg());
778 if (!(n.getArg() instanceof SingletonSet)) {
779 newline();
780 }
781 boolean first = true;
782 for (final ExtensionElem e : n.getElements()) {
783 final ValueExpr expr = e.getExpr();
784 if (!(expr instanceof Var) || !((Var) expr).getName().equals(e.getName())) {
785 if (!first) {
786 newline();
787 }
788 emit("BIND (").emit(expr).emit(" AS ?").emit(e.getName()).emit(")");
789 first = false;
790 }
791 }
792 }
793
794 @Override
795 public void meet(final ExtensionElem n) {
796 throw new Error("Should not be directly called");
797 }
798
799 @Override
800 public void meet(final Filter n) {
801 final ValueExpr cond = n.getCondition();
802 final boolean nopar = cond instanceof Exists || cond instanceof Not
803 && ((Not) cond).getArg() instanceof Exists;
804 emit(n.getArg());
805 if (!(n.getArg() instanceof SingletonSet)) {
806 newline();
807 }
808 emit("FILTER ").emit(cond, !nopar);
809 }
810
811 @Override
812 public void meet(final Service n) {
813 newline().emit("SERVICE ").emitIf(n.isSilent(), "SILENT ").openBrace()
814 .emit(n.getServiceExpr()).closeBrace().emit(" ").emit(n.getServiceRef());
815 }
816
817
818
819 @Override
820 public void meet(final Join n) {
821 final List<StatementPattern> bgp = getBGP(n);
822 if (bgp != null) {
823 emit(bgp);
824 } else {
825 final TupleExpr l = n.getLeftArg();
826 final TupleExpr r = n.getRightArg();
827 final boolean norpar = r instanceof Join || r instanceof StatementPattern
828 || r instanceof SingletonSet || r instanceof Service || r instanceof Union
829 || r instanceof BindingSetAssignment || r instanceof ArbitraryLengthPath;
830 emit(l).newline().emit(r, !norpar);
831 }
832 }
833
834 @Override
835 public void meet(final LeftJoin n) {
836 final TupleExpr l = n.getLeftArg();
837 final TupleExpr r = n.getCondition() == null ? n.getRightArg() :
838 new Filter(n.getRightArg(), n.getCondition());
839 emit(l);
840 if (!(l instanceof SingletonSet)) {
841 newline();
842 }
843 emit("OPTIONAL ").emit(r, true);
844 }
845
846 @Override
847 public void meet(final Union n) {
848 final TupleExpr l = n.getLeftArg();
849 final TupleExpr r = n.getRightArg();
850 final ZeroLengthPath p = l instanceof ZeroLengthPath ? (ZeroLengthPath) l
851 : r instanceof ZeroLengthPath ? (ZeroLengthPath) r : null;
852 if (p == null) {
853 emit(l, !(l instanceof Union)).emit(" UNION ").emit(r, !(r instanceof Union));
854 } else {
855 final Var s = p.getSubjectVar();
856 final Var o = p.getObjectVar();
857 final Var c = p.getContextVar();
858 if (c != null) {
859 emit("GRAPH ").emit(c).emit(" ").openBrace();
860 }
861 emit(s).emit(" ").emitPropertyPath(n, s, o).emit(" ").emit(o);
862 if (c != null) {
863 closeBrace();
864 }
865 }
866 }
867
868 @Override
869 public void meet(final Difference n) {
870 final TupleExpr l = n.getLeftArg();
871 final TupleExpr r = n.getRightArg();
872 emit(l, true).emit(" MINUS ").emit(r, true);
873 }
874
875
876
877 @Override
878 public void meet(final ArbitraryLengthPath n) {
879 final Var s = n.getSubjectVar();
880 final Var o = n.getObjectVar();
881 final Var c = n.getContextVar();
882 if (c != null) {
883 emit("GRAPH ").emit(c).openBrace();
884 }
885 emit(s).emit(" ").emitPropertyPath(n, s, o).emit(" ").emit(o).emit(" .");
886 if (c != null) {
887 closeBrace();
888 }
889 }
890
891 @Override
892 public void meet(final ZeroLengthPath node) {
893 throw new Error("Should not be directly called");
894 }
895
896 private Rendering emitPropertyPath(final TupleExpr node, final Var start, final Var end) {
897
898
899
900 final boolean parenthesis = !(node instanceof StatementPattern)
901 && (node.getParentNode() instanceof ArbitraryLengthPath || node
902 .getParentNode() instanceof Union);
903
904 emitIf(parenthesis, "(");
905
906 if (node instanceof StatementPattern) {
907
908 final StatementPattern pattern = (StatementPattern) node;
909 final boolean inverse = isInversePath(pattern, start, end);
910 if (!pattern.getPredicateVar().hasValue()
911 || !pattern.getPredicateVar().isAnonymous()) {
912 fail("Unsupported path expression. Check node: ", node);
913 }
914 emitIf(inverse, "^").emit(pattern.getPredicateVar().getValue());
915
916 } else if (node instanceof Join) {
917 final Join j = (Join) node;
918 final TupleExpr l = j.getLeftArg();
919 final TupleExpr r = j.getRightArg();
920 final StatementPattern s = l instanceof StatementPattern ? (StatementPattern) l
921 : r instanceof StatementPattern ? (StatementPattern) r : null;
922 if (s == null) {
923 return fail("Cannot process property path", node);
924 }
925 final Var m = s.getSubjectVar().equals(start) || s.getSubjectVar().equals(end) ? s
926 .getObjectVar() : s.getSubjectVar();
927 emitPropertyPath(l, start, m);
928 emit("/");
929 emitPropertyPath(r, m, end);
930
931 } else if (node instanceof ArbitraryLengthPath) {
932
933 final ArbitraryLengthPath path = (ArbitraryLengthPath) node;
934 Preconditions.checkArgument(path.getMinLength() <= 1, "Invalid path length");
935 emitPropertyPath(path.getPathExpression(), start, end).emit(
936 path.getMinLength() == 0 ? "*" : "+");
937
938 } else if (node instanceof Union) {
939
940 final Union union = (Union) node;
941 if (union.getLeftArg() instanceof ZeroLengthPath) {
942 emitPropertyPath(union.getRightArg(), start, end).emit("?");
943 } else if (union.getRightArg() instanceof ZeroLengthPath) {
944 emitPropertyPath(union.getLeftArg(), start, end).emit("?");
945 } else {
946 emitPropertyPath(union.getLeftArg(), start, end);
947 emit("|");
948 emitPropertyPath(union.getRightArg(), start, end);
949 }
950
951 } else if (node instanceof Filter) {
952
953 final Filter filter = (Filter) node;
954
955 Preconditions.checkArgument(filter.getArg() instanceof StatementPattern);
956 final StatementPattern pattern = (StatementPattern) filter.getArg();
957 final boolean inverse = isInversePath(pattern, start, end);
958 Preconditions.checkArgument(!pattern.getPredicateVar().hasValue()
959 && pattern.getPredicateVar().isAnonymous());
960
961 final Set<URI> negatedProperties = Sets.newLinkedHashSet();
962 extractNegatedProperties(filter.getCondition(), negatedProperties);
963
964 if (negatedProperties.size() == 1) {
965 emit("!").emitIf(inverse, "^").emit(negatedProperties.iterator().next());
966
967 } else {
968 emit("!(");
969 boolean first = true;
970 for (final URI negatedProperty : negatedProperties) {
971 emitIf(!first, "|").emitIf(inverse, "^").emit(negatedProperty);
972 first = false;
973 }
974 emit(")");
975 }
976
977 } else {
978 fail("Unsupported path expression node", node);
979 }
980
981 return emitIf(parenthesis, ")");
982 }
983
984 private void extractNegatedProperties(final ValueExpr condition,
985 final Set<URI> negatedProperties) {
986 if (condition instanceof And) {
987 final And and = (And) condition;
988 extractNegatedProperties(and.getLeftArg(), negatedProperties);
989 extractNegatedProperties(and.getRightArg(), negatedProperties);
990
991 } else if (condition instanceof Compare) {
992 final Compare compare = (Compare) condition;
993 Preconditions.checkArgument(compare.getOperator() == CompareOp.NE);
994 if (compare.getLeftArg() instanceof ValueConstant) {
995 Preconditions.checkArgument(compare.getRightArg() instanceof Var);
996 negatedProperties.add((URI) ((ValueConstant) compare.getLeftArg()).getValue());
997 } else if (compare.getRightArg() instanceof ValueConstant) {
998 Preconditions.checkArgument(compare.getLeftArg() instanceof Var);
999 negatedProperties
1000 .add((URI) ((ValueConstant) compare.getRightArg()).getValue());
1001 } else {
1002 fail("Unsupported path expression. Check condition node: ", condition);
1003 }
1004 }
1005 }
1006
1007 private boolean isInversePath(final StatementPattern node, final Var start, final Var end) {
1008 if (node.getSubjectVar().equals(start)) {
1009 Preconditions.checkArgument(node.getObjectVar().equals(end));
1010 return false;
1011 } else if (node.getObjectVar().equals(start)) {
1012 Preconditions.checkArgument(node.getSubjectVar().equals(end));
1013 return true;
1014 } else {
1015 fail("Unsupported path expression. Check node: ", node);
1016 return false;
1017 }
1018 }
1019
1020
1021
1022 @Override
1023 public void meet(final Intersection n) {
1024 fail("Not a SPARQL 1.1 node", n);
1025 }
1026
1027
1028
1029 @Override
1030 public void meet(final Add add) {
1031 throw new UnsupportedOperationException();
1032 }
1033
1034 @Override
1035 public void meet(final Clear clear) {
1036 throw new UnsupportedOperationException();
1037 }
1038
1039 @Override
1040 public void meet(final Copy copy) {
1041 throw new UnsupportedOperationException();
1042 }
1043
1044 @Override
1045 public void meet(final Create create) {
1046 throw new UnsupportedOperationException();
1047 }
1048
1049 @Override
1050 public void meet(final DeleteData deleteData) {
1051 throw new UnsupportedOperationException();
1052 }
1053
1054 @Override
1055 public void meet(final InsertData insertData) {
1056 throw new UnsupportedOperationException();
1057 }
1058
1059 @Override
1060 public void meet(final Load load) {
1061 throw new UnsupportedOperationException();
1062 }
1063
1064 @Override
1065 public void meet(final Modify modify) {
1066 throw new UnsupportedOperationException();
1067 }
1068
1069 @Override
1070 public void meet(final Move move) {
1071 throw new UnsupportedOperationException();
1072 }
1073
1074
1075
1076
1077
1078 @Override
1079 public void meet(final ValueConstant n) {
1080 emit(n.getValue());
1081 }
1082
1083 @Override
1084 public void meet(final Var n) {
1085 final String name = n.getName();
1086 if (n.getValue() != null) {
1087 emit(n.getValue());
1088 } else if (!n.isAnonymous()) {
1089 emit("?" + n.getName());
1090 } else {
1091 final ValueExpr expr = getVarExpr(this.root, n.getName());
1092 if (expr != null) {
1093 emit(expr);
1094 } else if (getVarRefs(this.root, n.getName()) <= 1) {
1095 emit("[]");
1096 } else {
1097 emit("?").emit(sanitize(name));
1098 }
1099 }
1100 }
1101
1102
1103
1104 @Override
1105 public void meet(final Compare n) {
1106 final QueryModelNode p = n.getParentNode();
1107 final boolean par = p instanceof Not || p instanceof MathExpr;
1108 emitIf(par, "(").emit(n.getLeftArg()).emit(" ").emit(n.getOperator().getSymbol())
1109 .emit(" ").emit(n.getRightArg()).emitIf(par, ")");
1110 }
1111
1112 @Override
1113 public void meet(final ListMemberOperator n) {
1114 final QueryModelNode p = n.getParentNode();
1115 final boolean par = p instanceof Not || p instanceof MathExpr;
1116 final List<ValueExpr> args = n.getArguments();
1117 emitIf(par, "(").emit(args.get(0)).emit(" in (")
1118 .emit(args.subList(1, args.size()), ", ").emit(")").emitIf(par, ")");
1119 }
1120
1121 @Override
1122 public void meet(final MathExpr n) {
1123 final QueryModelNode p = n.getParentNode();
1124 final MathOp op = n.getOperator();
1125 final MathOp pop = p instanceof MathExpr ? ((MathExpr) p).getOperator() : null;
1126 final boolean r = p instanceof BinaryValueOperator
1127 && n == ((BinaryValueOperator) p).getRightArg();
1128 final boolean par = p instanceof Not
1129 || (op == MathOp.PLUS || op == MathOp.MINUS)
1130 && (pop == MathOp.MINUS && r
1131 || pop == MathOp.DIVIDE || pop == MathOp.MULTIPLY)
1132 || (op == MathOp.MULTIPLY || op == MathOp.DIVIDE) && pop == MathOp.DIVIDE && r;
1133 emitIf(par, "(").emit(n.getLeftArg()).emit(" ").emit(op.getSymbol()).emit(" ")
1134 .emit(n.getRightArg()).emitIf(par, ")");
1135 }
1136
1137 @Override
1138 public void meet(final And n) {
1139 final QueryModelNode p = n.getParentNode();
1140 final boolean needPar = p instanceof Not || p instanceof MathExpr
1141 || p instanceof ListMemberOperator || p instanceof Compare;
1142 emitIf(needPar, "(").emit(n.getLeftArg()).emit(" && ").emit(n.getRightArg())
1143 .emitIf(needPar, ")");
1144 }
1145
1146 @Override
1147 public void meet(final Or n) {
1148 final QueryModelNode p = n.getParentNode();
1149 final boolean needPar = p instanceof Not || p instanceof And || p instanceof MathExpr
1150 || p instanceof ListMemberOperator || p instanceof Compare;
1151 emitIf(needPar, "(").emit(n.getLeftArg()).emit(" || ").emit(n.getRightArg())
1152 .emitIf(needPar, ")");
1153 }
1154
1155 @Override
1156 public void meet(final Not n) {
1157 final String op = n.getArg() instanceof Exists ? "NOT " : "!";
1158 emit(op).emit(n.getArg());
1159 }
1160
1161
1162
1163 @Override
1164 public void meet(final Count node) {
1165 emit("COUNT(").emitIf(node.isDistinct(), "DISTINCT ")
1166 .emit(defaultIfNull(node.getArg(), "*")).emit(")");
1167 }
1168
1169 @Override
1170 public void meet(final Sum node) {
1171 emit("SUM(").emitIf(node.isDistinct(), "DISTINCT ").emit(node.getArg()).emit(")");
1172 }
1173
1174 @Override
1175 public void meet(final Min node) {
1176 emit("MIN(").emitIf(node.isDistinct(), "DISTINCT ").emit(node.getArg()).emit(")");
1177 }
1178
1179 @Override
1180 public void meet(final Max node) {
1181 emit("MAX(").emitIf(node.isDistinct(), "DISTINCT ").emit(node.getArg()).emit(")");
1182 }
1183
1184 @Override
1185 public void meet(final Avg node) {
1186 emit("AVG(").emitIf(node.isDistinct(), "DISTINCT ").emit(node.getArg()).emit(")");
1187 }
1188
1189 @Override
1190 public void meet(final Sample node) {
1191 emit("SAMPLE(").emitIf(node.isDistinct(), "DISTINCT ").emit(node.getArg()).emit(")");
1192 }
1193
1194 @Override
1195 public void meet(final GroupConcat node) {
1196 emit("GROUP_CONCAT(").emitIf(node.isDistinct(), "DISTINCT ").emit(node.getArg());
1197 if (node.getSeparator() != null) {
1198 emit(" ; separator=").emit(node.getSeparator());
1199 }
1200 emit(")");
1201 }
1202
1203
1204
1205 @Override
1206 public void meet(final Str n) {
1207 emit("STR(").emit(n.getArg()).emit(")");
1208 }
1209
1210 @Override
1211 public void meet(final Lang n) {
1212 emit("LANG(").emit(n.getArg()).emit(")");
1213 }
1214
1215 @Override
1216 public void meet(final LangMatches n) {
1217 emit("LANGMATCHES(").emit(n.getLeftArg()).emit(", ").emit(n.getRightArg()).emit(")");
1218 }
1219
1220 @Override
1221 public void meet(final Datatype n) {
1222 emit("DATATYPE(").emit(n.getArg()).emit(")");
1223 }
1224
1225 @Override
1226 public void meet(final Bound n) {
1227 emit("BOUND(").emit(n.getArg()).emit(")");
1228 }
1229
1230 @Override
1231 public void meet(final IRIFunction n) {
1232 emit("IRI(").emit(n.getArg()).emit(")");
1233 if (n.getBaseURI() != null) {
1234 this.base = n.getBaseURI();
1235 }
1236 }
1237
1238 @Override
1239 public void meet(final BNodeGenerator n) {
1240 final ValueExpr expr = n.getNodeIdExpr();
1241 emit("BNODE(").emitIf(expr != null, expr).emit(")");
1242 }
1243
1244 @Override
1245 public void meet(final FunctionCall n) {
1246 final String uri = n.getURI();
1247 String name = NAMES.get(uri);
1248 if (name == null && NAMES.values().contains(uri.toUpperCase())) {
1249 name = n.getURI().toUpperCase();
1250 }
1251 emit(name != null ? name : new URIImpl(uri)).emit("(").emit(n.getArgs(), ", ")
1252 .emit(")");
1253 }
1254
1255 @Override
1256 public void meet(final Coalesce n) {
1257 emit("COALESCE(").emit(n.getArguments(), ", ").emit(")");
1258 }
1259
1260 @Override
1261 public void meet(final If n) {
1262 emit("IF(").emit(n.getCondition()).emit(", ").emit(n.getResult()).emit(", ")
1263 .emit(n.getAlternative()).emit(")");
1264 }
1265
1266 @Override
1267 public void meet(final SameTerm n) {
1268 emit("sameTerm(").emit(n.getLeftArg()).emit(", ").emit(n.getRightArg()).emit(")");
1269 }
1270
1271 @Override
1272 public void meet(final IsURI n) {
1273 emit("isIRI(").emit(n.getArg()).emit(")");
1274 }
1275
1276 @Override
1277 public void meet(final IsBNode n) {
1278 emit("isBLANK(").emit(n.getArg()).emit(")");
1279 }
1280
1281 @Override
1282 public void meet(final IsLiteral n) {
1283 emit("isLITERAL(").emit(n.getArg()).emit(")");
1284 }
1285
1286 @Override
1287 public void meet(final IsNumeric n) {
1288 emit("isNUMERIC(").emit(n.getArg()).emit(")");
1289 }
1290
1291 @Override
1292 public void meet(final Regex n) {
1293 emit("REGEX(").emit(n.getArg()).emit(", ").emit(n.getPatternArg());
1294 if (n.getFlagsArg() != null) {
1295 emit(", ").emit(n.getFlagsArg());
1296 }
1297 emit(")");
1298 }
1299
1300 @Override
1301 public void meet(final Exists node) {
1302 emit("EXISTS ").emit(node.getSubQuery());
1303 }
1304
1305
1306
1307 @Override
1308 public void meet(final IsResource n) {
1309 fail("Not a SPARQL 1.1 node", n);
1310 }
1311
1312 @Override
1313 public void meet(final Label n) {
1314 fail("Not a SPARQL 1.1 node", n);
1315 }
1316
1317 @Override
1318 public void meet(final Like n) {
1319 fail("Not a SPARQL 1.1 node", n);
1320 }
1321
1322 @Override
1323 public void meet(final LocalName n) {
1324 fail("Not a SPARQL 1.1 node", n);
1325 }
1326
1327 @Override
1328 public void meet(final Namespace n) {
1329 fail("Not a SPARQL 1.1 node", n);
1330 }
1331
1332 @Override
1333 public void meet(final In n) {
1334 fail("Not a SPARQL 1.1 node", n);
1335 }
1336
1337 @Override
1338 public void meet(final CompareAll n) {
1339 fail("Not a SPARQL 1.1 node", n);
1340 }
1341
1342 @Override
1343 public void meet(final CompareAny n) {
1344 fail("Not a SPARQL 1.1 node", n);
1345 }
1346
1347
1348
1349 @Override
1350 public void meetOther(final QueryModelNode n) {
1351 fail("Unknown node", n);
1352 }
1353
1354 }
1355
1356 private enum Form {
1357 SELECT, CONSTRUCT, ASK, DESCRIBE
1358 }
1359
1360 private enum Modifier {
1361 DISTINCT, REDUCED
1362 }
1363
1364 private static final class Query {
1365
1366 final QueryModelNode root;
1367
1368 final Form form;
1369
1370 @Nullable
1371 final Modifier modifier;
1372
1373 final List<ProjectionElem> select;
1374
1375 @Nullable
1376 final TupleExpr construct;
1377
1378 @Nullable
1379 final Dataset from;
1380
1381 final TupleExpr where;
1382
1383 final List<ProjectionElem> groupBy;
1384
1385 @Nullable
1386 final ValueExpr having;
1387
1388 final List<OrderElem> orderBy;
1389
1390 @Nullable
1391 final Long offset;
1392
1393 @Nullable
1394 final Long limit;
1395
1396 static Query create(final TupleExpr rootNode, @Nullable final Dataset dataset,
1397 final boolean forceSelect) {
1398
1399 Preconditions.checkNotNull(rootNode);
1400
1401
1402 if (rootNode instanceof EmptySet) {
1403 return new Query(rootNode, Form.CONSTRUCT, null, null, rootNode, dataset,
1404 rootNode, null, null, null, null, null);
1405 }
1406
1407
1408 Form form = null;
1409 Modifier modifier = null;
1410 final List<ProjectionElem> select = Lists.newArrayList();
1411 TupleExpr construct = null;
1412 TupleExpr where = null;
1413 final List<ProjectionElem> groupBy = Lists.newArrayList();
1414 ValueExpr having = null;
1415 final List<OrderElem> orderBy = Lists.newArrayList();
1416 Long offset = null;
1417 Long limit = null;
1418
1419 final List<UnaryTupleOperator> nodes = extractQueryNodes(rootNode, false);
1420
1421 where = nodes.size() > 0 ? nodes.get(nodes.size() - 1).getArg() : rootNode;
1422
1423 for (final UnaryTupleOperator node : nodes) {
1424
1425 if (node instanceof DescribeOperator) {
1426 form = Form.DESCRIBE;
1427
1428 } else if (node instanceof Distinct) {
1429 modifier = Modifier.DISTINCT;
1430
1431 } else if (node instanceof Reduced) {
1432 modifier = Modifier.REDUCED;
1433
1434 } else if (node instanceof Projection) {
1435 final Map<String, ExtensionElem> extensions = extractExtensions(node);
1436 final List<ProjectionElem> projections = ((Projection) node)
1437 .getProjectionElemList().getElements();
1438 final boolean isConstruct = projections.size() >= 3
1439 && "subject".equals(projections.get(0).getTargetName())
1440 && "predicate".equals(projections.get(1).getTargetName())
1441 && "object".equals(projections.get(2).getTargetName())
1442 && (projections.size() == 3 || projections.size() == 4
1443 && "context".equals(projections.get(3).getTargetName()));
1444 if (isConstruct && !forceSelect) {
1445 form = Form.CONSTRUCT;
1446 construct = extractConstructExpression(extensions,
1447 Collections.singleton(((Projection) node)
1448 .getProjectionElemList()));
1449 } else {
1450 form = form == null ? Form.SELECT : form;
1451 for (final ProjectionElem projection : projections) {
1452 final String variable = projection.getTargetName();
1453 ExtensionElem extension = extensions.get(variable);
1454 if (extension == null && projection.getSourceName() != null) {
1455 extension = extensions.get(projection.getSourceName());
1456 }
1457 final ProjectionElem newProjection = new ProjectionElem();
1458 newProjection.setTargetName(variable);
1459 newProjection.setSourceExpression(extension);
1460 newProjection.setSourceName(extension == null
1461 || !(extension.getExpr() instanceof Var) ? projection
1462 .getSourceName() : ((Var) extension.getExpr()).getName());
1463 select.add(newProjection);
1464 }
1465 }
1466
1467 } else if (node instanceof MultiProjection) {
1468 form = Form.CONSTRUCT;
1469 construct = extractConstructExpression(extractExtensions(node),
1470 ((MultiProjection) node).getProjections());
1471
1472 } else if (node instanceof Group) {
1473 final Group group = (Group) node;
1474 final Map<String, ExtensionElem> extensions = extractExtensions(group.getArg());
1475 for (final String variableName : group.getGroupBindingNames()) {
1476 final ExtensionElem extension = extensions.get(variableName);
1477 final ProjectionElem projection = new ProjectionElem();
1478 projection.setTargetName(variableName);
1479 projection.setSourceExpression(extension);
1480 projection.setSourceName(extension == null
1481 || !(extension.getExpr() instanceof Var) ? variableName
1482 : ((Var) extension.getExpr()).getName());
1483 groupBy.add(projection);
1484 }
1485
1486 } else if (node instanceof Order) {
1487 orderBy.addAll(((Order) node).getElements());
1488
1489 } else if (node instanceof Slice) {
1490 final Slice slice = (Slice) node;
1491 offset = slice.getOffset() < 0 ? null : slice.getOffset();
1492 limit = slice.getLimit() < 0 ? null : slice.getLimit();
1493 if (form == null && slice.getOffset() == 0 && slice.getLimit() == 1) {
1494 if (forceSelect) {
1495 form = Form.SELECT;
1496 limit = 1L;
1497
1498 } else {
1499 form = Form.ASK;
1500 }
1501 }
1502
1503 } else if (node instanceof Filter) {
1504 having = ((Filter) node).getCondition();
1505 }
1506 }
1507
1508 form = defaultIfNull(form, Form.SELECT);
1509 if (form == Form.CONSTRUCT && construct == null) {
1510 construct = new SingletonSet();
1511 }
1512
1513 return new Query(rootNode, form, modifier, select, construct, dataset, where, groupBy,
1514 having, orderBy, offset, limit);
1515 }
1516
1517 private static List<UnaryTupleOperator> extractQueryNodes(final TupleExpr rootNode,
1518 final boolean haltOnGroup) {
1519 final List<UnaryTupleOperator> nodes = Lists.newArrayList();
1520
1521 TupleExpr queryNode = rootNode;
1522 while (queryNode instanceof UnaryTupleOperator) {
1523 nodes.add((UnaryTupleOperator) queryNode);
1524 queryNode = ((UnaryTupleOperator) queryNode).getArg();
1525 }
1526
1527 boolean describeFound = false;
1528 boolean modifierFound = false;
1529 boolean projectionFound = false;
1530 boolean groupFound = false;
1531 boolean orderFound = false;
1532 boolean sliceFound = false;
1533 boolean extensionFound = false;
1534
1535 int index = 0;
1536 while (index < nodes.size()) {
1537 final UnaryTupleOperator node = nodes.get(index);
1538 if (node instanceof DescribeOperator && !describeFound) {
1539 describeFound = true;
1540
1541 } else if ((node instanceof Distinct || node instanceof Reduced) && !modifierFound
1542 && !projectionFound) {
1543 modifierFound = true;
1544
1545 } else if ((node instanceof Projection || node instanceof MultiProjection)
1546 && !projectionFound) {
1547 projectionFound = true;
1548
1549 } else if (node instanceof Group && !groupFound && !haltOnGroup) {
1550 groupFound = true;
1551
1552 } else if (node instanceof Order && !orderFound) {
1553 orderFound = true;
1554
1555 } else if (node instanceof Slice && !sliceFound) {
1556 sliceFound = true;
1557
1558 } else if (node instanceof Filter && !groupFound && !haltOnGroup) {
1559 int i = index + 1;
1560 for (; i < nodes.size() && nodes.get(i) instanceof Extension;) {
1561 ++i;
1562 }
1563 if (i < nodes.size() && nodes.get(i) instanceof Group) {
1564 groupFound = true;
1565 index = i;
1566 } else {
1567 break;
1568 }
1569
1570 } else if (node instanceof Extension && !extensionFound) {
1571 extensionFound = true;
1572
1573 } else if (!(node instanceof QueryRoot) || index > 0) {
1574 break;
1575 }
1576 ++index;
1577 }
1578
1579 return nodes.subList(0, index);
1580 }
1581
1582 private static Map<String, ExtensionElem> extractExtensions(final TupleExpr rootNode) {
1583 final Map<String, ExtensionElem> map = Maps.newHashMap();
1584 for (final UnaryTupleOperator node : extractQueryNodes(rootNode, true)) {
1585 if (node instanceof Extension) {
1586 for (final ExtensionElem elem : ((Extension) node).getElements()) {
1587 final String variable = elem.getName();
1588 final ValueExpr expression = elem.getExpr();
1589 if (!(expression instanceof Var)
1590 || !((Var) expression).getName().equals(variable)) {
1591 map.put(variable, elem);
1592 }
1593 }
1594 }
1595 }
1596 return map;
1597 }
1598
1599 private static TupleExpr extractConstructExpression(
1600 final Map<String, ExtensionElem> extensions,
1601 final Iterable<? extends ProjectionElemList> multiProjections) {
1602 TupleExpr expression = null;
1603 for (final ProjectionElemList projections : multiProjections) {
1604 final Var subj = extractConstructVar(extensions, projections.getElements().get(0));
1605 final Var pred = extractConstructVar(extensions, projections.getElements().get(1));
1606 final Var obj = extractConstructVar(extensions, projections.getElements().get(2));
1607 final Var ctx = projections.getElements().size() < 4 ? null : extractConstructVar(
1608 extensions, projections.getElements().get(3));
1609 final StatementPattern pattern = new StatementPattern(
1610 ctx == null ? Scope.DEFAULT_CONTEXTS : Scope.NAMED_CONTEXTS, subj, pred,
1611 obj, ctx);
1612 expression = expression == null ? pattern : new Join(expression, pattern);
1613 }
1614 return expression;
1615 }
1616
1617 private static Var extractConstructVar(final Map<String, ExtensionElem> extensions,
1618 final ProjectionElem projection) {
1619 final ExtensionElem extension = extensions.get(projection.getSourceName());
1620 String name = projection.getSourceName();
1621 if (name.startsWith("-anon-")) {
1622 name += "-construct";
1623 }
1624 if (extension == null || extension.getExpr() instanceof BNodeGenerator) {
1625 final Var var = new Var(name);
1626 var.setAnonymous(name.startsWith("-anon-"));
1627 return var;
1628 } else if (extension.getExpr() instanceof ValueConstant) {
1629 final ValueConstant constant = (ValueConstant) extension.getExpr();
1630 return new Var(name, constant.getValue());
1631 } else {
1632 throw new UnsupportedOperationException(
1633 "Unsupported extension in construct query: " + extension);
1634 }
1635 }
1636
1637 private Query(
1638 final QueryModelNode root,
1639 final Form form,
1640 @Nullable final Modifier modifier,
1641 @Nullable final Iterable<? extends ProjectionElem> selectist,
1642 @Nullable final TupleExpr construct,
1643 @Nullable final Dataset from,
1644 final TupleExpr where,
1645 @Nullable final Iterable<? extends ProjectionElem> groupByt,
1646 @Nullable final ValueExpr having,
1647 @Nullable final Iterable<? extends OrderElem> orderBy,
1648 @Nullable final Long offset,
1649 @Nullable final Long limit) {
1650
1651 this.root = Preconditions.checkNotNull(root);
1652 this.form = Preconditions.checkNotNull(form);
1653 this.modifier = modifier;
1654 this.select = selectist == null ? ImmutableList.<ProjectionElem>of() : ImmutableList
1655 .copyOf(selectist);
1656 this.construct = construct;
1657 this.from = from;
1658 this.where = Preconditions.checkNotNull(where);
1659 this.groupBy = groupByt == null ? ImmutableList.<ProjectionElem>of() : ImmutableList
1660 .copyOf(groupByt);
1661 this.having = having;
1662 this.orderBy = orderBy == null ? ImmutableList.<OrderElem>of() : ImmutableList
1663 .copyOf(orderBy);
1664 this.offset = offset;
1665 this.limit = limit;
1666 }
1667
1668 }
1669
1670 }