Package org.apache.jena.riot.writer

Source Code of org.apache.jena.riot.writer.TurtleShell$ComparePredicates

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for ad
* ditional 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.riot.writer;

import static org.apache.jena.riot.writer.WriterConst.* ;

import java.util.* ;

import org.apache.jena.atlas.io.IndentedWriter ;
import org.apache.jena.atlas.lib.Pair ;
import org.apache.jena.riot.other.GLib ;
import org.apache.jena.riot.out.NodeFormatter ;
import org.apache.jena.riot.out.NodeFormatterTTL ;
import org.apache.jena.riot.out.NodeToLabel ;
import org.apache.jena.riot.system.PrefixMap ;
import org.apache.jena.riot.system.PrefixMapFactory ;
import org.apache.jena.riot.system.RiotLib ;

import com.hp.hpl.jena.graph.Graph ;
import com.hp.hpl.jena.graph.Node ;
import com.hp.hpl.jena.graph.Triple ;
import com.hp.hpl.jena.util.iterator.ExtendedIterator ;
import com.hp.hpl.jena.vocabulary.RDF ;
import com.hp.hpl.jena.vocabulary.RDFS ;

/** Base class to support the pretty forms of Turtle-related languages (Turtle, TriG) */
public abstract class TurtleShell
{
    protected final IndentedWriter out ;
    protected final NodeFormatter nodeFmt ;
    protected final PrefixMap prefixMap ;
    protected final String baseURI ;

    protected TurtleShell(IndentedWriter out, PrefixMap pmap, String baseURI)
    {
        this.out = out ;
        if ( pmap == null )
            pmap = PrefixMapFactory.emptyPrefixMap() ;
        this.nodeFmt = new NodeFormatterTTL(baseURI,  pmap, NodeToLabel.createScopeByDocument()) ;
        this.prefixMap = pmap ;
        this.baseURI = baseURI ;
    }

    protected void writeBase(String base)
    {
        RiotLib.writeBase(out, base) ;
    }

    protected void writePrefixes(PrefixMap prefixMap)
    {
        RiotLib.writePrefixes(out, prefixMap) ;
    }

    protected void writeGraphTTL(Graph graph)
    {
        ShellGraph x = new ShellGraph(graph) ;
        x.writeGraph() ;
    }
   
    // Write one graph - using an inner object class to isolate
    // the state variables for writing a single graph.
    private final class ShellGraph {
        // Per graph member variables.
        private final Graph graph ;
        private final Set<Node> nestedObjects ;           // Blank nodes that have one incoming triple
        private final Set<Node> freeBnodes ;              // Blank nodes subjects that are not referenced
        private final Map<Node, List<Node>> lists;        // The head node in each well-formed lists -> list elements
        private final Map<Node, List<Node>> freeLists;    // List that do not have any incoming triples
        private final Map<Node, List<Node>> nLinkedLists; // Lists that have more than one incoming triple
        private final Collection<Node> listElts ;         // All nodes that are part of list structures.

        private ShellGraph(Graph graph)
        {
            this.graph          = graph ;
            this.nestedObjects  = new HashSet<Node>()  ;
            this.freeBnodes     = new HashSet<Node>()  ;
           
            this.lists          = new HashMap<Node, List<Node>>() ;
            this.freeLists      = new HashMap<Node, List<Node>>() ;
            this.nLinkedLists   = new HashMap<Node, List<Node>>() ;
            this.listElts       = new HashSet<Node>() ;

            // Must be in this order.
            findLists() ;
            findBNodesSyntax() ;
            // Stop head of lists printed as triples going all the way to the good part.
            nestedObjects.removeAll(listElts) ;
        }

