JENA-1968: Relative URIs output, with no BASE
diff --git a/jena-arq/src-examples/arq/examples/riot/ExRIOTw3_AddNewWriter.java b/jena-arq/src-examples/arq/examples/riot/ExRIOT7_AddNewWriter.java
similarity index 98%
rename from jena-arq/src-examples/arq/examples/riot/ExRIOTw3_AddNewWriter.java
rename to jena-arq/src-examples/arq/examples/riot/ExRIOT7_AddNewWriter.java
index ca1b9c4..79cf7b7 100644
--- a/jena-arq/src-examples/arq/examples/riot/ExRIOTw3_AddNewWriter.java
+++ b/jena-arq/src-examples/arq/examples/riot/ExRIOT7_AddNewWriter.java
@@ -34,11 +34,11 @@
 import org.apache.jena.sparql.util.Context ;
 
 /** Example of registering a new writer with RIOT */
-public class ExRIOTw3_AddNewWriter
+public class ExRIOT7_AddNewWriter
 {
     static { LogCtl.setLogging(); }
     
-    // See also ExRIOT6_AddNewLang
+    // See also ExRIOT6_AddReader
     public static void main(String[] args)
     {
         System.out.println("## Example of a registering a new language with RIOT for writing") ; 
diff --git a/jena-arq/src-examples/arq/examples/riot/ExRIOT8_RelativeURIs.java b/jena-arq/src-examples/arq/examples/riot/ExRIOT8_RelativeURIs.java
new file mode 100644
index 0000000..bfe12cc
--- /dev/null
+++ b/jena-arq/src-examples/arq/examples/riot/ExRIOT8_RelativeURIs.java
@@ -0,0 +1,71 @@
+/**
+ * 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.examples.riot;
+
+import org.apache.jena.atlas.logging.LogCtl ;
+import org.apache.jena.rdf.model.Model ;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFParser;
+import org.apache.jena.riot.RDFWriter;
+import org.apache.jena.riot.RIOT;
+
+/** Example of registering a new writer with RIOT */
+public class ExRIOT8_RelativeURIs
+{
+    static { LogCtl.setLogging(); }
+    
+    public static void main(String[] args)
+    {
+        System.out.println("## Example of I/O with relative URIs.") ; 
+        System.out.println() ;
+        
+        Model model = ModelFactory.createDefaultModel(); 
+        
+        // Read data that has relative URIs, and does not include a BASE directive.
+        // (When reading a URL (including files), the base defaults to the URL.)
+        RDFParser.create()
+            .base("http://theBase/")
+            .source("SomeData.ttl")     // Implies "lang(Lang.TTL)"
+            .parse(model);
+
+        // == With BASE URI
+        // Write data, with relative URIs and with BASE.
+        RDFWriter.create()
+            // Cause a BASE to output and URIs to be made relative.
+            .base("http://theBase/")
+            .lang(Lang.TTL)
+            .source(model)
+            .output(System.out);
+        
+        // == Without BASE URI
+        // Write data, with relative URIs but no base.
+        // The data is not portable - the exact triples it contains when read back in
+        // will be influenced by the base URI of the parsing step.
+        RDFWriter.create()
+            // Don't print "BASE".  
+            .set(RIOT.symTurtleOmitBase, true)
+            // Cause URIs to be made relative using the base.
+            .base("http://theBase/")
+            .lang(Lang.TTL)
+            .source(model)
+            .output(System.out);
+    }
+}
+
diff --git a/jena-arq/src-examples/arq/examples/riot/ExRIOT7_ParserPiped.java b/jena-arq/src-examples/arq/examples/riot/ExRIOT9_ParserPiped.java
similarity index 98%
rename from jena-arq/src-examples/arq/examples/riot/ExRIOT7_ParserPiped.java
rename to jena-arq/src-examples/arq/examples/riot/ExRIOT9_ParserPiped.java
index 0581fed..04aff8c 100644
--- a/jena-arq/src-examples/arq/examples/riot/ExRIOT7_ParserPiped.java
+++ b/jena-arq/src-examples/arq/examples/riot/ExRIOT9_ParserPiped.java
@@ -32,7 +32,7 @@
  * {@link PipedRDFStream} and a {@link PipedRDFIterator}
  * 
  */
-public class ExRIOT7_ParserPiped {
+public class ExRIOT9_ParserPiped {
 
     public static void main(String... argv) {
         final String filename = "data.ttl";
diff --git a/jena-arq/src-examples/arq/examples/riot/ExRIOT_RDFXML_ReaderProperties.java b/jena-arq/src-examples/arq/examples/riot/ExRIOT_RDFXML_ReaderProperties.java
index f87a5d0..9a802cf 100644
--- a/jena-arq/src-examples/arq/examples/riot/ExRIOT_RDFXML_ReaderProperties.java
+++ b/jena-arq/src-examples/arq/examples/riot/ExRIOT_RDFXML_ReaderProperties.java
@@ -32,7 +32,7 @@
 import org.apache.jena.riot.SysRIOT;
 import org.apache.jena.sparql.util.Context;
 
-/** Set proeprties of the RDF/XML parser (ARP) */
+/** Set properties of the RDF/XML parser (ARP) */
 public class ExRIOT_RDFXML_ReaderProperties {
     static { LogCtl.setLogging(); }
     
@@ -42,7 +42,7 @@
             ("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
             ,"<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
             ,"         xmlns:ex=\"http://examples.org/\">"
-            // This rdf:ID startswith a digit which normal causes a warning.
+            // This rdf:ID starts with a digit which normal causes a warning.
             ,"  <ex:Type rdf:ID='012345'></ex:Type>"
             ,"</rdf:RDF>"
             );
diff --git a/jena-arq/src-examples/arq/examples/riot/ExRIOTw1_writeModel.java b/jena-arq/src-examples/arq/examples/riot/ExRIOT_writeModel.java
similarity index 97%
rename from jena-arq/src-examples/arq/examples/riot/ExRIOTw1_writeModel.java
rename to jena-arq/src-examples/arq/examples/riot/ExRIOT_writeModel.java
index 405d350..587c155 100644
--- a/jena-arq/src-examples/arq/examples/riot/ExRIOTw1_writeModel.java
+++ b/jena-arq/src-examples/arq/examples/riot/ExRIOT_writeModel.java
@@ -22,7 +22,7 @@
 import org.apache.jena.riot.* ;
 
 /** Example writing a model with RIOT */
-public class ExRIOTw1_writeModel
+public class ExRIOT_writeModel
 {
     public static void main(String[] args)
     {
diff --git a/jena-arq/src-examples/arq/examples/riot/ExRIOTw2_writeRDF.java b/jena-arq/src-examples/arq/examples/riot/ExRIOT_writeRDF.java
similarity index 98%
rename from jena-arq/src-examples/arq/examples/riot/ExRIOTw2_writeRDF.java
rename to jena-arq/src-examples/arq/examples/riot/ExRIOT_writeRDF.java
index 69288bd..8a3ffa1 100644
--- a/jena-arq/src-examples/arq/examples/riot/ExRIOTw2_writeRDF.java
+++ b/jena-arq/src-examples/arq/examples/riot/ExRIOT_writeRDF.java
@@ -24,7 +24,7 @@
 import org.apache.jena.riot.RDFDataMgr ;
 
 /** Other writer examples */
-public class ExRIOTw2_writeRDF
+public class ExRIOT_writeRDF
 {
     public static void main(String[] args)
     {
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterBuilder.java b/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterBuilder.java
index 2ad010f..2ad2c2a 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterBuilder.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterBuilder.java
@@ -90,8 +90,7 @@
     public RDFWriterBuilder source(Dataset dataset) {
         return source(dataset.asDatasetGraph());
     }
-
-
+    
 //    // Not implemented
 //    public RDFWriterBuilder labels(NodeToLabel nodeToLabel) { return this; }
 //    
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RIOT.java b/jena-arq/src/main/java/org/apache/jena/riot/RIOT.java
index 39300ec..7fd7e6f 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/RIOT.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/RIOT.java
@@ -21,9 +21,11 @@
 import org.apache.jena.query.ARQ ;
 import org.apache.jena.riot.lang.JsonLDReader;
 import org.apache.jena.riot.resultset.ResultSetLang;
+import org.apache.jena.riot.system.StreamRDF;
 import org.apache.jena.sparql.SystemARQ ;
 import org.apache.jena.sparql.mgt.SystemInfo ;
 import org.apache.jena.sparql.util.Context ;
+import org.apache.jena.sparql.util.MappingRegistry;
 import org.apache.jena.sparql.util.Symbol ;
 import org.apache.jena.sys.JenaSystem ;
 
@@ -76,7 +78,10 @@
             RDFParserRegistry.init() ;
             RDFWriterRegistry.init() ;
             ResultSetLang.init();
-
+            
+            MappingRegistry.addPrefixMapping("ttl", TURTLE_SYMBOL_BASE) ;
+            MappingRegistry.addPrefixMapping("trig", TURTLE_SYMBOL_BASE) ;
+            
             IO_Jena.wireIntoJena() ;
 
             // Don't register JMX info with ARQ as it may not be initialized
@@ -122,6 +127,15 @@
      */
     public static final Symbol symTurtleDirectiveStyle = SystemARQ.allocSymbol(TURTLE_SYMBOL_BASE, "directiveStyle");
 
+    /**
+     * Printing style. Whether to output "BASE"/"@base" (according to
+     * {@link #symTurtleDirectiveStyle} or not. BASE is normally written if there is
+     * a base URI passed to the writer or, for a streaming writer, if
+     * {@link StreamRDF#base} is called. If this context setting is set true, then do
+     * not output BASE even when given.
+     */
+    public static final Symbol symTurtleOmitBase = SystemARQ.allocSymbol(TURTLE_SYMBOL_BASE, "omitBase");
+    
     /** @deprecated Use {@link #symTurtleDirectiveStyle}. */
     @Deprecated
     public static final Symbol symTurtlePrefixStyle = SystemARQ.allocSymbol(TURTLE_SYMBOL_BASE, "prefixStyle");
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/out/NodeFormatterTTL.java b/jena-arq/src/main/java/org/apache/jena/riot/out/NodeFormatterTTL.java
index 19f8db8..b0947a3 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/out/NodeFormatterTTL.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/out/NodeFormatterTTL.java
@@ -52,7 +52,7 @@
         this.prefixMap = prefixMap ;
         this.baseIRI = baseIRI ;
         this.iriResolver = 
-            baseIRI != null ? IRIResolver.iriFactory().construct(baseIRI) : null ;
+            baseIRI != null ? IRIResolver.iriFactory().create(baseIRI) : null ;
     }
 
     @Override
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/IRIResolver.java b/jena-arq/src/main/java/org/apache/jena/riot/system/IRIResolver.java
index c46a3e8..dceebd1 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/system/IRIResolver.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/IRIResolver.java
@@ -66,13 +66,14 @@
         setErrorWarning(iriFactoryInst, ViolationCodes.NON_INITIAL_DOT_SEGMENT, false, false);
 
         // Turn off?? (ignored in CheckerIRI.iriViolations anyway).
-        // setErrorWarning(iriFactory, ViolationCodes.LOWERCASE_PREFERRED, false, false);
-        // setErrorWarning(iriFactory, ViolationCodes.PERCENT_ENCODING_SHOULD_BE_UPPERCASE, false, false);
         // setErrorWarning(iriFactory, ViolationCodes.SCHEME_PATTERN_MATCH_FAILED, false, false);
 
+        // Choices
+        setErrorWarning(iriFactoryInst, ViolationCodes.LOWERCASE_PREFERRED, false, true);
+        //setErrorWarning(iriFactoryInst, ViolationCodes.PERCENT_ENCODING_SHOULD_BE_UPPERCASE, false, true);
+        
         // NFC tests are not well understood by general developers and these cause confusion.
         // See JENA-864
-
         // NFC is in RDF 1.1 so do test for that.
         // https://www.w3.org/TR/rdf11-concepts/#section-IRIs
         // Leave switched on as a warning.
@@ -87,6 +88,7 @@
         // The set of legal characters depends on the Java version.
         // If not set, this causes test failures in Turtle and Trig eval tests.
         setErrorWarning(iriFactoryInst, ViolationCodes.UNASSIGNED_UNICODE_CHARACTER, false, false);
+        
 
         if ( ShowResolverSetup ) {
             System.out.println("---- After initialization ----");
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/writer/TriGWriterBase.java b/jena-arq/src/main/java/org/apache/jena/riot/writer/TriGWriterBase.java
index 6c591a5..087a484 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/writer/TriGWriterBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/TriGWriterBase.java
@@ -51,7 +51,7 @@
 
     private void output$(IndentedWriter iOut, DatasetGraph dsg, PrefixMap prefixMap, String baseURI, Context context) {
         if ( baseURI != null )
-            baseURI = IRIResolver.resolveString(baseURI) ;
+            baseURI = IRIResolver.resolveStringSilent(baseURI) ;
         output(iOut, dsg, prefixMap, baseURI, context) ;
         iOut.flush() ;
     }
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/writer/TurtleShell.java b/jena-arq/src/main/java/org/apache/jena/riot/writer/TurtleShell.java
index deff68b..8da1501 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/writer/TurtleShell.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/TurtleShell.java
@@ -83,7 +83,8 @@
     }
 
     protected void writeBase(String base) {
-        RiotLib.writeBase(out, base, prefixStyle==DirectiveStyle.SPARQL) ;
+        if ( context == null || ! context.isTrue(RIOT.symTurtleOmitBase) )
+            RiotLib.writeBase(out, base, prefixStyle==DirectiveStyle.SPARQL) ;
     }
 
     protected void writePrefixes(PrefixMap prefixMap) {
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/writer/TurtleWriterBase.java b/jena-arq/src/main/java/org/apache/jena/riot/writer/TurtleWriterBase.java
index 625d92d..30373c2 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/writer/TurtleWriterBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/TurtleWriterBase.java
@@ -50,7 +50,7 @@
 
     private void output$(IndentedWriter iOut, Graph graph, PrefixMap prefixMap, String baseURI, Context context) {
         if ( baseURI != null )
-            baseURI = IRIResolver.resolveString(baseURI) ;
+            baseURI = IRIResolver.resolveStringSilent(baseURI);
         output(iOut, graph, prefixMap, baseURI, context) ;
         iOut.flush() ;
     }
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterStreamRDFBase.java b/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterStreamRDFBase.java
index d1c4d85..7556c10 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterStreamRDFBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterStreamRDFBase.java
@@ -25,6 +25,7 @@
 import org.apache.jena.atlas.io.IndentedWriter ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.Triple ;
+import org.apache.jena.riot.RIOT;
 import org.apache.jena.riot.out.NodeFormatterTTL ;
 import org.apache.jena.riot.out.NodeToLabel ;
 import org.apache.jena.riot.system.PrefixMap ;
@@ -55,117 +56,101 @@
     protected NodeFormatterTTL fmt ;
     protected final IndentedWriter out ;
     protected final DirectiveStyle prefixStyle;
+    protected final boolean printBase;
 
-    public WriterStreamRDFBase(OutputStream output, Context context)
-    {
+    public WriterStreamRDFBase(OutputStream output, Context context) {
         this(new IndentedWriter(output), context) ;
     }
 
-    public WriterStreamRDFBase(Writer output, Context context)
-    { this(wrap(output), context) ; }
+    public WriterStreamRDFBase(Writer output, Context context) {
+        this(wrap(output), context);
+    }
 
-
-    public WriterStreamRDFBase(IndentedWriter output, Context context)
-    {
+    public WriterStreamRDFBase(IndentedWriter output, Context context) {
         out = output ;
-        baseURI = null ;
         pMap = PrefixMapFactory.create() ;
         nodeToLabel = NodeToLabel.createScopeByDocument() ;
+        
+        // Stream writing does not take an external base URI from the API "write"
+        // call. The base URI is output if StreamRDF.base() called, which means BASE
+        // was in the data stream.
+        baseURI = null ;
         prefixStyle = WriterLib.directiveStyle(context);
+        printBase = 
+            ( context == null ) ? true : context.isFalseOrUndef(RIOT.symTurtleOmitBase);
         setFormatter() ;
     }
 
-    private void setFormatter()
-    {
-        fmt = new NodeFormatterTTL(baseURI, pMap, nodeToLabel) ;
+    private void setFormatter() {
+        fmt = new NodeFormatterTTL(baseURI, pMap, nodeToLabel);
     }
 
-    private static IndentedWriter wrap(Writer output)
-    {
-        if ( ! ( output instanceof BufferedWriter ) )
-            output = new BufferedWriter(output, 32*1024) ;
-        return RiotLib.create(output) ;
+    private static IndentedWriter wrap(Writer output) {
+        if ( !(output instanceof BufferedWriter) )
+            output = new BufferedWriter(output, 32 * 1024);
+        return RiotLib.create(output);
     }
 
-    private void reset$()
-    {
-        activeTripleData = false ;
-        activeQuadData = false ;
-        lastWasDirective = false ;
+    private void reset$() {
+        activeTripleData = false;
+        activeQuadData = false;
+        lastWasDirective = false;
     }
 
     @Override
-    public final void start()
-    {
-        reset$() ;
-        startData() ;
+    public final void start() {
+        reset$();
+        startData();
     }
 
     @Override
-    public final void finish()
-    {
-        endData() ;
-        out.flush() ;
+    public final void finish() {
+        endData();
+        out.flush();
     }
 
     @Override
-    public final void triple(Triple triple)
-    {
-        print(triple) ;
-        activeTripleData = true ;
+    public final void triple(Triple triple) {
+        print(triple);
+        activeTripleData = true;
     }
 
     @Override
-    public final void quad(Quad quad)
-    {
-        print(quad) ;
-        activeQuadData = true ;
+    public final void quad(Quad quad) {
+        print(quad);
+        activeQuadData = true;
     }
 
     @Override
-    public final void base(String base)
-    {
-        baseURI = base ;
-        lastWasDirective = true ;
-        setFormatter() ;
-        RiotLib.writeBase(out, base, prefixStyle==DirectiveStyle.SPARQL) ;
+    public final void base(String base) {
+        baseURI = base;
+        lastWasDirective = true;
+        setFormatter();
+        if ( printBase )
+            RiotLib.writeBase(out, base, prefixStyle == DirectiveStyle.SPARQL);
     }
 
     @Override
-    public final void prefix(String prefix, String iri)
-    {
-        endData() ;
-        lastWasDirective = true ;
-        pMap.add(prefix, iri) ;
-        RiotLib.writePrefix(out, prefix, iri, prefixStyle==DirectiveStyle.SPARQL);
+    public final void prefix(String prefix, String iri) {
+        endData();
+        lastWasDirective = true;
+        pMap.add(prefix, iri);
+        RiotLib.writePrefix(out, prefix, iri, prefixStyle == DirectiveStyle.SPARQL);
     }
 
-    protected void outputNode(Node n)
-    {
-        fmt.format(out, n) ;
+    protected void outputNode(Node n) {
+        fmt.format(out, n);
     }
 
     // Subclass contract
 
-    protected abstract void startData() ;
+    protected abstract void startData();
 
-    protected abstract void endData() ;
+    protected abstract void endData();
 
-    protected abstract void print(Quad quad) ;
+    protected abstract void print(Quad quad);
 
-    protected abstract void print(Triple triple) ;
+    protected abstract void print(Triple triple);
 
-    protected abstract void reset() ;
-
-    protected void DEBUG(String fmt, Object...args)
-    {
-        int loc = out.getCol() ;            // Absolute
-        int off = out.getAbsoluteIndent() ;
-        out.ensureStartOfLine();
-        out.setAbsoluteIndent(0) ;
-        out.println(String.format(fmt, args)) ;
-        out.setAbsoluteIndent(off) ;
-        out.ensureStartOfLine();
-        out.pad(loc, true) ;
-    }
+    protected abstract void reset();
 }
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/TS_RiotGeneral.java b/jena-arq/src/test/java/org/apache/jena/riot/TS_RiotGeneral.java
index 914bb21..5c8c5b7 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/TS_RiotGeneral.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/TS_RiotGeneral.java
@@ -37,6 +37,9 @@
     , TestRDFWriter.class
     , TestParseURISchemeBases.class
     
+    , TestTurtleWriter.class
+    , TestTurtleWriterPretty.class
+    
     , TestJsonLDReader.class
     , TestJsonLDWriter.class
 })
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/TestRDFWriter.java b/jena-arq/src/test/java/org/apache/jena/riot/TestRDFWriter.java
index c8aeb31..0e6726b 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/TestRDFWriter.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/TestRDFWriter.java
@@ -31,18 +31,18 @@
 
 public class TestRDFWriter {
     private static Graph graph = SSE.parseGraph("(graph (:s :p :o))");
-    
+
     @Test public void rdfwriter_1() {
         RDFWriter.create().source(graph).build();
     }
-    
+
     @Test(expected=RiotException.class)
     public void rdfwriter_2() {
         RDFWriter.create().build();
     }
 
     @Test public void rdfwriter_3() {
-        String s = 
+        String s =
             RDFWriter.create()
                 .source(graph)
                 .lang(Lang.NT)
@@ -52,13 +52,13 @@
 
     @Test(expected=RiotException.class)
     public void rdfwriter_4() {
-        String s = 
+        String s =
             RDFWriter.create()
                 // No syntax
                 .source(graph)
                 .asString();
     }
-    
+
     @Test public void rdfwriter_5() {
         ByteArrayOutputStream bout = new ByteArrayOutputStream();
         RDFWriter.create()
@@ -68,7 +68,7 @@
         String s = StrUtils.fromUTF8bytes(bout.toByteArray());
         assertTrue(s.contains("example/s"));
     }
-    
+
     @SuppressWarnings("deprecation")
     @Test public void rdfwriter_6() {
         Writer w = new CharArrayWriter();
@@ -80,6 +80,4 @@
         String s = w.toString();
         assertTrue(s.contains("example/s"));
     }
-    
-   
 }
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/TestTurtleWriter.java b/jena-arq/src/test/java/org/apache/jena/riot/TestTurtleWriter.java
new file mode 100644
index 0000000..5d9ba40
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/riot/TestTurtleWriter.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.riot;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jena.graph.Graph;
+import org.apache.jena.sparql.graph.GraphFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for Turtle (and Trig) */
+@RunWith(Parameterized.class)
+public class TestTurtleWriter {
+    @Parameters(name = "{index}: {0}")
+    public static Iterable<Object[]> data() {
+        List<Object[]> x = new ArrayList<>() ;
+        x.add(new Object[]{"Turtle", RDFFormat.TURTLE});
+        x.add(new Object[]{"Turtle/Pretty", RDFFormat.TURTLE_PRETTY});
+        x.add(new Object[]{"Turtle/Blocks", RDFFormat.TURTLE_BLOCKS});
+        x.add(new Object[]{"Turtle/Flat", RDFFormat.TURTLE_FLAT});
+        x.add(new Object[]{"Trig", RDFFormat.TRIG});
+        x.add(new Object[]{"Trig/Pretty", RDFFormat.TRIG_PRETTY});
+        x.add(new Object[]{"Trig/Blocks", RDFFormat.TRIG_BLOCKS});
+        x.add(new Object[]{"Trig/Flat", RDFFormat.TRIG_FLAT});
+        return x ; 
+    }
+    
+    private static String DIR = "testing/RIOT/Writer/";
+
+    private static String BASE = "http://BASE/";
+
+    private final RDFFormat format;
+
+    private final String filename;
+    
+
+    public TestTurtleWriter(String name, RDFFormat format) {
+        this.format = format;
+        if ( format.getLang().equals(Lang.TRIG) )
+            this.filename = DIR+"rdfwriter-02.trig";
+        else
+            this.filename = DIR+"rdfwriter-01.ttl";
+    }
+    
+    // read file, with external base URI
+    private static Graph data(String fn, String baseURI) {
+        Graph g1 = GraphFactory.createDefaultGraph();
+        RDFParser.create()
+            .base(BASE)
+            .source(fn)
+            .parse(g1);
+        return g1;
+    }
+
+    // .base() for Turtle.
+    @Test public void writer_parse_base_1() {
+        // This has a relative URI
+        // Not an ideal URI but legal (host is upper case). Allowed.
+        Graph g = data(filename, BASE);
+        
+        String written = 
+            RDFWriter.create()
+                .base(BASE)
+                .source(g)
+                .set(RIOT.symTurtleDirectiveStyle, "sparql")
+                .format(format)
+                .base(BASE)
+                .asString();
+        
+        // Test BASE used.
+        assertTrue(written.contains("<>"));
+        assertTrue(written.contains("BASE"));
+    }
+
+    // Stream writer (BLOCKS and FLAT) don't print a base URI unless explicitly given one in the data.
+    // THis test is in TestTurtleWriterPretty
+    //@Test public void writer_parse_base_2()
+}
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/TestTurtleWriterPretty.java b/jena-arq/src/test/java/org/apache/jena/riot/TestTurtleWriterPretty.java
new file mode 100644
index 0000000..8afce9f
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/riot/TestTurtleWriterPretty.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.riot;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.jena.graph.Graph;
+import org.apache.jena.sparql.graph.GraphFactory;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Tests for Turtle and Trig pertty format */
+@RunWith(Parameterized.class)
+public class TestTurtleWriterPretty {
+    @Parameters(name = "{index}: {0}")
+    public static Iterable<Object[]> data() {
+        List<Object[]> x = new ArrayList<>() ;
+        x.add(new Object[]{"Turtle/Pretty", RDFFormat.TURTLE_PRETTY});
+        x.add(new Object[]{"Trig/Pretty", RDFFormat.TRIG_PRETTY});
+        return x ; 
+    }
+    
+    private static String DIR = "testing/RIOT/Writer/";
+
+    private static String BASE = "http://BASE/";
+
+    private final RDFFormat format;
+
+    private final String filename;
+
+    public TestTurtleWriterPretty(String name, RDFFormat format) {
+        this.format = format;
+        if ( format.getLang().equals(Lang.TRIG) )
+            this.filename = DIR+"rdfwriter-02.trig";
+        else
+            this.filename = DIR+"rdfwriter-01.ttl";
+    }
+    
+    // read file, with external base URI
+    private static Graph data(String fn, String baseURI) {
+        Graph g1 = GraphFactory.createDefaultGraph();
+        RDFParser.create()
+            .base(BASE)
+            .source(fn)
+            .parse(g1);
+        return g1;
+    }
+
+    // Stream writer (BLOCKS and FLAT) don't print a base URI unless explicitly given one in the data.
+   
+    @Test public void writer_parse_base_2() {
+        assumeTrue(format.getVariant().equals(RDFFormat.PRETTY));
+        
+        Graph g = data(filename, BASE);
+
+        String written = 
+            RDFWriter.create()
+                .base(BASE)
+                .source(g)
+                .set(RIOT.symTurtleDirectiveStyle, "sparql")
+                .set(RIOT.symTurtleOmitBase, true)
+                .format(format)
+                .base(BASE)
+                .asString();
+        {
+            // Same base URI => same graph
+            Graph g1 = GraphFactory.createDefaultGraph();
+            RDFParser.create()
+                .base(BASE)
+                .fromString(written)
+                .lang(Lang.TTL)
+                .parse(g1);
+            assertTrue(g.isIsomorphicWith(g1));
+        }
+        {
+            // Different base URI => different graph
+            Graph g2 = GraphFactory.createDefaultGraph();
+            String BASE2 = "http://BASE2/";
+            RDFParser.create()
+                .base(BASE2)
+                .fromString(written)
+                .lang(Lang.TTL)
+                .parse(g2);
+            assertFalse(g.isIsomorphicWith(g2));
+        }
+    }
+}
diff --git a/jena-arq/testing/RIOT/Writer/rdfwriter-01.ttl b/jena-arq/testing/RIOT/Writer/rdfwriter-01.ttl
new file mode 100644
index 0000000..ebaaf1a
--- /dev/null
+++ b/jena-arq/testing/RIOT/Writer/rdfwriter-01.ttl
@@ -0,0 +1,6 @@
+## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0
+
+# test for base URI handling externally.
+PREFIX : <http://example/>
+
+<> :p 123 .
diff --git a/jena-arq/testing/RIOT/Writer/rdfwriter-02.trig b/jena-arq/testing/RIOT/Writer/rdfwriter-02.trig
new file mode 100644
index 0000000..724b1ac
--- /dev/null
+++ b/jena-arq/testing/RIOT/Writer/rdfwriter-02.trig
@@ -0,0 +1,8 @@
+## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0
+
+# test for base URI handling externally.
+PREFIX : <http://example/>
+
+{
+   <> :p 456 .
+}
diff --git a/jena-cmds/src/main/java/arq/cmdline/ModLangParse.java b/jena-cmds/src/main/java/arq/cmdline/ModLangParse.java
index 0b9ee11..6f45653 100644
--- a/jena-cmds/src/main/java/arq/cmdline/ModLangParse.java
+++ b/jena-cmds/src/main/java/arq/cmdline/ModLangParse.java
@@ -114,7 +114,8 @@
 
         if ( cmdLine.contains(argBase) ) {
             baseIRI = cmdLine.getValue(argBase) ;
-            IRI iri = IRIResolver.resolveIRI(baseIRI) ;
+            IRI iri = IRIResolver.iriFactory().create(baseIRI);
+            
             if ( iri.hasViolation(false) )
                 throw new CmdException("Bad base IRI: " + baseIRI) ;
             if ( !iri.isAbsolute() )
diff --git a/jena-cmds/src/main/java/riotcmd/CmdLangParse.java b/jena-cmds/src/main/java/riotcmd/CmdLangParse.java
index 88a91f2..49d5af5 100644
--- a/jena-cmds/src/main/java/riotcmd/CmdLangParse.java
+++ b/jena-cmds/src/main/java/riotcmd/CmdLangParse.java
@@ -34,7 +34,6 @@
 import jena.cmd.CmdGeneral ;
 import org.apache.jena.Jena ;
 import org.apache.jena.atlas.io.IO ;
-import org.apache.jena.atlas.lib.InternalErrorException ;
 import org.apache.jena.atlas.lib.Pair ;
 import org.apache.jena.riot.* ;
 import org.apache.jena.riot.lang.LabelToNode ;
@@ -84,8 +83,10 @@
 
     protected List<ParseRecord> outcomes = new ArrayList<>();
 
-    OutputStream outputWrite = System.out ;
-    StreamRDF outputStream = null ;
+    protected OutputStream outputWrite = System.out ;
+    protected StreamRDF outputStream   = null ;
+    protected String parserBaseIRI     = null;
+    protected String writerBaseIRI     = null;
 
     @Override
     protected void processModulesAndArgs() {
@@ -122,6 +123,10 @@
         boolean oldStrictValue = SysRIOT.isStrictMode() ;
         if ( modLangParse.strictMode() )
             SysRIOT.setStrictMode(true) ;
+        
+        parserBaseIRI = modLangParse.getBaseIRI();
+        writerBaseIRI = parserBaseIRI;
+        
         try { exec$() ; }
         finally { SysRIOT.setStrictMode(oldStrictValue) ; }
     }
@@ -218,10 +223,10 @@
     }
 
     public ParseRecord parseFile(String filename) {
-        String baseURI = modLangParse.getBaseIRI() ;
+        String baseParserIRI = this.parserBaseIRI;
         RDFParserBuilder builder = RDFParser.create();
-        if ( baseURI != null )
-            builder.base(baseURI);
+        if ( baseParserIRI != null )
+            builder.base(baseParserIRI);
         if ( modLangParse.getLang() != null )
             // Always use the command line specified syntax.
             builder.forceLang(modLangParse.getLang());
@@ -236,9 +241,9 @@
 
         // Set the source.
         if ( filename.equals("-") ) {
-            if ( baseURI == null ) {
-                baseURI = "http://base/";
-                builder.base(baseURI);
+            if ( baseParserIRI == null ) {
+                baseParserIRI = "http://base/";
+                builder.base(baseParserIRI);
             }
             filename = "stdin";
             builder.source(System.in);
@@ -330,23 +335,18 @@
         final DatasetGraph dsg = DatasetGraphFactory.create() ;
         StreamRDF sink = StreamRDFLib.dataset(dsg) ;
         final RDFFormat fmt = modLangOutput.getOutputFormatted() ;
-        PostParseHandler handler = new PostParseHandler() {
-            @Override
-            public void postParse() {
-                // Try as dataset, then as graph.
-                WriterDatasetRIOTFactory w = RDFWriterRegistry.getWriterDatasetFactory(fmt) ;
-                if ( w != null ) {
-                    RDFDataMgr.write(outputWrite, dsg, fmt) ;
-                    return ;
-                }
-                WriterGraphRIOTFactory wg = RDFWriterRegistry.getWriterGraphFactory(fmt) ;
-                if ( wg != null ) {
-                    RDFDataMgr.write(System.out, dsg.getDefaultGraph(), fmt) ;
-                    return ;
-                }
-                throw new InternalErrorException("failed to find the writer: "+fmt) ;
-            }
-        } ;
+        PostParseHandler handler = ()->{
+            RDFWriterBuilder builder = RDFWriter.create();
+            builder.format(fmt);
+            if ( RDFLanguages.isQuads(fmt.getLang()) )
+                builder.source(dsg);
+            else
+                builder.source(dsg.getDefaultGraph());
+            String baseURI = writerBaseIRI;
+            if ( baseURI != null )
+                builder.base(baseURI);
+            builder.output(outputWrite);
+        };
         return Pair.create(sink, handler) ;
     }