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 2016/01/05 13:46:08 UTC

[14/20] jena git commit: JENA-1108 : jena-cmds module

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/query.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/query.java b/jena-cmds/src/main/java/arq/query.java
new file mode 100644
index 0000000..9a9ed5f
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/query.java
@@ -0,0 +1,257 @@
+/*
+ * 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 arq;
+
+import arq.cmdline.* ;
+import jena.cmd.ArgDecl;
+import jena.cmd.CmdException;
+import jena.cmd.TerminationException;
+import org.apache.jena.atlas.io.IndentedWriter ;
+import org.apache.jena.atlas.lib.Lib ;
+import org.apache.jena.atlas.logging.LogCtl ;
+import org.apache.jena.query.* ;
+import org.apache.jena.riot.RiotException ;
+import org.apache.jena.riot.RiotNotFoundException ;
+import org.apache.jena.riot.SysRIOT ;
+import org.apache.jena.shared.JenaException ;
+import org.apache.jena.sparql.ARQInternalErrorException ;
+import org.apache.jena.sparql.mgt.Explain ;
+import org.apache.jena.sparql.resultset.ResultSetException ;
+import org.apache.jena.sparql.resultset.ResultsFormat ;
+import org.apache.jena.sparql.util.QueryExecUtils ;
+
+public class query extends CmdARQ
+{
+    private ArgDecl argRepeat   = new ArgDecl(ArgDecl.HasValue, "repeat") ;
+    private ArgDecl argExplain  = new ArgDecl(ArgDecl.NoValue, "explain") ;
+    private ArgDecl argOptimize = new ArgDecl(ArgDecl.HasValue, "opt", "optimize") ;
+
+    protected int repeatCount = 1 ; 
+    protected int warmupCount = 0 ;
+    protected boolean queryOptimization = true ;
+    
+    protected ModTime       modTime =     new ModTime() ;
+    protected ModQueryIn    modQuery =    null;
+    protected ModDataset    modDataset =  null ;
+    protected ModResultsOut modResults =  new ModResultsOut() ;
+    protected ModEngine     modEngine =   new ModEngine() ;
+    
+    public static void main (String... argv)
+    {
+        new query(argv).mainRun() ;
+    }
+    
+    public query(String[] argv)
+    {
+        super(argv) ;
+        modQuery = new ModQueryIn(getDefaultSyntax()) ; 
+        modDataset = setModDataset() ;
+       
+        super.addModule(modQuery) ;
+        super.addModule(modResults) ;
+        super.addModule(modDataset) ;
+        super.addModule(modEngine) ;
+        super.addModule(modTime) ;
+
+        super.getUsage().startCategory("Control") ;
+        super.add(argExplain,  "--explain", "Explain and log query execution") ;
+        super.add(argRepeat,   "--repeat=N or N,M", "Do N times or N warmup and then M times (use for timing to overcome start up costs of Java)");
+        super.add(argOptimize, "--optimize=", "Turn the query optimizer on or off (default: on)") ;
+    }
+
+    /** Default syntax used when the syntax can not be determined from the command name or file extension
+     *  The order of determination is:
+     *  <ul>
+     *  <li>Explicitly given --syntax</li>
+     *  <li>File extension</li>
+     *  <li>Command default</li>
+     *  <li>System default</li>
+     *  </ul>
+     *  
+     */
+    protected Syntax getDefaultSyntax()     { return Syntax.defaultQuerySyntax ; }
+
+    @Override
+    protected void processModulesAndArgs()
+    {
+        super.processModulesAndArgs() ;
+        if ( contains(argRepeat) )
+        {
+            String[] x = getValue(argRepeat).split(",") ;
+            if ( x.length == 1 )
+            {
+                try { repeatCount = Integer.parseInt(x[0]) ; }
+                catch (NumberFormatException ex)
+                { throw new CmdException("Can't parse "+x[0]+" in arg "+getValue(argRepeat)+" as an integer") ; }
+                
+            }
+            else if ( x.length == 2 )
+            {
+                try { warmupCount = Integer.parseInt(x[0]) ; }
+                catch (NumberFormatException ex)
+                { throw new CmdException("Can't parse "+x[0]+" in arg "+getValue(argRepeat)+" as an integer") ; }
+                try { repeatCount = Integer.parseInt(x[1]) ; }
+                catch (NumberFormatException ex)
+                { throw new CmdException("Can't parse "+x[1]+" in arg "+getValue(argRepeat)+" as an integer") ; }
+            }
+            else
+                throw new CmdException("Wrong format for repeat count: "+getValue(argRepeat)) ;
+        }
+        if ( isVerbose() )
+            ARQ.getContext().setTrue(ARQ.symLogExec) ;
+        
+        if ( hasArg(argExplain) )
+            ARQ.setExecutionLogging(Explain.InfoLevel.ALL) ;
+        
+        if ( hasArg(argOptimize) )
+        {
+            String x1 = getValue(argOptimize) ;
+            if ( hasValueOfTrue(argOptimize) || x1.equalsIgnoreCase("on") || x1.equalsIgnoreCase("yes") ) 
+                queryOptimization = true ;
+            else if ( hasValueOfFalse(argOptimize) || x1.equalsIgnoreCase("off") || x1.equalsIgnoreCase("no") )
+                queryOptimization = false ;
+            else throw new CmdException("Optimization flag must be true/false/on/off/yes/no. Found: "+getValue(argOptimize)) ;
+        }
+    }
+    
+    protected ModDataset setModDataset()
+    {
+        return new ModDatasetGeneralAssembler() ;
+    }
+    
+    @Override
+    protected void exec()
+    {
+        if ( ! queryOptimization )
+            ARQ.getContext().setFalse(ARQ.optimization) ;
+        if ( cmdStrictMode )
+            ARQ.getContext().setFalse(ARQ.optimization) ;
+        
+        // Warm up.
+        for ( int i = 0 ; i < warmupCount ; i++ )
+        {
+            queryExec(false, ResultsFormat.FMT_NONE) ;
+        }
+        
+        for ( int i = 0 ; i < repeatCount ; i++ )
+            queryExec(modTime.timingEnabled(),  modResults.getResultsFormat()) ;
+        
+        if ( modTime.timingEnabled() && repeatCount > 1 )
+        {
+            long avg = totalTime/repeatCount ;
+            String avgStr = modTime.timeStr(avg) ;
+            System.err.println("Total time: "+modTime.timeStr(totalTime)+" sec for repeat count of "+repeatCount+ " : average: "+avgStr) ;
+        }
+    }
+
+    @Override
+    protected String getCommandName() { return Lib.className(this) ; }
+    
+    @Override
+    protected String getSummary() { return getCommandName()+" --data=<file> --query=<query>" ; }
+    
+    protected Dataset getDataset()  { 
+        try {
+            Dataset ds = modDataset.getDataset();
+            if ( ds == null )
+                ds = dealWithNoDataset();
+            return ds;
+        }
+        catch (RiotNotFoundException ex) {
+            System.err.println("Failed to load data: " + ex.getMessage());
+            throw new TerminationException(1);
+        }
+        catch (RiotException ex) {
+            System.err.println("Failed to load data");
+            throw new TerminationException(1);
+        }
+    }
+    
+    protected Dataset dealWithNoDataset()  {
+        return DatasetFactory.create() ;
+        //throw new CmdException("No dataset provided") ; 
+    }
+    
+    protected long totalTime = 0 ;
+    protected void queryExec(boolean timed, ResultsFormat fmt)
+    {
+        try{
+            Query query = modQuery.getQuery() ;
+            if ( isVerbose() )
+            {
+                IndentedWriter out = new IndentedWriter(System.out, true) ;
+                query.serialize(out) ;
+                out.flush() ;
+                System.out.println();
+            }
+            
+            if ( isQuiet() )
+                LogCtl.setError(SysRIOT.riotLoggerName) ;
+            Dataset dataset = getDataset() ;
+            modTime.startTimer() ;
+            QueryExecution qe = QueryExecutionFactory.create(query, dataset) ;
+            // Check there is a dataset
+            
+            if ( dataset == null && ! query.hasDatasetDescription() )
+            {
+                System.err.println("Dataset not specified in query nor provided on command line.");
+                throw new TerminationException(1) ;
+            }
+            try { QueryExecUtils.executeQuery(query, qe, fmt) ; } 
+            catch (QueryCancelledException ex) { 
+                System.out.flush() ;
+                System.err.println("Query timed out") ;
+            }
+            
+            long time = modTime.endTimer() ;
+            if ( timed )
+            {
+                totalTime += time ;
+                System.err.println("Time: "+modTime.timeStr(time)+" sec") ;
+            }
+            qe.close() ;
+        }
+        catch (ARQInternalErrorException intEx)
+        {
+            System.err.println(intEx.getMessage()) ;
+            if ( intEx.getCause() != null )
+            {
+                System.err.println("Cause:") ;
+                intEx.getCause().printStackTrace(System.err) ;
+                System.err.println() ;
+            }
+            intEx.printStackTrace(System.err) ;
+        }
+        catch (ResultSetException ex)
+        {
+            System.err.println(ex.getMessage()) ;
+            ex.printStackTrace(System.err) ;
+        }
+        catch (QueryException qEx)
+        {
+            //System.err.println(qEx.getMessage()) ;
+            throw new CmdException("Query Exeception", qEx) ;
+        }
+        catch (JenaException | CmdException ex) { throw ex ; }
+        catch (Exception ex)
+        {
+            throw new CmdException("Exception", ex) ;
+        }
+    }    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/rdfdiff.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/rdfdiff.java b/jena-cmds/src/main/java/arq/rdfdiff.java
new file mode 100644
index 0000000..78a51bb
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/rdfdiff.java
@@ -0,0 +1,312 @@
+/*
+ * 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 arq;
+
+import static org.apache.jena.atlas.logging.LogCtl.setCmdLogging;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.io.FileInputStream;
+
+import org.apache.jena.rdf.model.*;
+import org.apache.jena.sparql.util.Closure;
+
+/**
+ * A program which read two RDF models and provides a basic triple level diff
+ *
+ * <p>
+ * This program will read two RDF models, in a variety of languages, and compare
+ * them providing a basic triple level diff output. Since blank nodes are a
+ * complicating factor diffs for blank node containing portions of the graph are
+ * reported in terms of sub-graphs rather than individual triples.
+ * </p>
+ * <p>
+ * Input can be read either from a URL or from a file. The program writes its
+ * results to the standard output stream and sets its exit code to 0 if the
+ * models are equal, to 1 if they are not and to -1 if it encounters an error.
+ * </p>
+ *
+ * <p>
+ * </p>
+ *
+ * <pre>
+ * java jena.rdfcompare model1 model2 [lang1 [lang2]]
+ *
+ *       model1 and model2 can be file names or URL's
+ *       lang1 and lang2 specify the language of the input and can be:
+ *           RDF/XML
+ *           N-TRIPLE
+ *           N3
+ * </pre>
+ */
+public class rdfdiff extends java.lang.Object {
+
+    static {
+        setCmdLogging();
+    }
+
+    /**
+     * @param args
+     *            the command line arguments
+     */
+    public static void main(String... args) {
+        if (args.length < 2 || args.length > 6) {
+            usage();
+            System.exit(-1);
+        }
+
+        String in1 = args[0];
+        String in2 = args[1];
+        String lang1 = "RDF/XML";
+        if (args.length >= 3) {
+            lang1 = args[2];
+        }
+        String lang2 = "N-TRIPLE";
+        if (args.length >= 4) {
+            lang2 = args[3];
+        }
+        String base1 = null;
+        if (args.length >= 5) {
+            base1 = args[4];
+        }
+        String base2 = base1;
+        if (args.length >= 6) {
+            base2 = args[5];
+        }
+
+        System.out.println(in1 + " " + in2 + " " + lang1 + " " + lang2 + " " + base1 + " " + base2);
+        try {
+            Model m1 = ModelFactory.createDefaultModel();
+            Model m2 = ModelFactory.createDefaultModel();
+
+            read(m1, in1, lang1, base1);
+            read(m2, in2, lang2, base2);
+
+            if (m1.isIsomorphicWith(m2)) {
+                System.out.println("models are equal");
+                System.out.println();
+                System.exit(0);
+            } else {
+                System.out.println("models are unequal");
+                System.out.println();
+
+                if (m1.size() != m2.size()) {
+                    System.out.println(String.format("< %,d triples", m1.size()));
+                    System.out.println(String.format("> %,d triples", m2.size()));
+                }
+
+                // Calculate differences
+                Map<AnonId, Model> m1SubGraphs = new HashMap<>();
+                StmtIterator iter = m1.listStatements();
+                while (iter.hasNext()) {
+                    Statement stmt = iter.next();
+                    if (stmt.asTriple().isConcrete()) {
+                        if (!m2.contains(stmt)) {
+                            System.out.print("< ");
+                            System.out.println(stmt.toString());
+                        }
+                    } else {
+                        // Handle blank nodes via sub-graphs
+                        addToSubGraph(stmt, m1SubGraphs);
+                    }
+                }
+
+                Map<AnonId, Model> m2SubGraphs = new HashMap<>();
+                iter = m2.listStatements();
+                while (iter.hasNext()) {
+                    Statement stmt = iter.next();
+                    if (stmt.asTriple().isConcrete()) {
+                        if (!m1.contains(stmt)) {
+                            System.out.print("> ");
+                            System.out.println(stmt.toString());
+                        }
+                    } else {
+                        // Handle blank nodes via sub-graphs
+                        addToSubGraph(stmt, m2SubGraphs);
+                    }
+                }
+
+                // Compute sub-graph differences
+
+                // Reduce to sets
+                Set<Model> m1SubGraphSet = new TreeSet<>(new ModelReferenceComparator());
+                m1SubGraphSet.addAll(m1SubGraphs.values());
+                Set<Model> m2SubGraphSet = new TreeSet<>(new ModelReferenceComparator());
+                m2SubGraphSet.addAll(m2SubGraphs.values());
+
+                if (m1SubGraphSet.size() != m2SubGraphSet.size()) {
+                    System.out.println("< " + m1SubGraphs.size() + " sub-graphs");
+                    System.out.println("> " + m2SubGraphs.size() + " sub-graphs");
+                }
+                if (m1SubGraphSet.size() > 0) {
+                    diffSubGraphs(m1SubGraphSet, m2SubGraphSet, "< ");
+                }
+                if (m2SubGraphSet.size() > 0) {
+                    diffSubGraphs(m2SubGraphSet, m1SubGraphSet, "> ");
+                }
+
+                System.exit(1);
+            }
+        } catch (Exception e) {
+            System.err.println("Unhandled exception:");
+            System.err.println("    " + e.toString());
+            System.exit(-1);
+        }
+    }
+
+    private static void diffSubGraphs(Set<Model> m1SubGraphSet, Set<Model> m2SubGraphSet, String prefix) {
+        for (Model subGraph : m1SubGraphSet) {
+            // Find candidate matches
+            List<Model> candidates = new ArrayList<>();
+            for (Model subGraphCandidate : m2SubGraphSet) {
+                if (subGraph.size() == subGraphCandidate.size()) {
+                    candidates.add(subGraph);
+                }
+            }
+
+            if (candidates.size() == 0) {
+                // No match
+                printNonMatchingSubGraph(prefix, subGraph);
+            } else if (candidates.size() == 1) {
+                // Precisely 1 candidate
+                if (!subGraph.isIsomorphicWith(candidates.get(0))) {
+                    printNonMatchingSubGraph(prefix, subGraph);
+                } else {
+                    m2SubGraphSet.remove(candidates.get(0));
+                }
+            } else {
+                // Multiple candidates
+                boolean matched = false;
+                for (Model subGraphCandidate : candidates) {
+                    if (subGraph.isIsomorphicWith(subGraphCandidate)) {
+                        // Found a match
+                        matched = true;
+                        m2SubGraphSet.remove(subGraphCandidate);
+                        break;
+                    }
+                }
+
+                if (!matched) {
+                    // Didn't find a match
+                    printNonMatchingSubGraph(prefix, subGraph);
+                }
+            }
+        }
+    }
+
+    private static void printNonMatchingSubGraph(String prefix, Model subGraph) {
+        StmtIterator sIter = subGraph.listStatements();
+        while (sIter.hasNext()) {
+            System.out.print(prefix);
+            System.out.println(sIter.next().toString());
+        }
+    }
+
+    private static void addToSubGraph(Statement stmt, Map<AnonId, Model> subGraphs) {
+        Set<AnonId> ids = new HashSet<>();
+
+        addToIdList(stmt, ids);
+
+        // Here we take a copy of the IDs
+        Model subGraph = null;
+        for (AnonId id : ids) {
+            if (!subGraphs.containsKey(id)) {
+                subGraph = Closure.closure(stmt);
+                subGraph.add(stmt);
+                break;
+            }
+        }
+
+        // May already have built the sub-graph that includes this statement
+        if (subGraph == null)
+            return;
+
+        // Find any further IDs that occur in the sub-graph
+        StmtIterator sIter = subGraph.listStatements();
+        while (sIter.hasNext()) {
+            addToIdList(sIter.next(), ids);
+        }
+
+        // Associate the sub-graph with all mentioned blank node IDs
+        for (AnonId id : ids) {
+            if (subGraphs.containsKey(id))
+                throw new IllegalStateException(String.format("ID %s occurs in multiple sub-graphs", id));
+            subGraphs.put(id, subGraph);
+        }
+    }
+
+    private static void addToIdList(Statement stmt, Set<AnonId> ids) {
+        if (stmt.getSubject().isAnon()) {
+            ids.add(stmt.getSubject().getId());
+        }
+        if (stmt.getObject().isAnon()) {
+            ids.add(stmt.getObject().asResource().getId());
+        }
+    }
+
+    protected static void usage() {
+        System.err.println("usage:");
+        System.err.println("    java jena.rdfdiff source1 source2 [lang1 [lang2 [base1 [base2]]]]");
+        System.err.println();
+        System.err.println("    source1 and source2 can be URL's or filenames");
+        System.err.println("    lang1 and lang2 can take values:");
+        System.err.println("      RDF/XML");
+        System.err.println("      N-TRIPLE");
+        System.err.println("      N3");
+        System.err.println("    lang1 defaults to RDF/XML, lang2 to N-TRIPLE");
+        System.err.println("    base1 and base2 are URIs");
+        System.err.println("    base1 defaults to null");
+        System.err.println("    base2 defaults to base1");
+        System.err.println("    If no base URIs are specified Jena determines the base URI based on the input source");
+
+        System.err.println();
+    }
+
+    protected static void read(Model model, String in, String lang, String base) throws java.io.FileNotFoundException {
+        try {
+            URL url = new URL(in);
+            model.read(in, base, lang);
+        } catch (java.net.MalformedURLException e) {
+            model.read(new FileInputStream(in), base, lang);
+        }
+    }
+
+    private static class ModelReferenceComparator implements Comparator<Model> {
+
+        @Override
+        public int compare(Model o1, Model o2) {
+            if (o1 == o2)
+                return 0;
+            int h1 = System.identityHashCode(o1);
+            int h2 = System.identityHashCode(o2);
+
+            if (h1 == h2)
+                return 0;
+            return h1 < h2 ? -1 : 1;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/rset.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/rset.java b/jena-cmds/src/main/java/arq/rset.java
new file mode 100644
index 0000000..b1cbc3e
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/rset.java
@@ -0,0 +1,73 @@
+/*
+ * 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 arq;
+
+import org.apache.jena.query.ResultSet ;
+import arq.cmdline.CmdARQ ;
+import arq.cmdline.ModResultsIn ;
+import arq.cmdline.ModResultsOut ;
+
+/** Read and write result sets */
+
+public class rset extends CmdARQ
+{
+    ModResultsIn modInput    = new ModResultsIn() ;
+    ModResultsOut modOutput  = new ModResultsOut() ;
+    
+    static String usage = rset.class.getName()+
+            " [--in syntax] [--out syntax] [--file FILE | FILE ]" ; 
+
+    public static void main(String... argv)
+    {
+        new rset(argv).mainRun() ;
+    }
+
+    public rset(String[] argv)
+    {
+        super(argv) ;
+        super.addModule(modInput) ;
+        super.addModule(modOutput) ;
+    }
+            
+    @Override
+    protected void processModulesAndArgs()
+    {
+        super.processModulesAndArgs() ;
+    }
+
+    @Override
+    protected String getSummary()
+    {
+        return usage ;
+    }
+
+    @Override
+    protected void exec()
+    {
+        ResultSet rs = modInput.getResultSet() ;
+        modOutput.printResultSet(rs, null) ;
+    }
+
+    @Override
+    protected String getCommandName()
+    {
+        return "rset" ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/rsparql.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/rsparql.java b/jena-cmds/src/main/java/arq/rsparql.java
new file mode 100644
index 0000000..bf87dfd
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/rsparql.java
@@ -0,0 +1,95 @@
+/*
+ * 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 arq;
+
+import jena.cmd.CmdException;
+
+import org.apache.jena.query.Query ;
+import org.apache.jena.query.QueryExecution ;
+import org.apache.jena.query.QueryExecutionFactory ;
+import org.apache.jena.query.Syntax ;
+import org.apache.jena.sparql.engine.http.HttpQuery ;
+import org.apache.jena.sparql.engine.http.QueryExceptionHTTP ;
+import org.apache.jena.sparql.util.QueryExecUtils ;
+
+import arq.cmdline.CmdARQ ;
+import arq.cmdline.ModQueryIn ;
+import arq.cmdline.ModRemote ;
+import arq.cmdline.ModResultsOut ;
+
+public class rsparql extends CmdARQ
+{
+    protected ModQueryIn    modQuery =      new ModQueryIn(Syntax.syntaxSPARQL_11) ;
+    protected ModRemote     modRemote =     new ModRemote() ;
+    protected ModResultsOut modResults =    new ModResultsOut() ;
+
+    public static void main (String... argv)
+    {
+        new rsparql(argv).mainRun() ;
+    }
+
+
+    public rsparql(String[] argv)
+    {
+        super(argv) ;
+        super.addModule(modRemote) ;
+        super.addModule(modQuery) ;
+        super.addModule(modResults) ;
+    }
+    
+    
+    @Override
+    protected void processModulesAndArgs()
+    {
+        super.processModulesAndArgs() ;
+        if ( modRemote.getServiceURL() == null )
+            throw new CmdException("No SPARQL endpoint specificied") ;
+    }
+    
+    @Override
+    protected void exec()
+    {
+        Query query = modQuery.getQuery() ;
+
+        try {
+            String serviceURL = modRemote.getServiceURL() ;
+            QueryExecution qe = QueryExecutionFactory.sparqlService(serviceURL, query) ;
+            if ( modRemote.usePost() )
+                HttpQuery.urlLimit = 0 ;
+
+            QueryExecUtils.executeQuery(query, qe, modResults.getResultsFormat()) ;
+        } catch (QueryExceptionHTTP ex)
+        {
+            throw new CmdException("HTTP Exeception", ex) ;
+        }
+        catch (Exception ex)
+        {
+            System.out.flush() ;
+            ex.printStackTrace(System.err) ;
+        }
+    }
+
+
+    @Override
+    protected String getSummary()
+    {
+        return null ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/rupdate.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/rupdate.java b/jena-cmds/src/main/java/arq/rupdate.java
new file mode 100644
index 0000000..8ef988c
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/rupdate.java
@@ -0,0 +1,98 @@
+/**
+ * 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 arq;
+
+import java.util.List ;
+
+import jena.cmd.ArgDecl;
+import jena.cmd.CmdException;
+
+import org.apache.jena.update.UpdateExecutionFactory ;
+import org.apache.jena.update.UpdateFactory ;
+import org.apache.jena.update.UpdateProcessor ;
+import org.apache.jena.update.UpdateRequest ;
+
+import arq.cmdline.CmdARQ ;
+import arq.cmdline.ModRemote ;
+
+public class rupdate extends CmdARQ
+{
+    static final ArgDecl updateArg = new ArgDecl(ArgDecl.HasValue, "update", "file") ;
+    
+    protected ModRemote     modRemote =     new ModRemote() ;
+
+    List<String> requestFiles = null ;
+
+    public static void main(String[] argv)
+    {
+        new rupdate(argv).mainRun() ;
+    }
+
+    protected rupdate(String[] argv)
+    {
+        super(argv) ;
+        super.add(updateArg, "--update=FILE", "Update commands to execute") ;
+        super.addModule(modRemote) ;
+    }
+
+    @Override
+    protected void processModulesAndArgs()
+    {
+        requestFiles = getValues(updateArg) ;   // ????
+        super.processModulesAndArgs() ;
+    }
+    
+    
+    @Override
+    protected String getSummary()
+    {
+        return getCommandName()+" --service=URL --update=<request file>" ;
+    }
+
+    @Override
+    protected void exec()
+    {
+        if ( modRemote.getServiceURL() == null )
+        {
+            throw new CmdException("No endpoint given") ;
+        }
+        String endpoint = modRemote.getServiceURL() ;
+
+        for ( String filename : requestFiles )
+        {
+            UpdateRequest req = UpdateFactory.read( filename );
+            exec( endpoint, req );
+        }
+
+        for ( String requestString : super.getPositional() )
+        {
+            requestString = indirect( requestString );
+            UpdateRequest req = UpdateFactory.create( requestString );
+            exec( endpoint, req );
+        }
+    }
+
+    private void exec(String endpoint, UpdateRequest req) 
+    {
+        UpdateProcessor proc = UpdateExecutionFactory.createRemote(req, endpoint) ;
+        proc.execute() ;
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/sparql.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/sparql.java b/jena-cmds/src/main/java/arq/sparql.java
new file mode 100644
index 0000000..d42ba7f
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/sparql.java
@@ -0,0 +1,37 @@
+/*
+ * 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 arq;
+
+import org.apache.jena.query.Syntax ;
+
+/** A program to execute queries from the command line in SPARQL mode. */
+
+public class sparql extends query
+{
+    public static void main (String... argv) {
+        new sparql(argv).mainRun() ;
+    }
+    
+    public sparql(String[] argv) {
+        super(argv) ; 
+    }
+
+    @Override
+    protected Syntax getDefaultSyntax()     { return Syntax.syntaxSPARQL_11 ; } 
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/sse.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/sse.java b/jena-cmds/src/main/java/arq/sse.java
new file mode 100644
index 0000000..95a769e
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/sse.java
@@ -0,0 +1,108 @@
+/*
+ * 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 arq;
+
+import jena.cmd.ArgDecl;
+import jena.cmd.TerminationException;
+
+import org.apache.jena.atlas.io.IndentedWriter ;
+import org.apache.jena.atlas.lib.Lib ;
+import org.apache.jena.shared.PrefixMapping ;
+import org.apache.jena.sparql.serializer.SerializationContext ;
+import org.apache.jena.sparql.sse.Item ;
+import org.apache.jena.sparql.sse.ItemWriter ;
+import org.apache.jena.sparql.sse.SSE ;
+
+import arq.cmdline.CmdARQ_SSE ;
+
+public class sse extends CmdARQ_SSE
+{
+    protected final ArgDecl numberDecl      = new ArgDecl(ArgDecl.HasValue, "num", "number") ;
+    protected final ArgDecl noPrintDecl     = new ArgDecl(ArgDecl.NoValue, "n") ;
+    protected final ArgDecl noResolveDecl   = new ArgDecl(ArgDecl.NoValue, "raw") ;
+
+    private boolean         print       = true ;
+    private boolean         structural  = true ;
+    private boolean         lineNumbers = false ;
+
+    public static void main (String... argv)
+    {
+        new sse(argv).mainRun() ;
+    }
+
+    public sse(String[] argv)
+    {
+        super(argv) ;
+        super.add(noPrintDecl,      "-n",               "Don't print the expression") ;
+        super.add(numberDecl,       "--num [on|off]",   "Numbers") ;
+        super.add(noResolveDecl,    "--raw", "Don't handle base or prefix names specially") ;
+    }
+
+    @Override
+    protected void processModulesAndArgs()
+    {
+        super.processModulesAndArgs() ;
+        print = !contains(noPrintDecl) ;
+        if ( contains(numberDecl) )
+            lineNumbers = getValue(numberDecl).equalsIgnoreCase("on") ;
+        
+        if ( contains(noResolveDecl) )
+            SSE.setUseResolver(false) ;
+    }
+
+    @Override
+    protected String getCommandName() { return Lib.className(this) ; }
+
+    @Override
+    protected String getSummary() { return getCommandName() ; }
+
+    static final String divider = "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" ;
+    //static final String divider = "" ;
+
+    boolean needDivider = false ;
+    private void divider()
+    {
+        if ( needDivider ) System.out.println(divider) ;
+        needDivider = true ;
+    }
+
+    @Override
+    protected void exec(Item item)
+    {
+        if ( ! print )
+            return ;
+        
+        if ( item == null )
+        {
+            System.err.println("No expression") ;
+            throw new TerminationException(9) ;
+        }
+        divider() ;
+        IndentedWriter out = new IndentedWriter(System.out, lineNumbers) ;
+        
+        // Need to check if used.
+        //PrefixMapping pmap = SSE.getDefaultPrefixMapWrite() ;
+        PrefixMapping pmap = null ;
+        SerializationContext sCxt = new SerializationContext(pmap) ;
+        ItemWriter.write(out, item, sCxt) ;
+        //item.output(out) ;
+        out.ensureStartOfLine() ;
+        out.flush();
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/sse_exec.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/sse_exec.java b/jena-cmds/src/main/java/arq/sse_exec.java
new file mode 100644
index 0000000..3b5f49c
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/sse_exec.java
@@ -0,0 +1,50 @@
+/*
+ * 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 arq;
+
+import org.apache.jena.atlas.lib.Lib ;
+import org.apache.jena.sparql.sse.Item ;
+import org.apache.jena.sparql.sse.builders.BuilderExec ;
+import arq.cmdline.CmdARQ_SSE ;
+
+public class sse_exec extends CmdARQ_SSE
+{
+    
+    public static void main (String... argv)
+    {
+        new sse_exec(argv).mainRun() ;
+    }
+    
+    public sse_exec(String[] argv)
+    {
+        super(argv) ;
+    }
+    
+    @Override
+    protected String getCommandName() { return Lib.className(this) ; }
+    
+    @Override
+    protected String getSummary() { return getCommandName()+" [--file<file> | string]" ; }
+
+    @Override
+    protected void exec(Item item)
+    {
+        BuilderExec.exec(item) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/sse_expr.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/sse_expr.java b/jena-cmds/src/main/java/arq/sse_expr.java
new file mode 100644
index 0000000..9339d53
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/sse_expr.java
@@ -0,0 +1,24 @@
+/*
+ * 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 arq;
+
+public class sse_expr
+{
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/sse_query.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/sse_query.java b/jena-cmds/src/main/java/arq/sse_query.java
new file mode 100644
index 0000000..c5d8c7f
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/sse_query.java
@@ -0,0 +1,162 @@
+/*
+ * 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 arq;
+
+
+import jena.cmd.ArgDecl;
+import jena.cmd.CmdException;
+import jena.cmd.TerminationException;
+
+import org.apache.jena.atlas.io.IndentedWriter ;
+import org.apache.jena.atlas.lib.Lib ;
+import org.apache.jena.query.Dataset ;
+import org.apache.jena.query.DatasetFactory ;
+import org.apache.jena.sparql.algebra.Algebra ;
+import org.apache.jena.sparql.algebra.Op ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.engine.Plan ;
+import org.apache.jena.sparql.engine.PlanOp ;
+import org.apache.jena.sparql.engine.QueryIterator ;
+import org.apache.jena.sparql.util.QueryExecUtils ;
+
+import arq.cmdline.CmdARQ ;
+import arq.cmdline.ModAlgebra ;
+import arq.cmdline.ModDataset ;
+import arq.cmdline.ModDatasetGeneralAssembler ;
+import arq.cmdline.ModEngine ;
+import arq.cmdline.ModResultsOut ;
+import arq.cmdline.ModTime ;
+
+public class sse_query extends CmdARQ
+{
+    // Merging with qparse/sparql
+    // 1 - split those two into Query and QueryExecution parts
+    // 2 - This is then calls on the QueryExecution parts
+    // 3 - Printing plan - uses a verbose prefix setting.  Scan to see what's in use.
+    //      WriterOp.reducePrologue(prologue, op) => prologue.
+    
+    protected final ArgDecl printDecl  = new ArgDecl(ArgDecl.HasValue, "print") ;
+    
+    ModAlgebra    modAlgebra =  new ModAlgebra() ;
+    ModDataset    modDataset =  new ModDatasetGeneralAssembler() ;
+    ModResultsOut modResults =  new ModResultsOut() ;
+    ModTime       modTime =     new ModTime() ;
+    ModEngine     modEngine =   new ModEngine() ;
+
+    boolean printOp      = false ;
+    boolean printPlan    = false ;
+    
+    public static void main (String... argv)
+    {
+        new sse_query(argv).mainRun() ;
+    }
+    
+    public sse_query(String[] argv)
+    {
+        super(argv) ;
+        super.add(printDecl, "--print=op/plan",  "Print details") ;
+        super.addModule(modAlgebra) ;
+        super.addModule(modResults) ;
+        super.addModule(modDataset) ;
+        super.addModule(modTime) ;
+        super.addModule(modEngine) ;
+    }
+
+    @Override
+    protected void processModulesAndArgs()
+    {
+        super.processModulesAndArgs() ;
+
+        for (String arg : getValues(printDecl))
+        {
+            if ( arg.equalsIgnoreCase("op") ||
+                      arg.equalsIgnoreCase("alg") || 
+                      arg.equalsIgnoreCase("algebra") ) { printOp = true ; }
+            else if ( arg.equalsIgnoreCase("plan"))     { printPlan = true ; }
+            else
+                throw new CmdException("Not a recognized print form: "+arg+" : Choices are: query, op, quad") ;
+        }
+        
+    }
+    
+    @Override
+    protected String getCommandName() { return Lib.className(this) ; }
+    
+    @Override
+    protected String getSummary() { return getCommandName()+" --data=<file> --query=<query>" ; }
+
+    static final String divider = "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" ;
+    //static final String divider = "" ;
+    boolean needDivider = false ;
+    private void divider()
+    {
+        if ( needDivider ) System.out.println(divider) ;
+        needDivider = true ;
+    }
+    
+    @Override
+    protected void exec()
+    {
+        Op op = modAlgebra.getOp() ;
+
+        if ( op == null )
+        {
+            System.err.println("No query expression to execute") ;
+            throw new TerminationException(9) ;
+        }
+
+        Dataset dataset = modDataset.getDataset() ;
+        // Check there is a dataset.
+        if ( dataset == null )
+            dataset = DatasetFactory.createGeneral() ;
+
+        modTime.startTimer() ;
+        DatasetGraph dsg = dataset.asDatasetGraph() ;
+
+        if ( printOp || printPlan )
+        {
+            if ( printOp )
+            {
+                divider() ;
+                IndentedWriter out = new IndentedWriter(System.out, true) ;
+                op.output(out) ;
+                out.flush();
+            }
+
+            if ( printPlan )
+            {
+                QueryIterator qIter = Algebra.exec(op, dsg) ;
+                Plan plan = new PlanOp(op, null, qIter) ;
+                divider() ;
+                IndentedWriter out = new IndentedWriter(System.out, false) ;
+                plan.output(out) ;
+                out.flush();
+            }
+            //return ;
+        }
+
+        // Do not optimize.  Execute as-is.
+        QueryExecUtils.execute(op, dsg, modResults.getResultsFormat()) ;
+
+        long time = modTime.endTimer() ;
+        if ( modTime.timingEnabled() )
+            System.out.println("Time: "+modTime.timeStr(time)) ;
+    }    
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/tokens.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/tokens.java b/jena-cmds/src/main/java/arq/tokens.java
new file mode 100644
index 0000000..a7cbbd2
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/tokens.java
@@ -0,0 +1,24 @@
+/*
+ * 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 arq;
+
+public class tokens
+{
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/uparse.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/uparse.java b/jena-cmds/src/main/java/arq/uparse.java
new file mode 100644
index 0000000..83d44cb
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/uparse.java
@@ -0,0 +1,187 @@
+/*
+ * 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 arq;
+
+import java.io.IOException ;
+import java.util.List ;
+
+import jena.cmd.ArgDecl;
+import jena.cmd.CmdException;
+
+import org.apache.jena.atlas.io.IndentedLineBuffer ;
+import org.apache.jena.atlas.lib.Lib ;
+import org.apache.jena.query.QueryParseException ;
+import org.apache.jena.query.Syntax ;
+import org.apache.jena.sparql.modify.request.UpdateWriter ;
+import org.apache.jena.update.UpdateFactory ;
+import org.apache.jena.update.UpdateRequest ;
+import org.apache.jena.util.FileUtils ;
+
+import arq.cmdline.CmdARQ ;
+
+public class uparse extends CmdARQ
+{
+    protected static final ArgDecl fileArg          = new ArgDecl(ArgDecl.HasValue, "file", "update") ;
+    protected static final ArgDecl syntaxArg        = new ArgDecl(ArgDecl.HasValue, "syntax", "syn") ;
+    protected static final ArgDecl argDeclPrint     = new ArgDecl(ArgDecl.HasValue, "print") ;
+    List<String> requestFiles = null ;
+    protected Syntax updateSyntax = null ;
+    private boolean printUpdate = false ;
+    private boolean printNone  = false ;
+    
+    public static void main (String... argv)
+    { new uparse(argv).mainRun() ; }
+    
+    protected uparse(String[] argv)
+    {
+        super(argv) ;
+        super.add(fileArg, "--file=FILE",  "Update commands to parse") ;
+        super.add(syntaxArg, "--syntax=name", "Update syntax") ;
+        super.add(argDeclPrint, "--print", "Print in various forms [update, none]") ;
+    }
+
+    @Override
+    protected void processModulesAndArgs()
+    {
+        requestFiles = getValues(fileArg) ;
+        super.processModulesAndArgs() ;
+        if ( super.cmdStrictMode )
+            updateSyntax = Syntax.syntaxSPARQL_11 ;
+
+        // Set syntax
+        if ( super.contains(syntaxArg) ) {
+            // short name
+            String s = super.getValue(syntaxArg) ;
+            Syntax syn = Syntax.lookup(s) ;
+            if ( syn == null )
+                super.cmdError("Unrecognized syntax: " + s) ;
+            updateSyntax = syn ;
+        }
+        
+        for ( String arg : getValues( argDeclPrint ) )
+        {
+            if ( arg.equalsIgnoreCase( "query" ) )
+                printUpdate = true;
+            else if ( arg.equalsIgnoreCase( "none" ) )
+                printNone = true;
+            else
+                throw new CmdException("Not a recognized print form: " + arg + " : Choices are: update, none" );
+        }
+        
+        if ( !printUpdate && ! printNone )
+            printUpdate = true ;
+        
+    }
+    
+    @Override
+    protected String getCommandName() { return Lib.className(this) ; }
+    
+    @Override
+    protected String getSummary() { return getCommandName()+" --file=<request file> | <update string>" ; }
+
+    @Override
+    protected void exec()
+    {
+        for ( String filename : requestFiles )
+        {
+            Syntax syntax = updateSyntax ;
+            if ( syntax == null )
+                syntax = Syntax.guessUpdateFileSyntax(filename) ;
+            String x = oneFile( filename );
+            if ( x != null )
+                execOne( x, syntax );
+        }
+
+        
+        
+        
+        for ( String x : super.positionals ) {
+            Syntax syntax = updateSyntax ;    
+            if ( matchesIndirect(x) ) {
+                if ( syntax == null )
+                    syntax = Syntax.guessUpdateFileSyntax(x) ;
+                x = indirect( x );
+            }
+            if ( syntax == null )
+                syntax = Syntax.defaultUpdateSyntax ;
+            execOne( x, syntax );
+        }
+
+    }
+    
+    private String oneFile(String filename)
+    {
+        divider() ;
+        try
+        {
+            return FileUtils.readWholeFileAsUTF8(filename) ;
+        } catch (IOException ex)
+        {
+            System.err.println("No such file: "+filename) ;
+            return null ;
+        }
+    }
+    
+    private void execOne(String updateString, Syntax syntax)
+    {
+        UpdateRequest req ; 
+        try {
+            req = UpdateFactory.create(updateString, syntax) ;
+        } catch (QueryParseException ex)
+        {
+            System.err.print("Parse error: ") ;
+            System.err.println(ex.getMessage()) ;
+            return ; 
+        }
+        //req.output(IndentedWriter.stderr) ;
+        if ( printUpdate )
+            System.out.print(req) ;
+        
+        if ( printNone )
+            return ;
+        
+        // And some checking.
+        IndentedLineBuffer w = new IndentedLineBuffer() ;
+        UpdateWriter.output(req, w) ;
+        String updateString2 = w.asString() ;
+        UpdateRequest req2 = null ;
+        try {
+            req2 = UpdateFactory.create(updateString2, syntax) ;
+        } catch (QueryParseException ex)
+        {
+            System.err.println("Can not reparse update after serialization") ;
+            System.err.println(updateString2) ; 
+        }
+
+        if ( ! req.equalTo(req2) )
+            System.err.println("Reparsed update does not .equalTo original parsed request") ;
+        
+        
+    }
+    
+    static final String divider = "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" ;
+    //static final String divider = "" ;
+    static boolean needDivider = false ;
+    private static void divider()
+    {
+        if ( needDivider ) System.out.println(divider) ;
+        needDivider = true ;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/update.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/update.java b/jena-cmds/src/main/java/arq/update.java
new file mode 100644
index 0000000..333d41a
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/update.java
@@ -0,0 +1,128 @@
+/*
+ * 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 arq;
+
+import java.util.List ;
+
+import jena.cmd.ArgDecl;
+import jena.cmd.CmdException;
+
+import org.apache.jena.atlas.lib.Lib ;
+import org.apache.jena.query.ReadWrite ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.sparql.SystemARQ ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.DatasetGraphFactory ;
+import org.apache.jena.sparql.core.Transactional ;
+import org.apache.jena.sparql.core.TransactionalNull ;
+import org.apache.jena.update.UpdateExecutionFactory ;
+import org.apache.jena.update.UpdateFactory ;
+import org.apache.jena.update.UpdateRequest ;
+
+import arq.cmdline.CmdUpdate ;
+
+public class update extends CmdUpdate
+{
+    static final ArgDecl updateArg = new ArgDecl(ArgDecl.HasValue, "update", "file") ;
+    static final ArgDecl dumpArg = new ArgDecl(ArgDecl.NoValue, "dump") ;       // Write the result to stdout.
+    
+    List<String> requestFiles = null ;
+    boolean dump = false ;
+    
+    public static void main (String... argv)
+    { new update(argv).mainRun() ; }
+    
+    protected update(String[] argv) {
+        super(argv) ;
+        super.add(updateArg, "--update=FILE", "Update commands to execute") ;
+        super.add(dumpArg, "--dump", "Dump the resulting graph store") ;
+    }
+
+    @Override
+    protected void processModulesAndArgs() {
+        requestFiles = getValues(updateArg) ; // ????
+        dump = contains(dumpArg) ;
+        super.processModulesAndArgs() ;
+    }
+
+    @Override
+    protected String getCommandName() { return Lib.className(this) ; }
+    
+    @Override
+    protected String getSummary() { return getCommandName()+" --desc=assembler [--dump] --update=<request file>" ; }
+
+    // Subclass for specialised commands making common updates more convenient
+    @Override
+    protected void execUpdate(DatasetGraph graphStore) {
+        if ( requestFiles.size() == 0 && getPositional().size() == 0 )
+            throw new CmdException("Nothing to do") ;
+
+        Transactional transactional = (graphStore instanceof Transactional) ? (Transactional)graphStore : new TransactionalNull() ;
+
+        for ( String filename : requestFiles ) {
+            try {
+                transactional.begin(ReadWrite.WRITE) ;
+                execOneFile(filename, graphStore) ;
+                transactional.commit() ;
+            }
+            catch (Throwable ex) { 
+                try { transactional.abort() ; } catch (Exception ex2) {}
+                throw ex ;
+            }
+            finally { transactional.end() ; }
+        }
+
+        for ( String requestString : super.getPositional() ) {
+            requestString = indirect(requestString) ;
+
+            try {
+                transactional.begin(ReadWrite.WRITE) ;
+                execOne(requestString, graphStore) ;
+                transactional.commit() ;
+            }
+            catch (Throwable ex) { 
+                try { transactional.abort() ; } catch (Exception ex2) {}
+                throw ex ;
+            }
+            finally { transactional.end() ; }
+        }
+        
+        if ( ! (graphStore instanceof Transactional) )
+            SystemARQ.sync(graphStore) ;
+
+        if ( dump )
+            RDFDataMgr.write(System.out, graphStore, Lang.NQUADS) ;
+    }
+
+    private void execOneFile(String filename, DatasetGraph store) {
+        UpdateRequest req = UpdateFactory.read(filename, updateSyntax) ;
+        UpdateExecutionFactory.create(req, store).execute() ;
+    }
+
+    private void execOne(String requestString, DatasetGraph store) {
+        UpdateRequest req = UpdateFactory.create(requestString, updateSyntax) ;
+        UpdateExecutionFactory.create(req, store).execute() ;
+    }
+
+    @Override
+    protected DatasetGraph dealWithNoDataset() {
+        return DatasetGraphFactory.create() ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/utf8.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/utf8.java b/jena-cmds/src/main/java/arq/utf8.java
new file mode 100644
index 0000000..bc47569
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/utf8.java
@@ -0,0 +1,29 @@
+/*
+ * 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 arq;
+
+
+public class utf8
+{
+    /** Simple program to help hunt down bad UTF-8 encoded characters */
+    public static void main(String[] args)
+    {
+        riotcmd.utf8.main(args) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/version.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/version.java b/jena-cmds/src/main/java/arq/version.java
new file mode 100644
index 0000000..e55434c
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/version.java
@@ -0,0 +1,29 @@
+/*
+ * 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 arq;
+
+import jena.cmd.ModVersion;
+
+public class version
+{
+    public static void main (String... argv)
+    {
+        new ModVersion(false).printVersionAndExit() ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/wwwdec.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/wwwdec.java b/jena-cmds/src/main/java/arq/wwwdec.java
new file mode 100644
index 0000000..60a07c5
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/wwwdec.java
@@ -0,0 +1,36 @@
+/*
+ * 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 arq;
+
+import org.apache.jena.atlas.lib.StrUtils ;
+
+public class wwwdec
+{
+    public static void main(String...args)
+    {
+        for ( String x : args)
+        {
+            String y = StrUtils.decodeHex(x, '%') ;
+            System.out.println(y) ;
+            
+//            String s2 = URLDecoder.decode(x, "utf-8") ;
+//            System.out.println(s2) ;   
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/arq/wwwenc.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/arq/wwwenc.java b/jena-cmds/src/main/java/arq/wwwenc.java
new file mode 100644
index 0000000..4ee19af
--- /dev/null
+++ b/jena-cmds/src/main/java/arq/wwwenc.java
@@ -0,0 +1,61 @@
+/*
+ * 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 arq;
+
+import org.apache.jena.atlas.lib.StrUtils ;
+
+public class wwwenc
+{
+    /* http://en.wikipedia.org/wiki/Percent-encoding
+     * Reserved characters after percent-encoding 
+     *   !    *   "   '   (   )   ;   :   @   &   =   +   $   ,   /   ?   %   #   [   ]
+     *   %21  %2A %22 %27 %28 %29 %3B %3A %40 %26 %3D %2B %24 %2C %2F %3F %25 %23 %5B %5D
+     * These loose any reserved meaning if encoded.
+     *   
+     * Other common, but unreserved, characters after percent-encoding 
+     *   <   >   ~   .   {   }   |   \   -   `   _   ^
+     *   %3C %3E %7E %2E %7B %7D %7C %5C %2D %60 %5F %5E
+     * 
+     * Unreserved characters treated equivalent to their unencoded form.  
+     *   
+     *   
+     */
+    public static void main(String...args)
+    {
+        // Reserved characters + space
+        char reserved[] = 
+            {' ',
+             '\n','\t',
+             '!', '*', '"', '\'', '(', ')', ';', ':', '@', '&', 
+             '=', '+', '$', ',', '/', '?', '%', '#', '[', ']'} ;
+        
+        char[] other = {'<', '>', '~', '.', '{', '}', '|', '\\', '-', '`', '_', '^'} ;        
+        
+        for ( String x : args)
+        {
+            // Not URLEncoder which does www-form-encoding.
+            String y = StrUtils.encodeHex(x, '%', reserved) ;
+            System.out.println(y) ;
+            
+//            String s2 = URLEncoder.encode(s, "utf-8") ;
+//            System.out.println(s2) ;
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/jena/RuleMap.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/jena/RuleMap.java b/jena-cmds/src/main/java/jena/RuleMap.java
new file mode 100644
index 0000000..111f44b
--- /dev/null
+++ b/jena-cmds/src/main/java/jena/RuleMap.java
@@ -0,0 +1,196 @@
+/*
+ * 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 jena;
+
+
+import static org.apache.jena.atlas.logging.LogCtl.setCmdLogging;
+
+import java.util.*;
+import java.io.*;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.Options;
+import org.apache.jena.graph.* ;
+import org.apache.jena.rdf.model.* ;
+import org.apache.jena.reasoner.Reasoner ;
+import org.apache.jena.reasoner.rulesys.* ;
+import org.apache.jena.reasoner.rulesys.builtins.BaseBuiltin ;
+import org.apache.jena.util.FileManager ;
+import org.apache.jena.util.FileUtils ;
+
+/**
+ * General command line utility to process one RDF file into another
+ * by application of a set of forward chaining rules.
+ * <pre>
+ * Usage:  RuleMap [-il inlang] [-ol outlang] [-d] rulefile infile
+ * </pre>
+ * The resulting RDF data is written to stdout in format <code>outlang</code>
+ * (default N3). If <code>-d</code> is given then only the deductions
+ * generated by the rules are output. Otherwise all data including any input
+ * data (other than any removed triples) is output.
+ * <p>
+ * Rules are permitted an additional action "deduce" which forces triples
+ * to be added to the deductions graph even if they are already known (for use
+ * in deductions only mode). 
+ * </p>
+ */
+public class RuleMap {
+    static { setCmdLogging() ; }
+    
+    /**
+     * Load a set of rule definitions including processing of
+     * comment lines and any initial prefix definition lines.
+     * Also notes the prefix definitions for adding to a later inf model.
+     */
+    public static List<Rule> loadRules(String filename, Map<String, String> prefixes) {
+        String fname = filename;
+        if (fname.startsWith("file:///")) {
+            fname = File.separator + fname.substring(8);
+        } else if (fname.startsWith("file:/")) {
+            fname = File.separator + fname.substring(6);
+        } else if (fname.startsWith("file:")) {
+            fname = fname.substring(5);
+        }
+
+        BufferedReader src = FileUtils.openResourceFile(fname);
+        return loadRules(src, prefixes);
+    }
+    
+    /**
+     * Load a set of rule definitions including processing of
+     * comment lines and any initial prefix definition lines.
+     * Also notes the prefix definitions for adding to a later inf model.
+     */
+    public static List<Rule> loadRules(BufferedReader src, Map<String, String> prefixes) {
+        Rule.Parser parser = Rule.rulesParserFromReader(src);
+        List<Rule> rules = Rule.parseRules(parser);
+        prefixes.putAll(parser.getPrefixMap());
+        return rules;
+    }
+    
+    /**
+     * Internal implementation of the "deduce" primitve.
+     * This takes the form <code> ... -> deduce(s, p, o)</code>
+     */
+    static class Deduce extends BaseBuiltin {
+
+        /**
+         * Return a name for this builtin, normally this will be the name of the 
+         * functor that will be used to invoke it.
+         */
+        @Override
+        public String getName() {
+            return "deduce";
+        }    
+   
+        /**
+         * Return the expected number of arguments for this functor or 0 if the number is flexible.
+         */
+        @Override
+        public int getArgLength() {
+            return 3;
+        }
+
+        /**
+         * This method is invoked when the builtin is called in a rule head.
+         * Such a use is only valid in a forward rule.
+         * @param args the array of argument values for the builtin, this is an array 
+         * of Nodes.
+         * @param length the length of the argument list, may be less than the length of the args array
+         * for some rule engines
+         * @param context an execution context giving access to other relevant data
+         */
+        @Override
+        public void headAction(Node[] args, int length, RuleContext context) {
+            if (context.getGraph() instanceof FBRuleInfGraph) {
+                Triple t = new Triple(args[0], args[1], args[2]);
+                ((FBRuleInfGraph)context.getGraph()).addDeduction(t);
+            } else {
+                throw new BuiltinException(this, context, "Only usable in FBrule graphs");
+            }
+        }
+    }
+    
+    /**
+     * General command line utility to process one RDF file into another
+     * by application of a set of forward chaining rules. 
+     * <pre>
+     * Usage:  RuleMap [-il inlang] [-ol outlang] -d infile rulefile
+     * </pre>
+     */
+    public static void main(String[] args) {
+    	try {
+
+            // Parse the command line
+            String usage = "Usage:  RuleMap [-il inlang] [-ol outlang] [-d] rulefile infile (- for stdin)"; 
+            final CommandLineParser parser = new DefaultParser();
+			Options options = new Options().addOption("il", "inputLang", true, "input language")
+					.addOption("ol", "outputLang", true, "output language").addOption("d", "Deductions only?");
+			CommandLine cl = parser.parse(options, args);
+			final List<String> filenameArgs = cl.getArgList();
+            if (filenameArgs.size() != 2) {
+                System.err.println(usage);
+                System.exit(1);
+            }
+            
+            String inLang = cl.getOptionValue("inputLang");
+            String fname = filenameArgs.get(1);
+            Model inModel = null;
+            if (fname.equals("-")) {
+                inModel = ModelFactory.createDefaultModel();
+                inModel.read(System.in, null, inLang);
+            } else {
+                inModel = FileManager.get().loadModel(fname, inLang);
+            }
+            
+            String outLang = cl.hasOption("outputLang") ? cl.getOptionValue("outputLang") : "N3";            
+            
+            boolean deductionsOnly = cl.hasOption('d');
+            
+            // Fetch the rule set and create the reasoner
+            BuiltinRegistry.theRegistry.register(new Deduce());
+            Map<String, String> prefixes = new HashMap<>();
+            List<Rule> rules = loadRules(filenameArgs.get(0), prefixes);
+            Reasoner reasoner = new GenericRuleReasoner(rules);
+            
+            // Process
+            InfModel infModel = ModelFactory.createInfModel(reasoner, inModel);
+            infModel.prepare();
+            infModel.setNsPrefixes(prefixes);
+            
+            // Output
+            try ( PrintWriter writer = new PrintWriter(System.out) ) {
+                if (deductionsOnly) {
+                    Model deductions = infModel.getDeductionsModel();
+                    deductions.setNsPrefixes(prefixes);
+                    deductions.setNsPrefixes(inModel);
+                    deductions.write(writer, outLang);
+                } else {
+                    infModel.write(writer, outLang);
+                }
+            }
+        } catch (Throwable t) {
+            System.err.println("An error occured: \n" + t);
+            t.printStackTrace();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/jena/cmd/Arg.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/jena/cmd/Arg.java b/jena-cmds/src/main/java/jena/cmd/Arg.java
new file mode 100644
index 0000000..d7fb8c9
--- /dev/null
+++ b/jena-cmds/src/main/java/jena/cmd/Arg.java
@@ -0,0 +1,69 @@
+/*
+ * 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 jena.cmd;
+
+import java.util.ArrayList ;
+import java.util.List ;
+
+public class Arg
+{
+    String name ;
+    String value ;                                      // Last seen
+    List<String> values = new ArrayList<>() ;     // All seen
+    
+    Arg() { name = null ; value = null ; }
+    
+    public Arg(String _name) { this() ; setName(_name) ; }
+    
+    Arg(String _name, String _value) { this() ; setName(_name) ; setValue(_value) ; }
+    
+    void setName(String n) { name = n ; }
+    
+    public void setValue(String v) { value = v ; }
+    public void addValue(String v) { values.add(v) ; }
+    
+    public String getName() { return name ; }
+    public String getValue() { return value; }
+    public List<String> getValues() { return values; }
+    
+    public boolean hasValue() { return value != null ; }
+    
+    public boolean matches(ArgDecl decl)
+    {
+        return decl.getNames().contains(name) ;
+    }
+    
+    @Override
+    public String toString()
+    {
+        String base = (( name.length() == 1 )?"-":"--") + name ;
+        if ( getValues().size() == 0 )
+            return base ;
+
+        String str = "" ;
+        String sep = "" ;
+
+        for ( String v : getValues() )
+        {
+            str = str + sep + base + "=" + v;
+            sep = " ";
+        }
+        return str ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/jena/cmd/ArgDecl.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/jena/cmd/ArgDecl.java b/jena-cmds/src/main/java/jena/cmd/ArgDecl.java
new file mode 100644
index 0000000..c3781e2
--- /dev/null
+++ b/jena-cmds/src/main/java/jena/cmd/ArgDecl.java
@@ -0,0 +1,93 @@
+/*
+ * 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 jena.cmd;
+
+import static java.util.Arrays.asList;
+
+import java.util.ArrayList ;
+import java.util.Iterator ;
+import java.util.List ;
+
+/** A command line argument specification. */
+public class ArgDecl
+{
+    boolean takesValue ;
+    
+    List<String> names = new ArrayList<>() ;
+	
+    public static final boolean HasValue = true ;
+    public static final boolean NoValue = false ;
+
+   /** Create a declaration for a command argument.
+    *
+    * @param hasValue  Does it take a value or not?
+    */
+   public ArgDecl(boolean hasValue)
+   {
+       takesValue = hasValue ;
+   }
+
+   /** Create a declaration for a command argument.
+    *
+    * @param hasValue  Does it take a value or not?
+    * @param names     Names of arguments
+    */
+   public ArgDecl(boolean hasValue, String... names)
+   {
+       this(hasValue) ;
+       asList(names).forEach(this::addName);
+   }
+   
+    public void addName(String name)
+    {
+        name = canonicalForm(name) ;
+        if ( ! names.contains(name))
+            names.add(name) ;
+    }
+    
+    public String getKeyName() { return names.get(0) ; }
+    
+    public List<String> getNames() { return names ; }
+    public Iterator<String> names() { return names.iterator() ; }
+    
+    public boolean takesValue() { return takesValue ; }
+    
+    public boolean matches(Arg a)
+    {
+    	 	String name = a.getName();
+    		return names.stream().anyMatch(name::equals);
+    }
+    
+    public boolean matches(String arg)
+    {
+        arg = canonicalForm(arg) ;
+        return names.contains(arg) ;
+    }
+    
+    public static String canonicalForm(String str)
+    {
+        if ( str.startsWith("--") )
+            return str.substring(2) ;
+        
+        if ( str.startsWith("-") )
+            return str.substring(1) ;
+        
+        return str ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/jena/cmd/ArgModule.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/jena/cmd/ArgModule.java b/jena-cmds/src/main/java/jena/cmd/ArgModule.java
new file mode 100644
index 0000000..82194de
--- /dev/null
+++ b/jena-cmds/src/main/java/jena/cmd/ArgModule.java
@@ -0,0 +1,26 @@
+/**
+ * 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 jena.cmd;
+
+public interface ArgModule
+{
+    // Argument processing phase
+    public void processArgs(CmdArgModule cmdLine) ;
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/jena/cmd/ArgModuleGeneral.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/jena/cmd/ArgModuleGeneral.java b/jena-cmds/src/main/java/jena/cmd/ArgModuleGeneral.java
new file mode 100644
index 0000000..d6f09fa
--- /dev/null
+++ b/jena-cmds/src/main/java/jena/cmd/ArgModuleGeneral.java
@@ -0,0 +1,25 @@
+/*
+ * 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 jena.cmd;
+
+public interface ArgModuleGeneral extends ArgModule
+{
+    // Registration phase for usage messages
+    public abstract void registerWith(CmdGeneral cmdLine) ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/jena/cmd/ArgProc.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/jena/cmd/ArgProc.java b/jena-cmds/src/main/java/jena/cmd/ArgProc.java
new file mode 100644
index 0000000..51989e7
--- /dev/null
+++ b/jena-cmds/src/main/java/jena/cmd/ArgProc.java
@@ -0,0 +1,27 @@
+/*
+ * 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 jena.cmd;
+
+
+public interface ArgProc {
+
+    void startArgs() ;
+    void finishArgs() ;
+    void arg(String arg, int i) ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/jena/cmd/CmdArgModule.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/jena/cmd/CmdArgModule.java b/jena-cmds/src/main/java/jena/cmd/CmdArgModule.java
new file mode 100644
index 0000000..c3279dc
--- /dev/null
+++ b/jena-cmds/src/main/java/jena/cmd/CmdArgModule.java
@@ -0,0 +1,70 @@
+/*
+ * 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 jena.cmd;
+
+import java.util.ArrayList ;
+import java.util.List ;
+
+import jena.cmd.ArgModuleGeneral;
+
+public abstract class CmdArgModule extends CmdMain
+{
+    List<ArgModuleGeneral> modules = new ArrayList<>() ;
+    
+    protected CmdArgModule(String[] argv)
+    {
+        super(argv) ;
+    }
+    
+    protected void addModule(ArgModuleGeneral argModule)
+    {
+        modules.add(argModule) ;
+    }
+
+    @Override
+    final
+    public void process()
+    {
+        super.process() ;
+        forEach(new Action(){
+            @Override
+            public void action(CmdArgModule controller, ArgModuleGeneral module)
+            { 
+                module.processArgs(controller) ;
+            }
+        } ) ;
+        processModulesAndArgs() ;
+    }
+    
+    abstract
+    protected void processModulesAndArgs() ;
+    
+    private void forEach(Action action)
+    {
+        for ( ArgModuleGeneral am : modules )
+        {
+            action.action( this, am );
+        }
+    }
+                    
+    interface Action
+    {
+        public void action(CmdArgModule controller, ArgModuleGeneral module) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/498b2264/jena-cmds/src/main/java/jena/cmd/CmdException.java
----------------------------------------------------------------------
diff --git a/jena-cmds/src/main/java/jena/cmd/CmdException.java b/jena-cmds/src/main/java/jena/cmd/CmdException.java
new file mode 100644
index 0000000..df952af
--- /dev/null
+++ b/jena-cmds/src/main/java/jena/cmd/CmdException.java
@@ -0,0 +1,30 @@
+/*
+ * 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 jena.cmd;
+
+/**
+ * Indicate that something went wrong - while executing the command or processing the request.
+ */
+
+public class CmdException extends RuntimeException
+{
+    public CmdException()                             { super() ; }
+    public CmdException(String msg)                   { super(msg) ; }
+    public CmdException(String msg, Throwable cause)  { super(msg, cause) ; }
+}