        /* Bnodes that can written as [] */
        private void findBNodesSyntax()
        {
            Set<Node> rejects = new HashSet<Node>() ;           // Nodes known not to meet the requirement.

            ExtendedIterator<Triple> iter = graph.find(Node.ANY, Node.ANY, Node.ANY) ;
            try {
                for ( ; iter.hasNext() ; )
                {
                    Triple t = iter.next() ;
                    Node subj = t.getSubject() ;
                    if ( subj.isBlank() && ! lists.containsKey(subj))
                    {
                        // And not a list ...
                        int sConn = inLinks(subj) ;
                        if ( sConn == 0 )
                            freeBnodes.add(subj) ;
                    }
                   
                    Node obj = t.getObject() ;
                    if ( ! obj.isBlank() )
                        continue ;
                    if ( rejects.contains(obj) )
                        continue ;
                    // No point checking bNodesObj1.
                    int connectivity = inLinks(obj) ;
                    if ( connectivity == 1 )
                        nestedObjects.add(obj) ;
                    // else connected multiple times.
                }
            } finally { iter.close() ; }
        }

        // returns 0,1,2 (where 2 really means "more than 1")
        private int inLinks(Node obj)
        {
            ExtendedIterator<Triple> iter = graph.find(Node.ANY, Node.ANY, obj) ;
            int count = 0 ;
            try {
                if ( ! iter.hasNext() ) return 0 ;
                iter.next() ;
                if ( ! iter.hasNext() ) return 1 ;
                return 2 ;
            } finally { iter.close() ; }
        }

        // --- Lists setup
        /* Find all list heads and all nodes in well-formed lists.
         * Return a (list head -> Elements map), list elements) 
         */
        private void findLists()
        {
            List<Triple> tails = RiotLib.triples(graph, Node.ANY, RDF_Rest, RDF_Nil) ;
            for ( Triple t : tails )
            {
                // Returns the elements, reversed.
                Collection<Node> listElts2 = new HashSet<Node>() ;
                Pair<Node, List<Node>> p = followTailToHead(t.getSubject(), listElts2) ;
                if ( p != null )
                {
                    Node headElt = p.getLeft() ;
                    // Free standing?private
                    List<Node> elts = p.getRight() ;
                    long numLinks = RiotLib.countTriples(graph, null, null, headElt) ;
                    if ( numLinks == 1 )
                        lists.put(headElt, elts) ;
                    else if ( numLinks == 0 )
                        // 0 connected lists
                        freeLists.put(headElt, elts) ;
                    else
                        // Two triples to this list.
                        nLinkedLists.put(headElt, elts) ;
                    listElts.addAll(listElts2) ;
                }
            }
        }

        // return head elt node, list of elements.
        private Pair<Node, List<Node>> followTailToHead(Node lastListElt, Collection<Node> listElts)
        {
            List<Node> listCells = new ArrayList<Node>() ;
            List<Node> eltsReversed = new ArrayList<Node>() ;
            List<Triple> acc =  new ArrayList<Triple>() ;
            Node x = lastListElt ;

            for ( ; ; )
            {
                if ( ! validListElement(x, acc) )
                {
                    if ( listCells.size() == 0 )
                        // No earlier valid list.
                        return null ;
                    // Fix up to previous valid list cell.
                    x = listCells.remove(listCells.size()-1) ;
                    break ;
                }

                Triple t = RiotLib.triple1(graph, x, RDF_First, null) ;
                if ( t == null )
                    return null ;
                eltsReversed.add(t.getObject()) ;
                listCells.add(x) ;

                // Try to move up the list.
                List<Triple> acc2 = RiotLib.triples(graph, null, null, x) ;
                long numRest = RiotLib.countTriples(graph, null, RDF_Rest, x) ;
                if ( numRest != 1 )
                {
                    // Head of well-formed list.
                    // Classified by 0,1,more links later.
                    listCells.add(x) ;
                    break ;
                }
                // numRest == 1
                int numLinks = acc2.size() ;
                if ( numLinks > 1 )
                    // Non-list links to x
                    break ;
                // Valid.
                Triple tLink = acc2.get(0) ;
                x = tLink.getSubject() ;
            }
            // Success.
            listElts.addAll(listCells) ;
            Collections.reverse(eltsReversed) ;
            return Pair.create(x, eltsReversed);
        }

