You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2017/06/20 10:44:25 UTC
[5/6] jena git commit: JENA-369: Output pretty lists in basic graph
patterns.
JENA-369: Output pretty lists in basic graph patterns.
This includes property functions.
Conversion is cautious - it looks for the triples as the parser
might output them.
Nested bnodes structures in lists are not converted back.
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/f19d8b26
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/f19d8b26
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/f19d8b26
Branch: refs/heads/master
Commit: f19d8b267e1976f655e2c7509e6c7ce67c0c9496
Parents: 9a3a072
Author: Andy Seaborne <an...@apache.org>
Authored: Sat Jun 17 15:48:12 2017 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Jun 18 09:26:55 2017 +0100
----------------------------------------------------------------------
.../jena/sparql/serializer/FmtEltLib.java | 195 ++++++
.../sparql/serializer/FormatterElement.java | 688 ++++++++++---------
.../sparql/serializer/TriplesListBlock.java | 43 ++
3 files changed, 612 insertions(+), 314 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/f19d8b26/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FmtEltLib.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FmtEltLib.java b/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FmtEltLib.java
new file mode 100644
index 0000000..d14f3da
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FmtEltLib.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.serializer;
+
+import static org.apache.jena.graph.Node.ANY;
+
+import java.util.*;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.sparql.core.BasicPattern;
+import org.apache.jena.vocabulary.RDF;
+
+/** Place to move some of the code from FormatterElement */
+class FmtEltLib {
+
+ /*package*/ static Node rdfFirst = RDF.Nodes.first;
+ /*package*/ static Node rdfRest = RDF.Nodes.rest;
+ /*package*/ static Node rdfNil = RDF.Nodes.nil;
+
+ // Cautious list finder.
+ // The list must be like the parser creates them:
+ // Adjacent triples, in first, rest order.
+
+ // It does support embedded lists.
+ // It does not support bnode structures i.e. [:prop :obj]
+
+ // Ths code simply finds lists - it does not perform checks as to their suitablity to print in ()-form.
+
+ /*package*/ static TriplesListBlock createTriplesListBlock(BasicPattern bgp) {
+ TriplesListBlock tlb = new TriplesListBlock();
+ List<Triple> triples = bgp.getList();
+ for ( int idx = 0 ; idx < triples.size() ; idx++ ) {
+ Triple t = triples.get(idx);
+ if ( idx == triples.size() - 1 )
+ // Can't be a following triple.
+ break;
+ // null -> ANY
+ if ( matches(t, ANY, rdfFirst, ANY) ) {
+ Node consCell = t.getSubject();
+ int numTriples = collectList(consCell, idx, triples, tlb);
+ if ( numTriples > 0 ) {
+ // Skip triples.
+ idx = idx + numTriples - 1 ;
+ } else {
+ // Play safe
+ // skip to (? rdf:rest rdf:nil) (if any).
+ for ( idx = idx + 1 ; idx < triples.size() ; idx++ ) {
+ Triple t2 = triples.get(idx);
+ if ( matches(t2, ANY, rdfRest, rdfNil) )
+ break;
+ }
+ }
+ }
+ }
+ return tlb;
+ }
+
+ private static Node nullAsAny(Node n) {
+ return n == null ? ANY : n ;
+ }
+
+ /*package*/ static boolean matches(Triple t, Node s, Node p, Node o) {
+ s = nullAsAny(s) ;
+ p = nullAsAny(p) ;
+ o = nullAsAny(o) ;
+ if ( s != ANY && ! Objects.equals(s, t.getSubject()) )
+ return false ;
+ if ( p != ANY && ! Objects.equals(p, t.getPredicate()) )
+ return false ;
+ if ( o != ANY && ! Objects.equals(o, t.getObject()) )
+ return false ;
+ return true ;
+ }
+
+ /*package*/ static int collectList(Node consCell, int idx, List<Triple> triples, TriplesListBlock tlb) {
+ Set<Triple> listTriples = new LinkedHashSet<>();
+ TriplesListBlock block1 = collectList1(consCell, idx, triples, listTriples, tlb);
+ if ( block1 == null )
+ // Failed.
+ return -1;
+ if ( ! FormatterElement.FMT_FREE_STANDING_LISTS ) {
+ // Reject free standiang lists.
+ int inCount = count(triples, ANY, ANY, consCell);
+ int outCount = count(triples, consCell, ANY, ANY);
+ if ( inCount == 0 && outCount == 2 )
+ return -1;
+ }
+
+ int numTriples = block1.triplesInLists.size();
+ tlb.merge(block1);
+ return numTriples;
+ }
+
+ /**
+ * Spot parser pattern of adjacent "first-rest" pairs.
+ * Collect elements of a well-formed list else null.
+ * {@code triplesInList}.
+ */
+ /*package*/ static TriplesListBlock collectList1(Node consCell, int idx, List<Triple> triples, Set<Triple> triplesInList, TriplesListBlock tlb) {
+ // This list - accumulate separately because we aren't sure it is well-formed yet.
+ TriplesListBlock thisList = new TriplesListBlock();
+ List<Node> elts = new ArrayList<>();
+ thisList.listElementsMap.put(consCell, elts);
+
+ for ( ;; ) {
+ if ( idx + 1 >= triples.size() )
+ // Last triple - can't be an rdf:first, rdf:rest pair.
+ return null;
+ Triple t1 = triples.get(idx);
+ consCell = t1.getSubject();
+
+ Triple t2 = triples.get(idx + 1);
+
+ // -- Checks on t1
+ // t1 : (consCell rdf:first element)
+ if ( ! matches(t1, consCell, rdfFirst, ANY) )
+ return null;
+
+ // ---- Possible compound value.
+ // Second triple is rdf:rest, or rdf:first for a list in a list.
+ // or arbitrary triples for [:p :q] in a list.
+ // We don't handle the latter case because programatic can make this anything.
+
+ final boolean ListsInLists = true ;
+ if ( ListsInLists ) {
+ if ( rdfFirst.equals(t2.getPredicate()) && t1.getObject().equals(t2.getSubject()) ) {
+ // Recursion.
+ int numProcessed = collectList(t2.getSubject(), idx + 1, triples, thisList); // -1
+ if ( numProcessed < 0 )
+ return null;
+ // Not "-1" - this loop does not have autoincrement.
+ idx = idx + numProcessed ;
+ // idx: Posn of the rdf:nil. Probe to see if t2 is an "rdf:rest" to consider.
+ t2 = triples.get(idx + 1);
+ }
+ }
+
+ // -- Checks on t2
+ // t2 : (consCell rdf:rest element)
+ if ( ! matches(t2, consCell, rdfRest, ANY) )
+ return null;
+ // -- Check consCell - no other triples or one if a subject list.
+ int outCount = count(triples, consCell, ANY, ANY) ;
+ if ( outCount != 2 ) {
+ // Head cell also be a subject list. in which case the first cell of the list can have a count of 3.
+ if ( outCount == 3 && ! elts.isEmpty() )
+ return null;
+ }
+
+
+ int inCount = count(triples, ANY, ANY, consCell) ;
+ if ( inCount != 1 ) {
+ // Head cell can also be zero : subject or free standing list head.
+ if ( outCount == 0 && ! elts.isEmpty() )
+ return null;
+ }
+
+
+ Node elt = t1.getObject();
+ thisList.triplesInLists.add(t1);
+ thisList.triplesInLists.add(t2);
+ elts.add(elt);
+ if ( matches(t2, ANY, ANY, rdfNil) ) {
+ return thisList;
+ }
+ idx += 2;
+ }
+ }
+
+ /*package*/ static int count(List<Triple> triples, Node s, Node p, Node o) {
+ int count = 0 ;
+ for ( Triple t : triples ) {
+ if ( matches(t, s, p, o) )
+ count++;
+ }
+ return count;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/f19d8b26/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FormatterElement.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FormatterElement.java b/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FormatterElement.java
index 2d1f6cc..4770fce 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FormatterElement.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/serializer/FormatterElement.java
@@ -1,5 +1,5 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one
+f * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
@@ -18,283 +18,286 @@
package org.apache.jena.sparql.serializer;
-import java.util.ArrayList ;
-import java.util.Iterator ;
-import java.util.List ;
+import static org.apache.jena.graph.Node.ANY;
+import static org.apache.jena.sparql.serializer.FmtEltLib.count;
+import static org.apache.jena.sparql.serializer.FmtEltLib.createTriplesListBlock;
+import static org.apache.jena.sparql.serializer.FmtEltLib.rdfFirst;
-import org.apache.jena.atlas.io.IndentedLineBuffer ;
-import org.apache.jena.atlas.io.IndentedWriter ;
-import org.apache.jena.graph.Node ;
-import org.apache.jena.graph.Triple ;
-import org.apache.jena.query.Query ;
+import java.util.*;
+
+import org.apache.jena.atlas.io.IndentedLineBuffer;
+import org.apache.jena.atlas.io.IndentedWriter;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.query.Query;
import org.apache.jena.query.QueryVisitor;
import org.apache.jena.query.Syntax;
-import org.apache.jena.sparql.core.BasicPattern ;
-import org.apache.jena.sparql.core.PathBlock ;
-import org.apache.jena.sparql.core.TriplePath ;
-import org.apache.jena.sparql.expr.Expr ;
-import org.apache.jena.sparql.path.PathWriter ;
-import org.apache.jena.sparql.syntax.* ;
-import org.apache.jena.sparql.util.FmtUtils ;
-import org.apache.jena.vocabulary.RDF ;
-
-
-public class FormatterElement extends FormatterBase
- implements ElementVisitor
-{
- public static final int INDENT = 2 ;
-
+import org.apache.jena.sparql.core.BasicPattern;
+import org.apache.jena.sparql.core.PathBlock;
+import org.apache.jena.sparql.core.TriplePath;
+import org.apache.jena.sparql.expr.Expr;
+import org.apache.jena.sparql.path.PathWriter;
+import org.apache.jena.sparql.syntax.*;
+import org.apache.jena.sparql.util.FmtUtils;
+import org.apache.jena.vocabulary.RDF;
+
+public class FormatterElement extends FormatterBase implements ElementVisitor {
+ public static final int INDENT = 2;
+
/** Control whether to show triple pattern boundaries - creates extra nesting */
- public static final boolean PATTERN_MARKERS = false ;
-
-// /** Control whether triple patterns always have a final dot - it can be dropped in some cases */
-// public static boolean PATTERN_FINAL_DOT = false ;
+ public static final boolean PATTERN_MARKERS = false;
+
+// /** Control whether triple patterns always have a final dot - it can be dropped in some cases */
+// public static boolean PATTERN_FINAL_DOT = false ;
/** Control whether (non-triple) patterns have a final dot - it can be dropped */
- public static final boolean GROUP_SEP_DOT = false ;
-
+ public static final boolean GROUP_SEP_DOT = false;
+
/** Control whether the first item of a group is on the same line as the { */
- public static final boolean GROUP_FIRST_ON_SAME_LINE = true ;
+ public static final boolean GROUP_FIRST_ON_SAME_LINE = true;
/** Control pretty printing */
- public static final boolean PRETTY_PRINT = true ;
+ public static final boolean PRETTY_PRINT = true;
- /** Control whether disjunction has set of delimiters - as it's a group usually, these aren't needed */
- public static final boolean UNION_MARKERS = false ;
+ /** Control pretty printing of RDF lists */
+ public static final boolean FMT_LISTS = true;
+ /** Control pretty printing of free standing RDF lists */
+ // Do *not* set "true" - argument of property paths can be lists and these spane
+ // PathBlocks and Basic Graph Patterns and this is not handled prop.
+ public static final boolean FMT_FREE_STANDING_LISTS = false;
+
+ /**
+ * Control whether disjunction has set of delimiters - as it's a group usually, these
+ * aren't needed
+ */
+ public static final boolean UNION_MARKERS = false;
+
/** Control whether GRAPH indents in a fixed way or based on the layout size */
- public static final boolean GRAPH_FIXED_INDENT = true ;
-
- /** Control whether NOT EXIST/EXISTS indents in a fixed way or based on the layout size */
- public static final boolean ELEMENT1_FIXED_INDENT = true ;
-
+ public static final boolean GRAPH_FIXED_INDENT = true;
+
+ /**
+ * Control whether NOT EXIST/EXISTS indents in a fixed way or based on the layout size
+ */
+ public static final boolean ELEMENT1_FIXED_INDENT = true;
+
/** Control triples pretty printing */
- public static final int TRIPLES_SUBJECT_COLUMN = 8 ;
-
+ public static final int TRIPLES_SUBJECT_COLUMN = 8;
+
// Less than this => rest of triple on the same line
- // Could be smart and make it depend on the property length as well. Later.
- public static final int TRIPLES_SUBJECT_LONG = 12 ;
+ // Could be smart and make it depend on the property length as well. Later.
+ public static final int TRIPLES_SUBJECT_LONG = 12;
- public static final int TRIPLES_PROPERTY_COLUMN = 20;
-
- public static final int TRIPLES_COLUMN_GAP = 2 ;
+ public static final int TRIPLES_PROPERTY_COLUMN = 20;
+
+ public static final int TRIPLES_COLUMN_GAP = 2;
public FormatterElement(IndentedWriter out, SerializationContext context) {
- super(out, context) ;
+ super(out, context);
}
public static void format(IndentedWriter out, SerializationContext cxt, Element el) {
- FormatterElement fmt = new FormatterElement(out, cxt) ;
- fmt.startVisit() ;
- el.visit(fmt) ;
- fmt.finishVisit() ;
+ FormatterElement fmt = new FormatterElement(out, cxt);
+ fmt.startVisit();
+ el.visit(fmt);
+ fmt.finishVisit();
}
public static String asString(Element el) {
- SerializationContext cxt = new SerializationContext() ;
- IndentedLineBuffer b = new IndentedLineBuffer() ;
- FormatterElement.format(b, cxt, el) ;
- return b.toString() ;
+ SerializationContext cxt = new SerializationContext();
+ IndentedLineBuffer b = new IndentedLineBuffer();
+ FormatterElement.format(b, cxt, el);
+ return b.toString();
}
public boolean topMustBeGroup() {
- return false ;
+ return false;
}
@Override
public void visit(ElementTriplesBlock el) {
if ( el.isEmpty() ) {
- out.println("# Empty BGP") ;
- return ;
+ out.println("# Empty BGP");
+ return;
}
- formatTriples(el.getPattern()) ;
+ formatTriples(el.getPattern());
}
@Override
public void visit(ElementPathBlock el) {
- // Write path block - don't put in a final trailing "."
+ // Write path block - don't put in a final trailing "."
if ( el.isEmpty() ) {
- out.println("# Empty BGP") ;
- return ;
+ out.println("# Empty BGP");
+ return;
}
// Split into BGP-path-BGP-...
// where the BGPs may be empty.
- PathBlock pBlk = el.getPattern() ;
- BasicPattern bgp = new BasicPattern() ;
- boolean first = true ; // Has anything been output?
+ PathBlock pBlk = el.getPattern();
+ BasicPattern bgp = new BasicPattern();
+ boolean first = true; // Has anything been output?
for ( TriplePath tp : pBlk ) {
if ( tp.isTriple() ) {
- bgp.add(tp.asTriple()) ;
- continue ;
+ bgp.add(tp.asTriple());
+ continue;
}
-
+
if ( !bgp.isEmpty() ) {
if ( !first )
- out.println(" .") ;
- flush(bgp) ;
- first = false ;
+ out.println(" .");
+ flush(bgp);
+ first = false;
}
if ( !first )
- out.println(" .") ;
- // Path
- printSubject(tp.getSubject()) ;
- out.print(" ") ;
- PathWriter.write(out, tp.getPath(), context.getPrologue()) ;
- out.print(" ") ;
- printObject(tp.getObject()) ;
- first = false ;
+ out.println(" .");
+ // Path (no RDF list output).
+ printSubject(tp.getSubject());
+ out.print(" ");
+ PathWriter.write(out, tp.getPath(), context.getPrologue());
+ out.print(" ");
+ printObject(tp.getObject());
+ first = false;
}
// Flush any stored triple patterns.
if ( !bgp.isEmpty() ) {
if ( !first )
- out.println(" .") ;
- flush(bgp) ;
- first = false ;
+ out.println(" .");
+ flush(bgp);
+ first = false;
}
}
- private void flush(BasicPattern bgp) {
- formatTriples(bgp) ;
- bgp.getList().clear();
- }
-
@Override
- public void visit(ElementDataset el)
- {
-// if ( el.getDataset() != null)
-// {
-// DatasetGraph dsNamed = el.getDataset() ;
-// out.print("DATASET ") ;
-// out.incIndent(INDENT) ;
-// Iterator iter = dsNamed.listNames() ;
-// if ( iter.hasNext() )
-// {
-// boolean first = false ;
-// for ( ; iter.hasNext() ; )
-// {
-// if ( ! first )
-// out.newline() ;
-// out.print("FROM <") ;
-// String s = (String)iter.next() ;
-// out.print(s) ;
-// out.print(">") ;
+ public void visit(ElementDataset el) {
+ // Not implemented.
+// if ( el.getDataset() != null ) {
+// DatasetGraph dsNamed = el.getDataset();
+// out.print("DATASET ");
+// out.incIndent(INDENT);
+// Iterator<Node> iter = dsNamed.listGraphNodes();
+// if ( iter.hasNext() ) {
+// boolean first = false;
+// for ( ; iter.hasNext() ; ) {
+// if ( !first )
+// out.newline();
+// out.print("FROM <");
+// Node n = iter.next();
+// out.print(slotToString(n));
+// out.print(">");
// }
// }
-// out.decIndent(INDENT) ;
-// out.newline() ;
+// out.decIndent(INDENT);
+// out.newline();
// }
- if ( el.getElement() != null )
- visitAsGroup(el.getElement()) ;
+// if ( el.getElement() != null )
+// visitAsGroup(el.getElement());
}
@Override
public void visit(ElementFilter el) {
- out.print("FILTER ") ;
- Expr expr = el.getExpr() ;
- FmtExprSPARQL v = new FmtExprSPARQL(out, context) ;
+ out.print("FILTER ");
+ Expr expr = el.getExpr();
+ FmtExprSPARQL v = new FmtExprSPARQL(out, context);
// This assumes that complex expressions are bracketted
// (parens) as necessary except for some cases:
// Plain variable or constant
- boolean addParens = false ;
+ boolean addParens = false;
if ( expr.isVariable() )
- addParens = true ;
+ addParens = true;
if ( expr.isConstant() )
- addParens = true ;
+ addParens = true;
if ( addParens )
- out.print("( ") ;
- v.format(expr) ;
+ out.print("( ");
+ v.format(expr);
if ( addParens )
- out.print(" )") ;
+ out.print(" )");
}
@Override
public void visit(ElementAssign el) {
- out.print("LET (") ;
- out.print("?" + el.getVar().getVarName()) ;
- out.print(" := ") ;
- FmtExprSPARQL v = new FmtExprSPARQL(out, context) ;
- v.format(el.getExpr()) ;
- out.print(")") ;
+ out.print("LET (");
+ out.print("?" + el.getVar().getVarName());
+ out.print(" := ");
+ FmtExprSPARQL v = new FmtExprSPARQL(out, context);
+ v.format(el.getExpr());
+ out.print(")");
}
@Override
public void visit(ElementBind el) {
- out.print("BIND(") ;
- FmtExprSPARQL v = new FmtExprSPARQL(out, context) ;
- v.format(el.getExpr()) ;
- out.print(" AS ") ;
- out.print("?" + el.getVar().getVarName()) ;
- out.print(")") ;
+ out.print("BIND(");
+ FmtExprSPARQL v = new FmtExprSPARQL(out, context);
+ v.format(el.getExpr());
+ out.print(" AS ");
+ out.print("?" + el.getVar().getVarName());
+ out.print(")");
}
@Override
public void visit(ElementData el) {
- QuerySerializer.outputDataBlock(out, el.getVars(), el.getRows(), context) ;
+ QuerySerializer.outputDataBlock(out, el.getVars(), el.getRows(), context);
}
@Override
public void visit(ElementUnion el) {
if ( el.getElements().size() == 1 ) {
// If this is an element of just one, just do it inplace
- // Can't happen from a parsed query.
- // Now can :-)
-
- // SPARQL 1.1 inline UNION.
- // Same as OPTIONAL, MINUS
- out.print("UNION") ;
- out.incIndent(INDENT) ;
- out.newline() ;
- visitAsGroup(el.getElements().get(0)) ;
- out.decIndent(INDENT) ;
- return ;
+ // Can't happen from a parsed query in SPARQL.
+ visitAsGroup(el.getElements().get(0));
+// // Same as OPTIONAL, MINUS
+// out.print("UNION");
+// out.incIndent(INDENT);
+// out.newline();
+// visitAsGroup(el.getElements().get(0));
+// out.decIndent(INDENT);
+ return;
}
if ( UNION_MARKERS ) {
- out.print("{") ;
- out.newline() ;
- out.pad() ;
+ out.print("{");
+ out.newline();
+ out.pad();
}
- out.incIndent(INDENT) ;
+ out.incIndent(INDENT);
- boolean first = true ;
+ boolean first = true;
for ( Element subElement : el.getElements() ) {
if ( !first ) {
- out.decIndent(INDENT) ;
- out.newline() ;
- out.print("UNION") ;
- out.newline() ;
- out.incIndent(INDENT) ;
+ out.decIndent(INDENT);
+ out.newline();
+ out.print("UNION");
+ out.newline();
+ out.incIndent(INDENT);
}
- visitAsGroup(subElement) ;
- first = false ;
+ visitAsGroup(subElement);
+ first = false;
}
- out.decIndent(INDENT) ;
+ out.decIndent(INDENT);
if ( UNION_MARKERS ) {
- out.newline() ;
- out.print("}") ;
+ out.newline();
+ out.print("}");
}
}
@Override
public void visit(ElementGroup el) {
- out.print("{") ;
- int initialRowNumber = out.getRow() ;
- out.incIndent(INDENT) ;
+ out.print("{");
+ int initialRowNumber = out.getRow();
+ out.incIndent(INDENT);
if ( !GROUP_FIRST_ON_SAME_LINE )
- out.newline() ;
+ out.newline();
- int row1 = out.getRow() ;
- out.pad() ;
+ int row1 = out.getRow();
+ out.pad();
- boolean first = true ;
- Element lastElt = null ;
+ boolean first = true;
+ Element lastElt = null;
for ( Element subElement : el.getElements() ) {
// Some adjacent elements need a DOT:
@@ -303,302 +306,359 @@ public class FormatterElement extends FormatterBase
// Need to move on after the last thing printed.
// Check for necessary DOT as separator
if ( GROUP_SEP_DOT || needsDotSeparator(lastElt, subElement) )
- out.print(" . ") ;
- out.newline() ;
+ out.print(" . ");
+ out.newline();
}
- subElement.visit(this) ;
- first = false ;
- lastElt = subElement ;
+ subElement.visit(this);
+ first = false;
+ lastElt = subElement;
}
- out.decIndent(INDENT) ;
+ out.decIndent(INDENT);
// Where to put the closing "}"
- int row2 = out.getRow() ;
+ int row2 = out.getRow();
if ( row1 != row2 )
- out.newline() ;
+ out.newline();
// Finally, close the group.
if ( out.getRow() == initialRowNumber )
- out.print(" ") ;
- out.print("}") ;
+ out.print(" ");
+ out.print("}");
}
private static boolean needsDotSeparator(Element el1, Element el2) {
- return needsDotSeparator(el1) && needsDotSeparator(el2) ;
+ return needsDotSeparator(el1) && needsDotSeparator(el2);
}
private static boolean needsDotSeparator(Element el) {
- return (el instanceof ElementTriplesBlock) || (el instanceof ElementPathBlock) ;
+ return (el instanceof ElementTriplesBlock) || (el instanceof ElementPathBlock);
}
@Override
public void visit(ElementOptional el) {
- out.print("OPTIONAL") ;
- out.incIndent(INDENT) ;
- out.newline() ;
- visitAsGroup(el.getOptionalElement()) ;
- out.decIndent(INDENT) ;
+ out.print("OPTIONAL");
+ out.incIndent(INDENT);
+ out.newline();
+ visitAsGroup(el.getOptionalElement());
+ out.decIndent(INDENT);
}
@Override
public void visit(ElementNamedGraph el) {
- visitNodePattern("GRAPH", el.getGraphNameNode(), el.getElement()) ;
+ visitNodePattern("GRAPH", el.getGraphNameNode(), el.getElement());
}
@Override
public void visit(ElementService el) {
- String x = "SERVICE" ;
+ String x = "SERVICE";
if ( el.getSilent() )
- x = "SERVICE SILENT" ;
- visitNodePattern(x, el.getServiceNode(), el.getElement()) ;
+ x = "SERVICE SILENT";
+ visitNodePattern(x, el.getServiceNode(), el.getElement());
}
private void visitNodePattern(String label, Node node, Element subElement) {
- int len = label.length() ;
- out.print(label) ;
- out.print(" ") ;
- String nodeStr = (node == null) ? "*" : slotToString(node) ;
- out.print(nodeStr) ;
- len += nodeStr.length() ;
+ int len = label.length();
+ out.print(label);
+ out.print(" ");
+ String nodeStr = (node == null) ? "*" : slotToString(node);
+ out.print(nodeStr);
+ len += nodeStr.length();
if ( GRAPH_FIXED_INDENT ) {
- out.incIndent(INDENT) ;
- out.newline() ; // NB and newline
+ out.incIndent(INDENT);
+ out.newline(); // NB and newline
} else {
- out.print(" ") ;
- len++ ;
- out.incIndent(len) ;
+ out.print(" ");
+ len++;
+ out.incIndent(len);
}
- visitAsGroup(subElement) ;
+ visitAsGroup(subElement);
if ( GRAPH_FIXED_INDENT )
- out.decIndent(INDENT) ;
+ out.decIndent(INDENT);
else
- out.decIndent(len) ;
+ out.decIndent(len);
}
private void visitElement1(String label, Element1 el) {
- int len = label.length() ;
- out.print(label) ;
- len += label.length() ;
+ int len = label.length();
+ out.print(label);
+ len += label.length();
if ( ELEMENT1_FIXED_INDENT ) {
- out.incIndent(INDENT) ;
- out.newline() ; // NB and newline
+ out.incIndent(INDENT);
+ out.newline(); // NB and newline
} else {
- out.print(" ") ;
- len++ ;
- out.incIndent(len) ;
+ out.print(" ");
+ len++;
+ out.incIndent(len);
}
- visitAsGroup(el.getElement()) ;
+ visitAsGroup(el.getElement());
if ( ELEMENT1_FIXED_INDENT )
- out.decIndent(INDENT) ;
+ out.decIndent(INDENT);
else
- out.decIndent(len) ;
+ out.decIndent(len);
}
@Override
public void visit(ElementExists el) {
- visitElement1("EXISTS", el) ;
+ visitElement1("EXISTS", el);
}
@Override
public void visit(ElementNotExists el) {
- visitElement1("NOT EXISTS", el) ;
+ visitElement1("NOT EXISTS", el);
}
@Override
public void visit(ElementMinus el) {
- out.print("MINUS") ;
- out.incIndent(INDENT) ;
- out.newline() ;
- visitAsGroup(el.getMinusElement()) ;
- out.decIndent(INDENT) ;
+ out.print("MINUS");
+ out.incIndent(INDENT);
+ out.newline();
+ visitAsGroup(el.getMinusElement());
+ out.decIndent(INDENT);
}
@Override
public void visit(ElementSubQuery el) {
- out.print("{ ") ;
- out.incIndent(INDENT) ;
- Query q = el.getQuery() ;
+ out.print("{ ");
+ out.incIndent(INDENT);
+ Query q = el.getQuery();
// Serialize with respect to the existing context
- QuerySerializerFactory factory = SerializerRegistry.get().getQuerySerializerFactory(Syntax.syntaxARQ) ;
- QueryVisitor serializer = factory.create(Syntax.syntaxARQ, context, out) ;
- q.visit(serializer) ;
+ QuerySerializerFactory factory = SerializerRegistry.get().getQuerySerializerFactory(Syntax.syntaxARQ);
+ QueryVisitor serializer = factory.create(Syntax.syntaxARQ, context, out);
+ q.visit(serializer);
- out.decIndent(INDENT) ;
- out.print("}") ;
+ out.decIndent(INDENT);
+ out.print("}");
}
public void visitAsGroup(Element el) {
- boolean needBraces = !((el instanceof ElementGroup) || (el instanceof ElementSubQuery)) ;
+ boolean needBraces = !((el instanceof ElementGroup) || (el instanceof ElementSubQuery));
if ( needBraces ) {
- out.print("{ ") ;
- out.incIndent(INDENT) ;
+ out.print("{ ");
+ out.incIndent(INDENT);
}
- el.visit(this) ;
+ el.visit(this);
if ( needBraces ) {
- out.decIndent(INDENT) ;
- out.print("}") ;
+ out.decIndent(INDENT);
+ out.print("}");
}
}
-
- int subjectWidth = -1 ;
- int predicateWidth = -1 ;
+ // -------- Formatting a basic graph pattern
+ // Triple order is preserved.
+
+ int subjectWidth = -1;
+ int predicateWidth = -1;
+
@Override
protected void formatTriples(BasicPattern triples) {
if ( !PRETTY_PRINT ) {
- super.formatTriples(triples) ;
- return ;
+ super.formatTriples(triples);
+ return;
}
- // TODO RDF Collections - spot the parsers pattern
if ( triples.isEmpty() )
- return ;
+ return;
+
+ // Lists in this BasicPattern.
+ // TriplesListBlock is a record of lists in this BGP.
+ // Formatting is off for list if there is an empty TriplesListBlock.
+ // This is cautionsly spotting the triples from RDF lists as generated by the
+ // parser.
- setWidths(triples) ;
+ TriplesListBlock block = FMT_LISTS
+ ? createTriplesListBlock(triples)
+ : new TriplesListBlock();
+
+ Set<Node> freeStanding = new HashSet<>();
+ for ( Node head : block.listElementsMap.keySet() ) {
+ // Check for suitablity to print.
+ // See also FmtEltLib#collectList
+ //
+ // Subject-list : inCount = 0, outCount = 3
+ // Object-list : inCount = 1, outCount = 2
+ // Free-standing list : inCount = 0, outCount = 2
+ // Free-standing list is handled as a special case.
+ int inCount = count(triples.getList(), ANY, ANY, head);
+ int outCount = count(triples.getList(), head, ANY, ANY);
+ if ( inCount == 0 && outCount == 2 )
+ // Free standing.
+ freeStanding.add(head);
+ }
+
+ setWidths(triples);
if ( subjectWidth > TRIPLES_SUBJECT_COLUMN )
- subjectWidth = TRIPLES_SUBJECT_COLUMN ;
+ subjectWidth = TRIPLES_SUBJECT_COLUMN;
if ( predicateWidth > TRIPLES_PROPERTY_COLUMN )
- predicateWidth = TRIPLES_PROPERTY_COLUMN ;
+ predicateWidth = TRIPLES_PROPERTY_COLUMN;
- // Loops:
- List<Triple> subjAcc = new ArrayList<>() ; // Accumulate all triples
- // with the same subject.
- Node subj = null ; // Subject being accumulated
+ // Accumulate all triples with the same subject.
+ List<Triple> subjAcc = new ArrayList<>();
+ // Subject being accumulated
+ Node subj = null;
+ // Print newlines between blocks.
+ boolean first = true;
- boolean first = true ; // Print newlines between blocks.
-
- int indent = -1 ;
+ int indent = -1;
for ( Triple t : triples ) {
+ if ( block.triplesInLists.contains(t) ) {
+ if ( rdfFirst.equals(t.getPredicate()) ) {
+ if ( freeStanding.contains(t.getSubject()) )
+ printNodeOrList(t.getSubject(), block.listElementsMap);
+ }
+ continue;
+ }
+
if ( subj != null && t.getSubject().equals(subj) ) {
- subjAcc.add(t) ;
- continue ;
+ subjAcc.add(t);
+ continue;
}
if ( subj != null ) {
if ( !first )
- out.println(" .") ;
- formatSameSubject(subj, subjAcc) ;
- first = false ;
+ out.println(" .");
+ formatSameSubject(subj, subjAcc, block.listElementsMap);
+ first = false;
// At end of line of a block of triples with same subject.
// Drop through and start new block of same subject triples.
}
// New subject
- subj = t.getSubject() ;
- subjAcc.clear() ;
- subjAcc.add(t) ;
+ subj = t.getSubject();
+ subjAcc.clear();
+ subjAcc.add(t);
}
// Flush accumulator
if ( subj != null && subjAcc.size() != 0 ) {
if ( !first )
- out.println(" .") ;
- first = false ;
- formatSameSubject(subj, subjAcc) ;
+ out.println(" .");
+ first = false;
+ formatSameSubject(subj, subjAcc, block.listElementsMap);
}
}
- private void formatSameSubject(Node subject, List<Triple> triples) {
+ // ----
+
+ private void flush(BasicPattern bgp) {
+ formatTriples(bgp);
+ bgp.getList().clear();
+ }
+
+ private void formatSameSubject(Node subject, List<Triple> triples, Map<Node, List<Node>> lists) {
+
if ( triples == null || triples.size() == 0 )
- return ;
-
- // Do the first triple.
- Iterator<Triple> iter = triples.iterator() ;
- Triple t1 = iter.next() ;
+ return;
-// int indent = TRIPLES_SUBJECT_COLUMN+TRIPLES_COLUMN_GAP ;
-// // Long subject => same line. Works for single triple as well.
-// int s1_len = printSubject(t1.getSubject()) ;
-// //int x = out.getCol() ;
+ // Do the first triple.
+ Iterator<Triple> iter = triples.iterator();
+ Triple t1 = iter.next();
- int indent = subjectWidth + TRIPLES_COLUMN_GAP ;
- int s1_len = printSubject(t1.getSubject()) ;
+ int indent = subjectWidth + TRIPLES_COLUMN_GAP;
+ int s1_len = printNodeOrList(subject, lists);
if ( s1_len > TRIPLES_SUBJECT_LONG ) {
- // Too long - start a new line.
- out.incIndent(indent) ;
- out.println() ;
+ out.incIndent(indent);
+ out.println();
} else {
- printGap() ;
- out.incIndent(indent) ;
+ printGap();
+ out.incIndent(indent);
}
// Remainder of first triple
- printProperty(t1.getPredicate()) ;
- printGap() ;
- printObject(t1.getObject()) ;
+ printProperty(t1.getPredicate());
+ printGap();
- // Do the rest
+ printNodeOrList(t1.getObject(), lists);
+ // Do the rest
for ( ; iter.hasNext() ; ) {
- Triple t = iter.next() ;
- out.println(" ;") ;
- printProperty(t.getPredicate()) ;
- printGap() ;
- printObject(t.getObject()) ;
- continue ;
- // print property list
+ Triple t = iter.next();
+ out.println(" ;");
+ printProperty(t.getPredicate());
+ printGap();
+ printNodeOrList(t.getObject(), lists);
+ continue;
}
// Finish off the block.
- out.decIndent(indent) ;
+ out.decIndent(indent);
// out.print(" .") ;
}
+ private int printNodeOrList(Node node, Map<Node, List<Node>> lists) {
+ if ( lists.containsKey(node) )
+ return printList(lists.get(node), lists);
+ else
+ return printNoCol(node);
+ }
+
private void setWidths(BasicPattern triples) {
- subjectWidth = -1 ;
- predicateWidth = -1 ;
+ subjectWidth = -1;
+ predicateWidth = -1;
for ( Triple t : triples ) {
- String s = slotToString(t.getSubject()) ;
+ String s = slotToString(t.getSubject());
if ( s.length() > subjectWidth )
- subjectWidth = s.length() ;
+ subjectWidth = s.length();
- String p = slotToString(t.getPredicate()) ;
+ String p = slotToString(t.getPredicate());
if ( p.length() > predicateWidth )
- predicateWidth = p.length() ;
+ predicateWidth = p.length();
}
}
private void printGap() {
- out.print(' ', TRIPLES_COLUMN_GAP) ;
+ out.print(' ', TRIPLES_COLUMN_GAP);
}
- // Indent must be set first.
+ // printSubject, printObject - used in ElementPathBlock.
private int printSubject(Node s) {
- String str = slotToString(s) ;
- out.print(str) ;
- out.pad(subjectWidth) ;
- return str.length() ;
+ return printNoCol(s);
}
// Assumes the indent is TRIPLES_SUBJECT_COLUMN+GAP
- private static String RDFTYPE = FmtUtils.stringForNode(RDF.Nodes.type, new SerializationContext()) ;
-
+ private static String RDFTYPE = FmtUtils.stringForNode(RDF.Nodes.type, new SerializationContext());
+
private int printProperty(Node p) {
- String str = slotToString(p) ;
+ String str = slotToString(p);
if ( p.equals(RDF.Nodes.type) && str.equals(RDFTYPE) )
- out.print("a") ;
+ out.print("a");
else
- out.print(str) ;
- out.pad(predicateWidth) ;
- return str.length() ;
+ out.print(str);
+ out.pad(predicateWidth);
+ return str.length();
}
private int printObject(Node obj) {
- return printNoCol(obj) ;
+ return printNoCol(obj);
}
- private int printNoCol(Node node) {
- String str = slotToString(node) ;
- out.print(str) ;
- return str.length() ;
+ private int printList(List<Node> list, Map<Node, List<Node>> lists) {
+ if ( list.isEmpty() ) {
+ out.print("()");
+ return 2;
+ }
+
+ int col0 = out.getCol();
+ out.print("( ");
+ for ( Node n : list ) {
+ printNodeOrList(n, lists);
+ out.print(" ");
+ }
+ out.print(")");
+ int col1 = out.getCol();
+ return col1 - col0;
+ }
+ private int printNoCol(Node node) {
+ String str = slotToString(node);
+ out.print(str);
+ return str.length();
}
}
http://git-wip-us.apache.org/repos/asf/jena/blob/f19d8b26/jena-arq/src/main/java/org/apache/jena/sparql/serializer/TriplesListBlock.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/serializer/TriplesListBlock.java b/jena-arq/src/main/java/org/apache/jena/sparql/serializer/TriplesListBlock.java
new file mode 100644
index 0000000..37671b8
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/serializer/TriplesListBlock.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.serializer;
+
+import java.util.*;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+
+/** Internal record of list details. */
+/*package*/ class TriplesListBlock {
+ Map<Node, List<Node>> listElementsMap = new HashMap<>();
+ // Triples in lists.
+ Set<Triple> triplesInLists = new LinkedHashSet<>();
+
+ /*package*/ void merge(TriplesListBlock other) {
+ listElementsMap.putAll(other.listElementsMap);
+ triplesInLists.addAll(other.triplesInLists);
+ }
+
+ @Override
+ public String toString() {
+ return Iter.asString(listElementsMap.keySet(), ", ") + "\n" + "{"+ Iter.asString(triplesInLists.iterator(), "\n")+"}";
+
+ }
+}
\ No newline at end of file