/******************************************************************************* * Copyright (c) 2010 Fraunhofer IWU and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Fraunhofer IWU - initial API and implementation *******************************************************************************/ package net.enilink.komma.parser.sparql.tree.visitor; import java.util.Iterator; import java.util.List; import net.enilink.vocab.rdf.RDF; import net.enilink.komma.parser.sparql.SparqlParser; import net.enilink.komma.parser.sparql.tree.BNode; import net.enilink.komma.parser.sparql.tree.BNodePropertyList; import net.enilink.komma.parser.sparql.tree.BasicGraphPattern; import net.enilink.komma.parser.sparql.tree.BooleanLiteral; import net.enilink.komma.parser.sparql.tree.Collection; import net.enilink.komma.parser.sparql.tree.ConstructQuery; import net.enilink.komma.parser.sparql.tree.Dataset; import net.enilink.komma.parser.sparql.tree.DescribeQuery; import net.enilink.komma.parser.sparql.tree.DoubleLiteral; import net.enilink.komma.parser.sparql.tree.GenericLiteral; import net.enilink.komma.parser.sparql.tree.Graph; import net.enilink.komma.parser.sparql.tree.GraphNode; import net.enilink.komma.parser.sparql.tree.GraphPattern; import net.enilink.komma.parser.sparql.tree.IntegerLiteral; import net.enilink.komma.parser.sparql.tree.IriRef; import net.enilink.komma.parser.sparql.tree.LimitModifier; import net.enilink.komma.parser.sparql.tree.MinusGraph; import net.enilink.komma.parser.sparql.tree.NamedGraph; import net.enilink.komma.parser.sparql.tree.OffsetModifier; import net.enilink.komma.parser.sparql.tree.OptionalGraph; import net.enilink.komma.parser.sparql.tree.OrderCondition; import net.enilink.komma.parser.sparql.tree.OrderModifier; import net.enilink.komma.parser.sparql.tree.PrefixDecl; import net.enilink.komma.parser.sparql.tree.Prologue; import net.enilink.komma.parser.sparql.tree.PropertyList; import net.enilink.komma.parser.sparql.tree.PropertyPattern; import net.enilink.komma.parser.sparql.tree.QName; import net.enilink.komma.parser.sparql.tree.Query; import net.enilink.komma.parser.sparql.tree.QueryWithSolutionModifier; import net.enilink.komma.parser.sparql.tree.SelectQuery; import net.enilink.komma.parser.sparql.tree.UnionGraph; import net.enilink.komma.parser.sparql.tree.Variable; import net.enilink.komma.parser.sparql.tree.expr.BuiltInCall; import net.enilink.komma.parser.sparql.tree.expr.Expression; import net.enilink.komma.parser.sparql.tree.expr.FunctionCall; import net.enilink.komma.parser.sparql.tree.expr.GraphPatternExpr; import net.enilink.komma.parser.sparql.tree.expr.LogicalExpr; import net.enilink.komma.parser.sparql.tree.expr.LogicalOperator; import net.enilink.komma.parser.sparql.tree.expr.NegateExpr; import net.enilink.komma.parser.sparql.tree.expr.NumericExpr; import net.enilink.komma.parser.sparql.tree.expr.RelationalExpr; public class ToStringVisitor implements Visitor<StringBuilder, StringBuilder> { StringBuilder indent = new StringBuilder(); protected String newLine() { return "\n" + indent.toString(); } protected void indent() { indent.append(" "); } protected void dedent() { int start = Math.max(0, indent.length() - 4); int end = Math.min(indent.length(), start + 4); indent.replace(start, end, ""); } protected void solutionModifiers(QueryWithSolutionModifier query, StringBuilder sb) { if (query.getLimitModifier() != null) { query.getLimitModifier().accept(this, sb); } if (query.getOffsetModifier() != null) { query.getOffsetModifier().accept(this, sb); } if (query.getOrderModifier() != null) { query.getOrderModifier().accept(this, sb); } } @Override public StringBuilder askQuery(Query askQuery, StringBuilder data) { askQuery.getPrologue().accept(this, data); data.append(newLine()); data.append("ASK "); askQuery.getDataset().accept(this, data); askQuery.getGraph().accept(this, data); return data; } @Override public StringBuilder bNode(BNode bNode, StringBuilder data) { if (bNode.getLabel() != null) { data.append(bNode.getLabel()); } else { data.append("[]"); } return bNode.getPropertyList().accept(this, data); } @Override public StringBuilder bNodePropertyList(BNodePropertyList bNode, StringBuilder data) { data.append("["); bNode.getBNodePropertyList().accept(this, data); data.append("]"); return data; } @Override public StringBuilder booleanLiteral(BooleanLiteral booleanLiteral, StringBuilder data) { return data.append(booleanLiteral.getValue() ? "true" : "true"); } @Override public StringBuilder builtInCall(BuiltInCall builtinCall, StringBuilder data) { data.append(builtinCall.getName()); data.append("("); printArgs(builtinCall.getArgs(), data); data.append(")"); return data; } @Override public StringBuilder collection(Collection collection, StringBuilder data) { data.append("("); for (Iterator<GraphNode> it = collection.getElements().iterator(); it .hasNext();) { it.next().accept(this, data); if (it.hasNext()) { data.append(" "); } } data.append(")"); collection.getPropertyList().accept(this, data); return data; } @Override public StringBuilder constructQuery(ConstructQuery constructQuery, StringBuilder data) { constructQuery.getPrologue().accept(this, data); data.append(newLine()).append("CONSTRUCT {"); indent(); for (Iterator<GraphNode> it = constructQuery.getTemplate().iterator(); it .hasNext();) { data.append(newLine()); it.next().accept(this, data); if (it.hasNext()) { data.append(" . "); } } constructQuery.getDataset().accept(this, data); dedent(); data.append(newLine()).append("} WHERE "); constructQuery.getGraph().accept(this, data); solutionModifiers(constructQuery, data); return data; } @Override public StringBuilder dataset(Dataset dataset, StringBuilder data) { for (Expression graph : dataset.getDefaultGraphs()) { data.append(newLine()).append("FROM "); graph.accept(this, data); } for (Expression graph : dataset.getNamedGraphs()) { data.append(newLine()).append("FROM NAMED "); graph.accept(this, data); } return data; } @Override public StringBuilder describeQuery(DescribeQuery describeQuery, StringBuilder data) { describeQuery.getPrologue().accept(this, data); data.append(newLine()).append("DESCRIBE "); if (describeQuery.getResources().isEmpty()) { data.append("* "); } else { for (GraphNode node : describeQuery.getResources()) { node.accept(this, data); data.append(" "); } } describeQuery.getDataset().accept(this, data); if (describeQuery.getGraph() != null) { data.append(newLine()).append("WHERE "); describeQuery.getGraph().accept(this, data); } solutionModifiers(describeQuery, data); return data; } @Override public StringBuilder doubleLiteral(DoubleLiteral doubleLiteral, StringBuilder data) { return data.append(doubleLiteral.getValue()); } @Override public StringBuilder functionCall(FunctionCall functionCall, StringBuilder data) { functionCall.getName().accept(this, data); data.append("("); printArgs(functionCall.getArgs(), data); data.append(")"); return data; } @Override public StringBuilder genericLiteral(GenericLiteral genericLiteral, StringBuilder data) { data.append("\"") .append(genericLiteral.getLabel().replaceAll("\"", "\\\"")) .append("\""); if (genericLiteral.getLanguage() != null) { data.append("@").append(genericLiteral.getLanguage()); } else if (genericLiteral.getDatatype() != null) { data.append("^^"); genericLiteral.getDatatype().accept(this, data); } return data; } @Override public StringBuilder basicGraphPattern(BasicGraphPattern pattern, StringBuilder data) { for (Iterator<GraphNode> nodeIt = pattern.getNodes().iterator(); nodeIt .hasNext();) { data.append(newLine()); nodeIt.next().accept(this, data); data.append(" . "); } return data; } protected char getLastChar(StringBuilder sb) { char[] chars = new char[1]; if (sb.length() > 0) { sb.getChars(sb.length() - 1, sb.length(), chars, 0); } return chars[0]; } @Override public StringBuilder graphPattern(GraphPattern graphPattern, StringBuilder data) { if (getLastChar(data) == '{') { data.append(newLine()); } data.append("{"); indent(); for (Graph graph : graphPattern.getPatterns()) { graph.accept(this, data); } for (Expression filter : graphPattern.getFilters()) { boolean addParens = !(filter instanceof GraphPatternExpr); data.append(newLine()).append("FILTER "); if (addParens) { data.append(" ("); } filter.accept(this, data); if (addParens) { data.append(")"); } } dedent(); data.append(newLine()).append("}"); return data; } @Override public StringBuilder integerLiteral(IntegerLiteral numericLiteral, StringBuilder data) { return data.append(numericLiteral.getValue()); } @Override public StringBuilder iriRef(IriRef iriRef, StringBuilder data) { if (SparqlParser.RDF_NIL.equals(iriRef.getIri())) { data.append("()"); } else { data.append("<").append(iriRef.getIri()).append(">"); } return iriRef.getPropertyList().accept(this, data); } @Override public StringBuilder limitModifier(LimitModifier limitModifier, StringBuilder data) { return data.append(newLine()).append("LIMIT ") .append(limitModifier.getLimit()); } @Override public StringBuilder logicalExpr(LogicalExpr logicalExpr, StringBuilder data) { if (logicalExpr.getOperator() == LogicalOperator.NOT) { data.append(logicalExpr.getOperator().getSymbol()).append(" "); } for (Iterator<Expression> it = logicalExpr.getExprs().iterator(); it .hasNext();) { Expression expr = it.next(); if (expr instanceof LogicalExpr // add parentheses for precedence if required && logicalExpr.getOperator().ordinal() > ((LogicalExpr) expr) .getOperator().ordinal()) { data.append("("); expr.accept(this, data); data.append(")"); } else { expr.accept(this, data); } if (it.hasNext()) { data.append(' ').append(logicalExpr.getOperator().getSymbol()) .append(' '); } } return data; } @Override public StringBuilder minusGraph(MinusGraph minusGraph, StringBuilder data) { data.append(newLine()).append("MINUS "); minusGraph.getGraph().accept(this, data); return data; } @Override public StringBuilder namedGraph(NamedGraph namedGraph, StringBuilder data) { data.append("GRAPH "); namedGraph.getName().accept(this, data); return namedGraph.getGraph().accept(this, data); } @Override public StringBuilder negateExpr(NegateExpr negateExpr, StringBuilder data) { data.append("! "); return negateExpr.getExpr().accept(this, data); } @Override public StringBuilder numericExpr(NumericExpr numericExpr, StringBuilder data) { Expression left = numericExpr.getLeft(); Expression right = numericExpr.getRight(); if (left instanceof NumericExpr && numericExpr.getOperator().hasPriorityOver( ((NumericExpr) left).getOperator())) { data.append("("); left.accept(this, data); data.append(")"); } else { left.accept(this, data); } data.append(numericExpr.getOperator().getSymbol()); if (right instanceof NumericExpr && numericExpr.getOperator().hasPriorityOver( ((NumericExpr) right).getOperator())) { data.append("("); right.accept(this, data); data.append(")"); } else { right.accept(this, data); } return data; } @Override public StringBuilder offsetModifier(OffsetModifier offsetModifier, StringBuilder data) { return data.append(newLine()).append("OFFSET ") .append(offsetModifier.getOffset()); } @Override public StringBuilder optionalGraph(OptionalGraph optionalGraph, StringBuilder data) { data.append(newLine()).append("OPTIONAL "); optionalGraph.getGraph().accept(this, data); return data; } @Override public StringBuilder orderModifier(OrderModifier orderModifier, StringBuilder data) { data.append(newLine()).append("ORDER BY "); for (Iterator<OrderCondition> it = orderModifier.getOrderConditions() .iterator(); it.hasNext();) { OrderCondition condition = it.next(); condition.getExpression().accept(this, data); switch (condition.getDirection()) { case DESC: data.append(" DESC"); } if (it.hasNext()) { data.append(", "); } } return data; } protected void printArgs(List<Expression> args, StringBuilder sb) { for (Iterator<Expression> it = args.iterator(); it.hasNext();) { Expression arg = it.next(); arg.accept(this, sb); if (it.hasNext()) { sb.append(", "); } } } @Override public StringBuilder prologue(Prologue prologue, StringBuilder data) { if (prologue.getBase() != null) { data.append(newLine()).append("BASE "); prologue.getBase().accept(this, data); } for (PrefixDecl prefixDecl : prologue.getPrefixDecls()) { data.append(newLine()).append("PREFIX ") .append(prefixDecl.getPrefix()).append(":"); prefixDecl.getIri().accept(this, data); } return data; } @Override public StringBuilder propertyList(PropertyList propertyList, StringBuilder data) { GraphNode lastPredicate = null; for (Iterator<PropertyPattern> it = propertyList.iterator(); it .hasNext();) { PropertyPattern pattern = it.next(); if (pattern.getPredicate() == lastPredicate) { data.append(", "); } else { data.append(" "); if (pattern.getPredicate() instanceof IriRef && ((IriRef) pattern.getPredicate()).getIri().equals( RDF.PROPERTY_TYPE.toString())) { data.append("a"); } else { pattern.getPredicate().accept(this, data); } data.append(" "); } pattern.getObject().accept(this, data); lastPredicate = pattern.getPredicate(); if (it.hasNext()) { data.append("; "); } } return data; } @Override public StringBuilder qName(QName qName, StringBuilder data) { if (qName.getPrefix() != null) { data.append(qName.getPrefix()); } data.append(":").append( qName.getLocalPart() != null ? qName.getLocalPart() : ""); return qName.getPropertyList().accept(this, data); } @Override public StringBuilder relationalExpr(RelationalExpr relationalExpr, StringBuilder data) { Expression left = relationalExpr.getLeft(); Expression right = relationalExpr.getRight(); left.accept(this, data); data.append(' ').append(relationalExpr.getOperator().getSymbol()) .append(' '); right.accept(this, data); return data; } @Override public StringBuilder selectQuery(SelectQuery selectQuery, StringBuilder data) { selectQuery.getPrologue().accept(this, data); data.append(newLine()); data.append("SELECT "); if (selectQuery.getModifier() != null) { switch (selectQuery.getModifier()) { case DISTINCT: data.append("DISTINCT "); break; case REDUCED: data.append("REDUCED "); break; } } if (selectQuery.getProjection().isEmpty()) { data.append("* "); } else { for (Variable var : selectQuery.getProjection()) { var.accept(this, data); data.append(" "); } } selectQuery.getDataset().accept(this, data); data.append(newLine()); data.append("WHERE "); selectQuery.getGraph().accept(this, data); solutionModifiers(selectQuery, data); return data; } @Override public StringBuilder unionGraph(UnionGraph unionGraph, StringBuilder data) { for (Iterator<Graph> it = unionGraph.getGraphs().iterator(); it .hasNext();) { Graph graph = it.next(); graph.accept(this, data); if (it.hasNext()) { data.append(" UNION "); } } return data; } @Override public StringBuilder variable(Variable variable, StringBuilder data) { data.append("?").append(variable.getName()); return variable.getPropertyList().accept(this, data); } @Override public StringBuilder graphPatternExpr(GraphPatternExpr graphPatternExpr, StringBuilder data) { switch (graphPatternExpr.getType()) { case EXISTS: data.append("EXISTS "); // case NOT_EXISTS default: data.append("NOT EXISTS "); } return graphPatternExpr.getPattern().accept(this, data); } }