        /* Return the triples of the list element, or null if invalid list */
        private boolean validListElement(Node x, List<Triple> acc)
        {
            Triple t1 = RiotLib.triple1(graph, x, RDF_Rest, null) ; // Which we came up to get here :-(
            if ( t1 == null )
                return false ;
            Triple t2 = RiotLib.triple1(graph, x, RDF_First, null) ;
            if ( t2 == null )
                return false ;
            long N = RiotLib.countTriples(graph, x, null, null) ;
            if ( N != 2 )
                return false ;
            acc.add(t1) ;
            acc.add(t2) ;
            return true ;
        }

        // ----

        private void writeGraph()
        {
            Iterator<Node> subjects = GLib.listSubjects(graph) ;
            boolean somethingWritten = writeBySubject(subjects) ;

            // Write remainders
            // 1 - Shared lists
            // 2 - Free standing lists
           
            if ( ! nLinkedLists.isEmpty() )
                somethingWritten = writeNLinkedLists(somethingWritten) ;
           
            if ( ! freeLists.isEmpty() )
                somethingWritten = writeFreeLists(somethingWritten) ;
           
               
        }

        // Write lists that are shared objects 
        private boolean writeNLinkedLists(boolean somethingWritten)
        {
            // Print carefully - need a label for the first cell.
            // So we write out the first element of the list in triples, then put
            // the remainer as a pretty list
            for ( Node n : nLinkedLists.keySet() )
            {
                if ( somethingWritten ) out.println() ;
                somethingWritten = true ;

                List<Node> x = nLinkedLists.get(n) ;
                writeNode(n) ;

                if ( out.getCol() > LONG_SUBJECT )
                    println() ;
                else
                    gap(GAP_S_P) ;
                out.incIndent(INDENT_PREDICATE) ;
                // ----
                // DRY writeCluster.
                out.pad() ;
                writeNode(RDF_First) ;
                print(" ") ;
                writeNode(x.get(0)) ;
                print(" ;") ;
                println() ;
                writeNode(RDF_Rest) ;
                print("  ") ;
                x = x.subList(1, x.size()) ;
                list(x) ;
                print(" .") ;
                out.decIndent(INDENT_PREDICATE) ;
                println() ;
            }
            return somethingWritten ;
        }

        // Write free standing lists - ones where the head is not an object of some other triple.
        // Turtle does not (... ) . so write as a predicateObjectList for one element.
        private boolean writeFreeLists(boolean somethingWritten)
        {
            //out.println("# Free standing lists") ;
            // Write free lists.
            for ( Node n : freeLists.keySet() )
            {
                if ( somethingWritten )
                    out.println() ;
                somethingWritten = true ;

                List<Node> x = freeLists.get(n) ;
                // Print first element for the [ ... ]
                out.print("[ ") ;

                writeNode(RDF_First) ;
                print(" ") ;
                writeNode(x.get(0)) ;
                print(" ; ") ;
                writeNode(RDF_Rest) ;
                print(" ") ;
                x = x.subList(1, x.size()) ;
                // Print remainder.
                list(x) ;
                out.println(" ] .") ;
            }
            return somethingWritten ;
        }

        // return true if did write something.
        private boolean writeBySubject(Iterator<Node> subjects)
        {
            boolean first = true ;
            for ( ; subjects.hasNext() ; )
            {
                Node subj = subjects.next() ;
                if ( nestedObjects.contains(subj) )
                    continue ;
               
                if ( listElts.contains(subj) )
                    continue ;
                if ( ! first )
                    out.println() ;
                first = false ;
                if ( freeBnodes.contains(subj) )
                {
                    // Write in "[....]" form.
                    nestedObject(subj) ;
                    out.println(" .") ;
                    continue ;
                }

                Collection<Triple> cluster = RiotLib.triplesOfSubject(graph, subj) ;
                writeCluster(subj, cluster) ;
            }
            return !first ;
        }


        // Common subject
        // Used by the blocks writer as well.
        private void writeCluster(Node subject, Collection<Triple> cluster)
        {
            if ( cluster.isEmpty() ) return ;
            writeNode(subject) ;

            if ( out.getCol() > LONG_SUBJECT )
                out.println() ;
            else
                gap(GAP_S_P) ;
            out.incIndent(INDENT_PREDICATE) ;
            out.pad() ;
            writePredicateObjectList(cluster) ;
            out.decIndent(INDENT_PREDICATE) ;
            // End of cluster.
            print(" .") ;
            println() ;
        }

        // Writing predciate-object lists.
        // We group the cluster by predicate and within each group
        // we print:
        //  literals, then simple objects, then pretty objects
       
        private void writePredicateObjectList(Collection<Triple> cluster)
        {
            Map<Node, List<Node>> pGroups = groupByPredicates(cluster) ;
            Collection<Node> predicates = pGroups.keySet() ;
           
            // Find longest predicate URI
            int predicateMaxWidth = RiotLib.calcWidth(prefixMap, baseURI, predicates, MIN_PREDICATE, LONG_PREDICATE) ;

            boolean first = true ;

            if ( ! OBJECT_LISTS )
            {
                for ( Node p : predicates )
                {
                    for ( Node o : pGroups.get(p) )
                    {
                        writePredicateObject(p, o, predicateMaxWidth, first) ;
                        first = false ;
                    }
                }
                return ;
            }
           
            for ( Node p : predicates )
            {
                List<Node> rdfLiterals = new ArrayList<Node>() ;        // Literals in the group
                List<Node> rdfSimpleNodes = new ArrayList<Node>() ;     // Non-literals, printed
                List<Node> rdfComplexNodes = new ArrayList<Node>() ;    // Non-literals, printed () or []-embedded

                for ( Node o : pGroups.get(p) )
                {
                    if ( o.isLiteral() ) { rdfLiterals.add(o) ; continue ; }
                    if ( isPrettyNode(o) ) { rdfComplexNodes.add(o) ; continue ; }
                    rdfSimpleNodes.add(o) ;
                }
               
                if ( rdfLiterals.size() != 0 )
                {
                    writePredicateObjectList(p, rdfLiterals, predicateMaxWidth, first) ;
                    first = false ;
                }
                if ( rdfSimpleNodes.size() != 0 )
                {
                    writePredicateObjectList(p, rdfSimpleNodes, predicateMaxWidth, first) ;
                    first = false ;
                }
               
                for ( Node o : rdfComplexNodes )
                {
                    writePredicateObject(p, o, predicateMaxWidth, first) ;
                    first = false ;
                }
            }
        }
       
        private void writePredicateObject(Node p, Node obj, int predicateMaxWidth, boolean first)
        {
            writePredicate(p, predicateMaxWidth, first) ;
            out.incIndent(INDENT_OBJECT)
            writeNodePretty(obj) ;
            out.decIndent(INDENT_OBJECT) ;
        }

        private void writePredicateObjectList(Node p, List<Node> objects, int predicateMaxWidth, boolean first)
        {
            writePredicate(p, predicateMaxWidth, first) ;
            out.incIndent(INDENT_OBJECT)
            boolean firstObject = true ;
            for ( Node o : objects)
            {
                if ( ! firstObject )
                    out.print(" , ") ;
                else
                    firstObject = false ;
                writeNode(o) ;
                //writeNodePretty(obj) ;
            }
            out.decIndent(INDENT_OBJECT) ;
        }
   
           
        /** Write a predicate - jump to next line if deemed long */  
        private void writePredicate(Node p, int predicateMaxWidth, boolean first)
        {
            if ( first )
                first = false ;
            else
            {
                print(" ;") ;
                println() ;
            }
            int colPredicateStart = out.getAbsoluteIndent() ;
           
            if ( ! prefixMap.contains(rdfNS) && RDF_type.equals(p) )
                print("a") ;
            else
                writeNode(p) ;
            int colPredicateFinish = out.getCol() ;
            int wPredicate = (colPredicateFinish-colPredicateStart) ;

            if ( wPredicate > LONG_PREDICATE )
                println() ;
            else
            {
                out.pad(predicateMaxWidth) ;
                //out.print(' ', predicateMaxWidth-wPredicate) ;
                gap(GAP_P_O) ;
            }
        }
           
        private Map<Node, List<Node>> groupByPredicates(Collection<Triple> cluster)
        {
            SortedMap<Node, List<Node>> x = new TreeMap<Node, List<Node>>(compPredicates) ;
            for ( Triple t : cluster )
            {
                Node p = t.getPredicate() ;
                if ( ! x.containsKey(p) )
                    x.put(p, new ArrayList<Node>()) ;
                x.get(p).add(t.getObject()) ;
            }
           
            return x ;
        }

        private int countPredicates(Collection<Triple> cluster)
        {
            Set<Node> x = new HashSet<Node>() ;
            for ( Triple t : cluster )
            {
                Node p = t.getPredicate() ;
                x.add(p) ;
            }
            return x.size() ;
        }

        private void nestedObject(Node node)
        {
            Collection<Triple> x = RiotLib.triplesOfSubject(graph, node) ;

            if ( x.isEmpty() )
            {
                print("[] ") ;
                return ;
            }

            int pCount = countPredicates(x) ;
           
            if ( pCount == 1 )
            {
                print("[ ") ;
                out.incIndent(2) ;
                writePredicateObjectList(x) ;
                out.decIndent(2) ;
                print(" ]") ;
                return ;
            }

            // Two or more.
            int indent0 = out.getAbsoluteIndent() ;
            int here = out.getCol() ;
            out.setAbsoluteIndent(here) ;
            print("[ ") ;
            out.incIndent(2) ;
            writePredicateObjectList(x) ;
            out.decIndent(2) ;
            if ( true )
            {
                println() ; // Newline for "]"
                print("]") ;
            }
            else
            {   // Compact
                print(" ]") ;
            }
            out.setAbsoluteIndent(indent0) ;
        }
       
        // Write a list
        private void list(List<Node> elts)
        {
            if ( elts.size() == 0 )
            {
                out.print("()") ;
                return ;
            }
           
            out.print("(") ;
            for ( Node n : elts )
            {
                out.print(" ") ;
                writeNodePretty(n) ;
            }

            out.print(" )") ;
        }
       
        private boolean isPrettyNode(Node n)
        {
            // Order matters? - one connected objects may include list elements.
            if ( lists.containsKey(n) ) return true ;
            if ( nestedObjects.contains(n) ) return true ;
            if ( RDF_Nil.equals(n) ) return true ;
            return false ;
        }
       
        // --> write S or O??
        private void writeNodePretty(Node obj)
        {
            // Order matters? - one connected objects may include list elements.
            if ( lists.containsKey(obj) )
                list(lists.get(obj)) ;
            else if ( nestedObjects.contains(obj) )
                nestedObject(obj) ;
            else if ( RDF_Nil.equals(obj) )
                out.println("()") ;
            else
                writeNode(obj) ;
        }
    }
   
   
    // Order of properties.
    // rdf:type ("a")
    // RDF and RDFS
    // Other.
    //   Sorted by URI.
   
    private static final class ComparePredicates implements Comparator<Node>
    {
        private static int classification(Node p)
        {
            if ( p.equals(RDF_type) )
                return 0 ;
           
            if ( p.getURI().startsWith(RDF.getURI()) || 
                 p.getURI().startsWith(RDFS.getURI()) )
                return 1 ;
           
            return 2 ;
        }

        @Override
        public int compare(Node t1, Node t2)
        {
            int class1 = classification(t1) ;
            int class2 = classification(t2) ;
            if ( class1 != class2 )
            {
                // Java 1.7
                //return Integer.compare(class1, class2) ;
                if ( class1 < class2 ) return -1 ;
                if ( class1 > class2 ) return 1 ;
                return 0 ;
             }  
            String p1 = t1.getURI() ;
            String p2 = t2.getURI() ;
            return p1.compareTo(p2) ;
        }
    }

    private static Comparator<Node> compPredicates = new ComparePredicates() ;
   
    protected final void writeNode(Node node)
    {
        nodeFmt.format(out, node) ;
    }

    private void print(String x)
    {
        out.print(x) ;
    }

    private void gap(int gap)
    {
        out.print(' ', gap) ;
    }

    // flush aggressively (debugging)
    private void println()
    {
        out.println() ;
        //out.flush() ;
    }
}
TOP

Related Classes of org.apache.jena.riot.writer.TurtleShell$ComparePredicates

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.