http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/logging/FmtLog.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/logging/FmtLog.java 
b/jena-base/src/main/java/org/apache/jena/atlas/logging/FmtLog.java
new file mode 100644
index 0000000..590c49e
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/logging/FmtLog.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 org.apache.jena.atlas.logging;
+
+import java.util.IllegalFormatException ;
+
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
+
+/** Logging with String.format (which can be a expensive)
+ *  The formatting operations are not designed specifically for performance, 
+ *  but they do delay forming strings for output
+ *  until it is know that a log message is actually
+ *  required by level setting. 
+ *  
+ *  An odd effect is order of the arguments - vararg arguments must be last
+ *  so the order is Logger/Thorwable?/Format/args.
+ *  
+ *  Also support: Class object instead of logger to help ad hoc usage. 
+ */
+public class FmtLog {
+    /* Log at 'trace' level. */
+    public static void trace(Logger log, String fmt, Object...args) {
+        if ( log.isTraceEnabled() )
+            log.trace(format(fmt, args)) ;
+    }
+
+    /* Log at 'trace' level. */
+    public static void trace(Logger log, Throwable th, String fmt, 
Object...args) {
+        if ( log.isTraceEnabled() )
+            log.trace(format(fmt, args), th) ;
+    }
+
+    /* Log at 'trace' level. */
+    public static void trace(Class<?> cls, String fmt, Object...args) {
+        trace(log(cls), fmt, args) ;
+    }
+
+    /* Log at 'trace' level. */
+    public static void trace(Class<?> cls, Throwable th, String fmt, 
Object...args) {
+        trace(log(cls), th, fmt, args) ;
+    }
+
+    /* Log at 'debug' level */
+    public static void debug(Logger log, String fmt, Object...args) {
+        if ( log.isDebugEnabled() )
+            log.debug(format(fmt, args)) ;
+    }
+
+    /* Log at 'debug' level */
+    public static void debug(Logger log, Throwable th, String fmt, 
Object...args) {
+        if ( log.isDebugEnabled() )
+            log.debug(format(fmt, args), th) ;
+    }
+
+    /* Log at 'debug' level */
+    public static void debug(Class<?> cls, String fmt, Object...args) {
+        debug(log(cls), fmt, args) ;
+    }
+
+    /* Log at 'debug' level */
+    public static void debug(Class<?> cls, Throwable th, String fmt, 
Object...args) {
+        debug(log(cls), th, fmt, args) ;
+    }
+
+    /* Log at 'info' level */
+    public static void info(Logger log, String fmt, Object...args) {
+        if ( log.isInfoEnabled() )
+            log.info(format(fmt, args)) ;
+    }
+
+    /* Log at 'info' level */
+    public static void info(Logger log, Throwable th, String fmt, 
Object...args) {
+        if ( log.isInfoEnabled() )
+            log.info(format(fmt, args), th) ;
+    }
+
+    /* Log at 'info' level */
+    public static void info(Class<?> cls, String fmt, Object...args) {
+        info(log(cls), fmt, args) ;
+    }
+
+    /* Log at 'info' level */
+    public static void info(Class<?> cls, Throwable th, String fmt, 
Object...args) {
+        info(log(cls), th, fmt, args) ;
+    }
+
+    /* Log at 'warn' level */
+    public static void warn(Logger log, String fmt, Object...args) {
+        if ( log.isWarnEnabled() )
+            log.warn(format(fmt, args)) ;
+    }
+
+    /* Log at 'warn' level */
+    public static void warn(Logger log, Throwable th, String fmt, 
Object...args) {
+        if ( log.isWarnEnabled() )
+            log.warn(format(fmt, args), th) ;
+    }
+
+    /* Log at 'warn' level */
+    public static void warn(Class<?> cls, String fmt, Object...args) {
+        warn(log(cls), fmt, args) ;
+    }
+
+    /* Log at 'warn' level */
+    public static void warn(Class<?> cls, Throwable th, String fmt, 
Object...args) {
+        warn(log(cls), th, fmt, args) ;
+    }
+
+   /* Log at 'error' level */
+    public static void error(Logger log, String fmt, Object...args) {
+        if ( log.isErrorEnabled() )
+            log.error(format(fmt, args)) ;
+    }
+
+    /* Log at 'error' level */
+    public static void error(Logger log, Throwable th, String fmt, 
Object...args) {
+        if ( log.isErrorEnabled() )
+            log.error(format(fmt, args), th) ;
+    }
+
+    /* Log at 'error' level */
+    public static void error(Class<?> cls, String fmt, Object...args) {
+        error(log(cls), fmt, args) ;
+    }
+
+    /* Log at 'error' level */
+    public static void error(Class<?> cls, Throwable th, String fmt, 
Object...args) {
+        error(log(cls), th, fmt, args) ;
+    }
+
+
+    private static String format(String fmt, Object[] args) {
+        try {
+            return String.format(fmt, args) ;
+        } catch (IllegalFormatException ex) {
+            // return something, however grotty.
+            return fmt+" "+args ;
+        }
+    }
+
+    private static Logger log(Class<?> cls) {
+        return LoggerFactory.getLogger(cls) ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/logging/Log.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/logging/Log.java 
b/jena-base/src/main/java/org/apache/jena/atlas/logging/Log.java
new file mode 100644
index 0000000..47783fc
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/logging/Log.java
@@ -0,0 +1,116 @@
+/*
+ * 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.atlas.logging ;
+
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
+
+/* Simple wrappers and operations for convenient, non-time critical logging.
+ */
+public class Log {
+    private Log() {}
+
+    static public void info(String caller, String msg) {
+        log(caller).info(msg) ;
+    }
+
+    static public void info(Object caller, String msg) {
+        log(caller.getClass()).info(msg) ;
+    }
+
+    static public void info(Class<? > cls, String msg) {
+        log(cls).info(msg) ;
+    }
+
+    static public void info(Object caller, String msg, Throwable th) {
+        log(caller.getClass()).info(msg, th) ;
+    }
+
+    static public void info(Class<? > cls, String msg, Throwable th) {
+        log(cls).info(msg, th) ;
+    }
+
+    static public void debug(String caller, String msg) {
+        log(caller).debug(msg) ;
+    }
+
+    static public void debug(Object caller, String msg) {
+        log(caller.getClass()).debug(msg) ;
+    }
+
+    static public void debug(Class<? > cls, String msg) {
+        log(cls).debug(msg) ;
+    }
+
+    static public void debug(Object caller, String msg, Throwable th) {
+        log(caller.getClass()).debug(msg, th) ;
+    }
+
+    static public void debug(Class<? > cls, String msg, Throwable th) {
+        log(cls).debug(msg, th) ;
+    }
+
+    static public void warn(String caller, String msg) {
+        log(caller).warn(msg) ;
+    }
+
+    static public void warn(Object caller, String msg) {
+        warn(caller.getClass(), msg) ;
+    }
+
+    static public void warn(Class<? > cls, String msg) {
+        log(cls).warn(msg) ;
+    }
+
+    static public void warn(Object caller, String msg, Throwable th) {
+        warn(caller.getClass(), msg, th) ;
+    }
+
+    static public void warn(Class<? > cls, String msg, Throwable th) {
+        log(cls).warn(msg, th) ;
+    }
+
+    static public void fatal(Object caller, String msg) {
+        fatal(caller.getClass(), msg) ;
+    }
+
+    static public void fatal(Class<? > cls, String msg) {
+        log(cls).error(msg) ;
+    }
+
+    static public void fatal(Object caller, String msg, Throwable th) {
+        fatal(caller.getClass(), msg, th) ;
+    }
+
+    static public void fatal(Class<? > cls, String msg, Throwable th) {
+        log(cls).error(msg, th) ;
+    }
+
+    static public void fatal(String caller, String msg) {
+        log(caller).error(msg) ;
+    }
+
+    static private Logger log(Class<? > cls) {
+        return LoggerFactory.getLogger(cls) ;
+    }
+
+    static private Logger log(String loggerName) {
+        return LoggerFactory.getLogger(loggerName) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/logging/LogCtl.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/logging/LogCtl.java 
b/jena-base/src/main/java/org/apache/jena/atlas/logging/LogCtl.java
new file mode 100644
index 0000000..ad1adce
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/logging/LogCtl.java
@@ -0,0 +1,282 @@
+/**
+ * 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.atlas.logging;
+
+import java.io.* ;
+import java.util.Properties ;
+
+import org.apache.jena.atlas.AtlasException ;
+import org.apache.jena.atlas.lib.StrUtils ;
+import org.apache.log4j.PropertyConfigurator ;
+import org.apache.log4j.xml.DOMConfigurator ;
+import org.slf4j.Logger ;
+
+/** Setup and control of logging - needs access to log4j binaries */ 
+public class LogCtl {
+
+    /**
+     * Turn on a logger (all levels). Works for Log4j and Java logging as the
+     * logging provider to Apache common logging or slf4j.
+     */
+    
+    static public void enable(Logger logger) {
+        enable(logger.getName()) ;
+    }
+
+    static public void enable(String logger) {
+        set(logger, "all") ;
+    }
+
+    static public void set(Logger logger, String level) {
+        set(logger.getName(), level) ;
+    }
+
+    /** Turn on a logger (specific level levels) */
+    static public void set(Class<? > logger, String level) {
+        set(logger.getName(), level) ;
+    }
+
+    static public void set(String logger, String level) {
+        org.apache.log4j.Level level1 = org.apache.log4j.Level.ALL ;
+        java.util.logging.Level level2 = java.util.logging.Level.ALL ;
+        if ( level.equalsIgnoreCase("info") ) {
+            level1 = org.apache.log4j.Level.INFO ;
+            level2 = java.util.logging.Level.INFO ;
+        }
+    
+        if ( level.equalsIgnoreCase("debug") ) {
+            level1 = org.apache.log4j.Level.DEBUG ;
+            level2 = java.util.logging.Level.FINE ;
+        }
+    
+        if ( level.equalsIgnoreCase("warn") ) {
+            level1 = org.apache.log4j.Level.WARN ;
+            level2 = java.util.logging.Level.WARNING ;
+        }
+        if ( level.equalsIgnoreCase("error") ) {
+            level1 = org.apache.log4j.Level.ERROR ;
+            level2 = java.util.logging.Level.SEVERE ;
+        }
+        logLevel(logger, level1, level2) ;
+    }
+
+    static public void logLevel(String logger, org.apache.log4j.Level level1, 
java.util.logging.Level level2) {
+        if ( level1 != null )
+            org.apache.log4j.LogManager.getLogger(logger).setLevel(level1) ;
+        if ( level2 != null )
+            java.util.logging.Logger.getLogger(logger).setLevel(level2) ;
+    }
+
+    /**
+     * Turn on a logger (all levels). Works for Log4j and Java logging as the
+     * logging provider to Apache common logging or slf4j.
+     */
+    static public void enable(Class<? > logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.ALL)
 ;
+        
java.util.logging.Logger.getLogger(logger.getName()).setLevel(java.util.logging.Level.ALL)
 ;
+    }
+
+    /**
+     * Turn on a logger (all levels). Works for Log4j and Java logging as the
+     * logging provider to Apache common logging or slf4j.
+     */
+    static public void disable(String logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.OFF)
 ;
+        
java.util.logging.Logger.getLogger(logger).setLevel(java.util.logging.Level.OFF)
 ;
+    }
+
+    /**
+     * Turn on a logger (all levels). Works for Log4j and Java logging as the
+     * logging provider to Apache common logging or slf4j.
+     */
+    static public void disable(Class<? > logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.OFF)
 ;
+        
java.util.logging.Logger.getLogger(logger.getName()).setLevel(java.util.logging.Level.OFF)
 ;
+    }
+
+    /**
+     * Set to info level. Works for Log4j and Java logging as the logging
+     * provider to Apache common logging or slf4j.
+     */
+    static public void setInfo(String logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.INFO)
 ;
+        
java.util.logging.Logger.getLogger(logger).setLevel(java.util.logging.Level.INFO)
 ;
+    }
+
+    /**
+     * Set to info level. Works for Log4j and Java logging as the logging
+     * provider to Apache common logging or slf4j.
+     */
+    static public void setInfo(Class<? > logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.INFO)
 ;
+        
java.util.logging.Logger.getLogger(logger.getName()).setLevel(java.util.logging.Level.INFO)
 ;
+    }
+
+    /**
+     * Set to warning level. Works for Log4j and Java logging as the logging
+     * provider to Apache common logging or slf4j.
+     */
+    static public void setWarn(String logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.WARN)
 ;
+        
java.util.logging.Logger.getLogger(logger).setLevel(java.util.logging.Level.WARNING)
 ;
+    }
+
+    /**
+     * Set to warning level. Works for Log4j and Java logging as the logging
+     * provider to Apache common logging or slf4j.
+     */
+    static public void setWarn(Class<? > logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.WARN)
 ;
+        
java.util.logging.Logger.getLogger(logger.getName()).setLevel(java.util.logging.Level.WARNING)
 ;
+    }
+
+    /**
+     * Set to error level. Works for Log4j and Java logging as the logging
+     * provider to Apache common logging or slf4j.
+     */
+    static public void setError(String logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.ERROR)
 ;
+        
java.util.logging.Logger.getLogger(logger).setLevel(java.util.logging.Level.SEVERE)
 ;
+    }
+
+    /**
+     * Set to error level. Works for Log4j and Java logging as the logging
+     * provider to Apache common logging or slf4j.
+     */
+    static public void setError(Class<? > logger) {
+        
org.apache.log4j.LogManager.getLogger(logger).setLevel(org.apache.log4j.Level.ERROR)
 ;
+        
java.util.logging.Logger.getLogger(logger.getName()).setLevel(java.util.logging.Level.SEVERE)
 ;
+    }
+
+    private static String log4Jsetup = StrUtils.strjoinNL
+    ("## Plain output to stdout",
+     "log4j.appender.jena.plain=org.apache.log4j.ConsoleAppender",
+     "log4j.appender.jena.plain.target=System.out",
+     "log4j.appender.jena.plain.layout=org.apache.log4j.PatternLayout",
+     "log4j.appender.jena.plain.layout.ConversionPattern=%m%n"
+     ,
+     "## Plain output with level, to stderr",
+     "log4j.appender.jena.plainlevel=org.apache.log4j.ConsoleAppender",
+     "log4j.appender.jena.plainlevel.target=System.err",
+     "log4j.appender.jena.plainlevel.layout=org.apache.log4j.PatternLayout",
+     "log4j.appender.jena.plainlevel.layout.ConversionPattern=%-5p %m%n"
+    
+     , "## Everything", "log4j.rootLogger=INFO, jena.plainlevel",
+     "log4j.logger.com.hp.hpl.jena=WARN",
+     "log4j.logger.org.openjena=WARN",
+     "log4j.logger.org.apache.jena=WARN",
+     "log4j.logger.org.apache.jena.tdb.loader=INFO"
+    
+     , "## Parser output"
+     , "log4j.additivity.org.apache.jena.riot=false"
+     , "log4j.logger.org.apache.jena.riot=INFO, jena.plainlevel ") ;
+    // ---- java.util.logging - because that's always present.
+    static String defaultProperties = StrUtils.strjoinNL
+        (
+         // Handlers - output
+         // All (comma
+         // separated)
+         // 
"handlers=java.util.logging.ConsoleHandler,org.apache.jena.atlas.logging.java.ConsoleHandlerStdout",
+    
+         // Atlas.
+         "handlers=org.openjena.atlas.logging.java.ConsoleHandlerStdout",
+         "org.apache.atlas.jena.logging.java.ConsoleHandlerStdout.level=INFO",
+         
"java.util.logging.ConsoleHandler.formatter=atlas.logging.java.TextFormatter"
+    ) ;
+
+    /**
+     * Set logging
+     * <ol>
+     * <li>Check for -Dlog4j.configuration.</li>
+     * <li>Looks for log4j.properties file in current directory.</li>
+     * </ol>
+     * Return true if we think Log4J is not initialized.
+     */
+    
+    public static void setLog4j() {
+        if ( System.getProperty("log4j.configuration") == null ) {
+            String fn = "log4j.properties" ;
+            File f = new File(fn) ;
+            if ( f.exists() )
+                System.setProperty("log4j.configuration", "file:" + fn) ;
+        }
+    }
+
+    /** Set log4j properties (XML or properties file) */
+    public static void setLog4j(String filename) {
+        if ( filename.toLowerCase().endsWith(".xml") )
+            DOMConfigurator.configure(filename) ;
+        else
+            PropertyConfigurator.configure(filename) ;
+    }
+
+    /**
+     * Set logging, suitable for a command line application.
+     * If "log4j.configuration" not set, then use the built-in default, 
+     * else just leave to log4j startup.
+     */
+    public static void setCmdLogging() {
+        setCmdLogging(log4Jsetup) ;
+    }
+
+    /**
+     * Set logging, suitable for a command line application.
+     * If "log4j.configuration" not set, then use the provided default 
+     * (log4j properties syntax) else just leave to log4j startup.
+     */
+    public static void setCmdLogging(String defaultConfig) {
+        if (System.getProperty("log4j.configuration") == null )
+            resetLogging(defaultConfig) ;
+    }
+
+    public static void resetLogging(String config) {
+        Properties p = new Properties() ;
+        InputStream in = new 
ByteArrayInputStream(StrUtils.asUTF8bytes(config)) ;
+        try {
+            p.load(in) ;
+        } catch (IOException ex) {}
+        PropertyConfigurator.configure(p) ;
+        System.setProperty("log4j.configuration", "set") ;
+    }
+
+    public static void setJavaLogging() {
+        setJavaLogging("logging.properties") ;
+    }
+
+    public static void setJavaLogging(String file) {
+        try {
+            InputStream details = new FileInputStream(file) ;
+            
java.util.logging.LogManager.getLogManager().readConfiguration(details) ;
+        } catch (Exception ex) {
+            throw new AtlasException(ex) ;
+        }
+    }
+
+    public static void setJavaLoggingDft() {
+        try {
+            InputStream details = new 
ByteArrayInputStream(defaultProperties.getBytes("UTF-8")) ;
+            
java.util.logging.LogManager.getLogManager().readConfiguration(details) ;
+    
+        } catch (Exception ex) {
+            throw new AtlasException(ex) ;
+        }
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/logging/ProgressLogger.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/logging/ProgressLogger.java 
b/jena-base/src/main/java/org/apache/jena/atlas/logging/ProgressLogger.java
new file mode 100644
index 0000000..344885d
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/logging/ProgressLogger.java
@@ -0,0 +1,133 @@
+/*
+ * 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.atlas.logging;
+
+import static org.apache.jena.atlas.lib.DateTimeUtils.* ;
+import org.apache.jena.atlas.lib.Timer ;
+import org.slf4j.Logger ;
+
+/** Progress monitor */
+public class ProgressLogger
+{
+    private final Logger log ;
+    private final long tickPoint ;
+    private final int superTick ;
+    private final Timer timer ;
+    private final String label ;
+    
+    private long counterBatch = 0 ;
+    private long counterTotal = 0 ;
+    
+    private long lastTime = 0 ;
+    
+    public ProgressLogger(Logger log, String label, long tickPoint, int 
superTick)
+    {
+        this.log = log ;
+        this.label = label ;
+        this.tickPoint = tickPoint ;
+        this.superTick = superTick ;
+        this.timer = new Timer() ;
+    }
+    
+    public void startMessage() { 
+        print("Start:") ;
+    }
+    
+    public void finishMessage() { 
+        // Elapsed.
+        long timePoint = timer.getTimeInterval() ;
+    
+        // *1000L is milli to second conversion
+        if ( timePoint != 0 ) {
+            double time = timePoint/1000.0 ;
+            long runAvgRate   = (counterTotal * 1000L) / timePoint ;
+            
+            print("Finished: %,d %s %.2fs (Avg: %,d)", counterTotal, label, 
time, runAvgRate) ;
+        }
+        else
+            print("Finished: %,d %s (Avg: ----)", counterTotal, label) ;
+    }
+    
+    public void start()
+    {
+        timer.startTimer() ;
+        lastTime = 0 ;
+    }
+
+    public long finish()
+    {
+        long totalTime = timer.endTimer() ;
+        return totalTime ;
+    }
+    
+    public long getTicks()
+    {
+        return counterTotal ;
+    }
+    
+    public void tick()
+    {
+        counterBatch++ ;
+        counterTotal++ ;
+    
+        if ( tickPoint(counterTotal, tickPoint) )
+        {
+            long timePoint = timer.readTimer() ;
+            long thisTime = timePoint - lastTime ;
+        
+            // *1000L is milli to second conversion
+            if ( thisTime != 0 && timePoint != 0 ) {
+                long batchAvgRate = (counterBatch * 1000L) / thisTime;
+                long runAvgRate   = (counterTotal * 1000L) / timePoint ;
+                print("Add: %,d %s (Batch: %,d / Avg: %,d)", counterTotal, 
label, batchAvgRate, runAvgRate) ;
+            } else {
+                print("Add: %,d %s (Batch: ---- / Avg: ----)", counterTotal, 
label) ;
+            }
+            
+            lastTime = timePoint ;
+
+            if ( tickPoint(counterTotal, superTick*tickPoint) )
+                elapsed(timePoint) ;
+            counterBatch = 0 ;
+            lastTime = timePoint ;
+        }
+    }
+    
+    private void elapsed(long timerReading)
+    {
+        float elapsedSecs = timerReading/1000F ;
+        print("  Elapsed: %,.2f seconds [%s]", elapsedSecs, nowAsString()) ;
+    }
+    
+    /** Print a message in the form for this ProgressLogger */ 
+    public void print(String fmt, Object...args)
+    {
+        if ( log != null && log.isInfoEnabled() )
+        {
+            String str = String.format(fmt, args) ;
+            log.info(str) ;
+        }
+    }
+    
+    static boolean tickPoint(long counter, long quantum)
+    {
+        return counter%quantum == 0 ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/logging/java/ConsoleHandlerStdout.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/logging/java/ConsoleHandlerStdout.java
 
b/jena-base/src/main/java/org/apache/jena/atlas/logging/java/ConsoleHandlerStdout.java
new file mode 100644
index 0000000..7bfab7e
--- /dev/null
+++ 
b/jena-base/src/main/java/org/apache/jena/atlas/logging/java/ConsoleHandlerStdout.java
@@ -0,0 +1,75 @@
+/*
+ * 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.atlas.logging.java;
+
+import java.util.logging.Formatter ;
+import java.util.logging.Handler ;
+import java.util.logging.LogManager ;
+import java.util.logging.LogRecord ;
+
+
+public class ConsoleHandlerStdout extends Handler 
+{
+
+    private void configure()
+    {
+        LogManager manager = LogManager.getLogManager();
+        String cname = getClass().getName();
+        
+        String cls = manager.getProperty(cname+".formatter") ;
+        Formatter fmt = null ;
+        
+        try {
+            if (cls != null) {
+                Class<?> clz = 
ClassLoader.getSystemClassLoader().loadClass(cls);
+                fmt = (Formatter) clz.newInstance();
+            }
+        } catch (Exception ex) {
+            // We got one of a variety of exceptions in creating the
+            // class or creating an instance.
+            // Drop through.
+        }
+        if ( fmt == null )
+            fmt = new TextFormatter() ;
+        setFormatter(fmt) ;
+    }
+    
+    public ConsoleHandlerStdout()
+    {
+        configure() ;
+    }
+    
+    @Override
+    public void close() throws SecurityException
+    {}
+
+    @Override
+    public void flush()
+    { System.out.flush(); }
+
+    @Override
+    public void publish(LogRecord record)
+    {
+        if ( ! super.isLoggable(record) )
+            return ;
+        String s = getFormatter().format(record) ;
+        System.out.print(s) ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/logging/java/TextFormatter.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/logging/java/TextFormatter.java 
b/jena-base/src/main/java/org/apache/jena/atlas/logging/java/TextFormatter.java
new file mode 100644
index 0000000..3e97942
--- /dev/null
+++ 
b/jena-base/src/main/java/org/apache/jena/atlas/logging/java/TextFormatter.java
@@ -0,0 +1,53 @@
+/*
+ * 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.atlas.logging.java;
+
+import java.text.MessageFormat ;
+import java.util.Date ;
+import java.util.logging.Formatter ;
+import java.util.logging.LogRecord ;
+
+/** A pattern-like log formatter */
+public class TextFormatter extends Formatter
+{
+    @Override
+    public String format(LogRecord record) {
+        String loggerName = record.getLoggerName();
+        if(loggerName == null) {
+            loggerName = "root";
+        }
+        
+        int i = loggerName.lastIndexOf('.') ; 
+        String loggerNameShort = loggerName.substring(i+1) ;
+            
+        String formatted$ = record.getMessage() ;
+        if ( record.getParameters() != null )
+            formatted$ = MessageFormat.format(formatted$, 
record.getParameters()) ;
+        
+        // %tT (%5$tT) is %5$tH:%5$tM:%5$tS
+        // %tF is 2008-11-22 "%tY-%tm-%td"
+        return String.format("%5$tT %3$-5s %2$-25s :: %6$s\n", 
+                             loggerName,                        // 1
+                             loggerNameShort,                   // 2
+                             record.getLevel(),                 // 3
+                             Thread.currentThread().getName(),  // 4
+                             new Date(record.getMillis()),      // 5
+                             formatted$) ;                      // 6
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/base/Closeable.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/base/Closeable.java 
b/jena-base/src/main/java/org/apache/jena/base/Closeable.java
new file mode 100644
index 0000000..59a06aa
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/base/Closeable.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 org.apache.jena.base;
+
+/** Close this object.
+ *  This form does not allow Exceptions (it does allow RuntimeExceptions)  
+ */
+public interface Closeable {
+    public void close() ; 
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/base/Sys.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/base/Sys.java 
b/jena-base/src/main/java/org/apache/jena/base/Sys.java
new file mode 100644
index 0000000..8dbe8d8
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/base/Sys.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 org.apache.jena.base;
+
+import java.io.File ;
+
+public class Sys {
+    /** Is this windows? */
+    public static final boolean isWindows = (File.pathSeparatorChar == ';') ;
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java
----------------------------------------------------------------------
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java 
b/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java
new file mode 100644
index 0000000..f455207
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/TC_Atlas.java
@@ -0,0 +1,41 @@
+/*
+ * 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.atlas;
+
+import org.apache.jena.atlas.io.TS_IO ;
+import org.apache.jena.atlas.iterator.TS_Iterator ;
+import org.apache.jena.atlas.lib.TS_Lib ;
+import org.junit.runner.RunWith ;
+import org.junit.runners.Suite ;
+
+@RunWith(Suite.class)
[email protected]( {
+    // Library
+      TS_Lib.class
+    , TS_Iterator.class
+    , TS_IO.class
+//    , TS_Event.class
+//    , TS_JSON.class
+//    , TS_Data.class
+//    , TS_Web.class
+//    , TestCSVParser.class
+}) 
+
+public class TC_Atlas
+{}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/AbstractTestPeekInputStream.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/AbstractTestPeekInputStream.java
 
b/jena-base/src/test/java/org/apache/jena/atlas/io/AbstractTestPeekInputStream.java
new file mode 100644
index 0000000..d127385
--- /dev/null
+++ 
b/jena-base/src/test/java/org/apache/jena/atlas/io/AbstractTestPeekInputStream.java
@@ -0,0 +1,243 @@
+/*
+ * 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.atlas.io;
+
+import org.apache.jena.atlas.io.PeekInputStream ;
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.junit.Test ;
+
+public abstract class AbstractTestPeekInputStream extends BaseTest
+{
+    static int INIT_LINE = PeekInputStream.INIT_LINE ;
+    static int INIT_COL = PeekInputStream.INIT_COL ;
+    
+    abstract PeekInputStream make(String contents, int size) ;
+    
+    @Test public void read0()
+    {
+        assertEquals("Init line", 1, INIT_LINE) ;
+        assertEquals("Init col", 1, INIT_COL) ;
+    }
+    
+    @Test public void read1()
+    {
+        PeekInputStream in = make("") ;
+        checkLineCol(in, INIT_LINE, INIT_COL) ;
+        
+        int x = in.peekByte() ;
+        assertEquals(-1, x) ;
+        x = in.readByte() ;
+        assertEquals(-1, x) ;
+        x = in.readByte() ;
+        assertEquals(-1, x) ;
+    }
+    
+    @Test public void read2()
+    {
+        // Assumes we start at (1,1) 
+        PeekInputStream in = make("a") ;
+        checkLineCol(in, INIT_LINE, INIT_COL) ;
+        
+        int x = in.peekByte() ;
+        assertEquals('a', x) ;
+        checkLineCol(in, INIT_LINE, INIT_COL) ;
+        
+        x = in.readByte() ;
+        checkLineCol(in, INIT_LINE, INIT_COL+1) ;
+        assertEquals('a', x) ;
+        
+        x = in.peekByte() ;
+        assertEquals(-1, x) ;
+        
+        x = in.readByte() ;
+        assertEquals(-1, x) ;
+    }
+
+    @Test public void read3()
+    {
+        String c = "abcde" ;
+        PeekInputStream in = make(c) ;
+        
+        for ( int i = 0 ; i < c.length(); i++ )
+        {
+            checkLineCol(in, INIT_LINE, i+INIT_COL) ;
+            long z = in.getPosition() ;
+            assertEquals(i, in.getPosition()) ;
+            assertEquals(c.charAt(i), in.readByte()) ;
+        }
+        assertTrue(in.eof()) ;
+    }
+
+    @Test public void read4()
+    {
+        position("abcde") ;
+    }
+
+    @Test public void read5()
+    {
+        position("abc\nde") ;
+    }
+
+    @Test public void read6()
+    {
+        position("abc\nde\n") ;
+    }
+    
+    @Test public void read7()
+    {
+        position("") ;
+    }
+
+    @Test public void read8()
+    {
+        position("x") ;
+    }
+
+
+    @Test public void read9()
+    {
+        PeekInputStream in = make("a\nb\n") ;
+        checkLineCol(in, INIT_LINE, INIT_COL) ;
+        int x = in.peekByte() ;
+        assertEquals('a', x) ;
+        checkLineCol(in, INIT_LINE, INIT_COL) ;
+        
+        x = in.readByte() ;
+        assertEquals('a', x) ;
+        checkLineCol(in, INIT_LINE, INIT_COL+1) ;
+
+        x = in.readByte() ;
+        assertEquals('\n', x) ;
+        checkLineCol(in, INIT_LINE+1, INIT_COL) ;
+    }
+    
+    @Test public void unread1()
+    {
+        PeekInputStream in = make("abc") ;
+        assertEquals('a', in.peekByte()) ;
+        in.pushbackByte('Z') ;
+        assertEquals('Z', in.peekByte()) ;
+        contains(in, "Zabc") ;
+    }
+
+    @Test public void unread2()
+    {
+        PeekInputStream in = make("abc") ;
+        checkLineCol(in, INIT_LINE, INIT_COL) ;
+        int ch = in.readByte() ;
+        // Pushback does not move line/col backwards.
+        checkLineCol(in, INIT_LINE, INIT_COL+1) ;
+        assertEquals('b', in.peekByte()) ;
+        checkLineCol(in, INIT_LINE, INIT_COL+1) ;
+        in.pushbackByte('a') ;
+        checkLineCol(in, INIT_LINE, INIT_COL+1) ;
+        contains(in, "abc") ;
+    }
+    
+    @Test public void unread3()
+    {
+        PeekInputStream in = make("") ;
+        int ch = in.readByte() ;
+        assertEquals(-1, in.peekByte()) ;
+        in.pushbackByte('a') ;
+        contains(in, "a") ;
+    }
+
+    @Test public void unread4()
+    {
+        PeekInputStream in = make("") ;
+        int ch = in.readByte() ;
+        assertEquals(-1, in.peekByte()) ;
+        in.pushbackByte('0') ;
+        in.pushbackByte('1') ;
+        in.pushbackByte('2') ;
+        in.pushbackByte('3') ;
+        contains(in, "3210") ;   // Backwards!
+    }
+
+    @Test public void unread5()
+    {
+        PeekInputStream in = make("") ;
+        long lineNum = in.getLineNum() ;
+        long colNum = in.getColNum() ;
+        
+        checkLineCol(in, lineNum, colNum) ;
+        
+        in.pushbackByte('0') ;
+        checkLineCol(in, lineNum, colNum) ;  // Unmoved.
+        in.pushbackByte('1') ;
+        checkLineCol(in, lineNum, colNum) ;
+        assertEquals('1', in.readByte()) ;
+        
+        checkLineCol(in, lineNum, colNum) ;  // Unmoved.
+        in.pushbackByte('2') ;
+        in.pushbackByte('3') ;
+        checkLineCol(in, lineNum, colNum) ;  // Unmoved.
+        assertEquals('3', in.peekByte()) ;
+        contains(in, "320") ;
+    }
+
+    private void checkLineCol(PeekInputStream in, long lineNum, long colNum)
+    {
+        assertEquals("Line", lineNum, in.getLineNum()) ; 
+        assertEquals("Column", colNum, in.getColNum()) ;
+    }
+    
+    private void position(String contents)
+    {
+        PeekInputStream in = make(contents) ;
+        
+        int line = INIT_LINE ;
+        int col = INIT_COL ;
+        checkLineCol(in, line, col) ;
+        assertEquals(0, in.getPosition()) ;
+        
+        for ( int i = 0 ; i < contents.length(); i++ )
+        {
+            int x = in.readByte() ;
+            if ( x != -1 )
+            {
+                if ( x == '\n' )
+                {
+                    line++ ;
+                    col = INIT_COL ;
+                }
+                else
+                    col++ ;
+            }
+            assertEquals(contents.charAt(i), x) ;
+            assertEquals(i+1, in.getPosition()) ;
+            checkLineCol(in, line, col) ;
+        }
+        assertTrue(in.eof()) ;
+    }
+    
+    private void contains(PeekInputStream in, String contents)
+    {
+        for ( int i = 0 ; i < contents.length(); i++ )
+        {
+            int x = in.readByte() ;
+            assertEquals("\""+contents+"\" -- Index "+i+" 
Expected:'"+contents.charAt(i)+"' Got: '"+(char)x+"'", contents.charAt(i), x) ;
+        }
+        assertTrue(in.eof()) ;
+    }
+    
+    private PeekInputStream make(String contents)
+    { return make(contents, 2) ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/AbstractTestPeekReader.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/AbstractTestPeekReader.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/AbstractTestPeekReader.java
new file mode 100644
index 0000000..7ab67b5
--- /dev/null
+++ 
b/jena-base/src/test/java/org/apache/jena/atlas/io/AbstractTestPeekReader.java
@@ -0,0 +1,243 @@
+/*
+ * 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.atlas.io;
+
+import org.apache.jena.atlas.io.PeekReader ;
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.junit.Test ;
+
+public abstract class AbstractTestPeekReader extends BaseTest
+{
+    static int INIT_LINE = PeekReader.INIT_LINE ;
+    static int INIT_COL = PeekReader.INIT_COL ;
+    
+    abstract PeekReader make(String contents, int size) ;
+    
+    @Test public void read0()
+    {
+        assertEquals("Init line", 1, INIT_LINE) ;
+        assertEquals("Init col", 1, INIT_COL) ;
+    }
+    
+    @Test public void read1()
+    {
+        PeekReader r = make("") ;
+        checkLineCol(r, INIT_LINE, INIT_COL) ;
+        
+        int x = r.peekChar() ;
+        assertEquals(-1, x) ;
+        x = r.readChar() ;
+        assertEquals(-1, x) ;
+        x = r.readChar() ;
+        assertEquals(-1, x) ;
+    }
+    
+    @Test public void read2()
+    {
+        // Assumes we start at (1,1) 
+        PeekReader r = make("a") ;
+        checkLineCol(r, INIT_LINE, INIT_COL) ;
+        
+        int x = r.peekChar() ;
+        assertEquals('a', x) ;
+        checkLineCol(r, INIT_LINE, INIT_COL) ;
+        
+        x = r.readChar() ;
+        checkLineCol(r, INIT_LINE, INIT_COL+1) ;
+        assertEquals('a', x) ;
+        
+        x = r.peekChar() ;
+        assertEquals(-1, x) ;
+        
+        x = r.readChar() ;
+        assertEquals(-1, x) ;
+    }
+
+    @Test public void read3()
+    {
+        String c = "abcde" ;
+        PeekReader r = make(c) ;
+        
+        for ( int i = 0 ; i < c.length(); i++ )
+        {
+            checkLineCol(r, INIT_LINE, i+INIT_COL) ;
+            long z = r.getPosition() ;
+            assertEquals(i, r.getPosition()) ;
+            assertEquals(c.charAt(i), r.readChar()) ;
+        }
+        assertTrue(r.eof()) ;
+    }
+
+    @Test public void read4()
+    {
+        position("abcde") ;
+    }
+
+    @Test public void read5()
+    {
+        position("abc\nde") ;
+    }
+
+    @Test public void read6()
+    {
+        position("abc\nde\n") ;
+    }
+    
+    @Test public void read7()
+    {
+        position("") ;
+    }
+
+    @Test public void read8()
+    {
+        position("x") ;
+    }
+
+
+    @Test public void read9()
+    {
+        PeekReader r = make("a\nb\n") ;
+        checkLineCol(r, INIT_LINE, INIT_COL) ;
+        int x = r.peekChar() ;
+        assertEquals('a', x) ;
+        checkLineCol(r, INIT_LINE, INIT_COL) ;
+        
+        x = r.readChar() ;
+        assertEquals('a', x) ;
+        checkLineCol(r, INIT_LINE, INIT_COL+1) ;
+
+        x = r.readChar() ;
+        assertEquals('\n', x) ;
+        checkLineCol(r, INIT_LINE+1, INIT_COL) ;
+    }
+    
+    @Test public void unread1()
+    {
+        PeekReader r = make("abc") ;
+        assertEquals('a', r.peekChar()) ;
+        r.pushbackChar('Z') ;
+        assertEquals('Z', r.peekChar()) ;
+        contains(r, "Zabc") ;
+    }
+
+    @Test public void unread2()
+    {
+        PeekReader r = make("abc") ;
+        checkLineCol(r, INIT_LINE, INIT_COL) ;
+        int ch = r.readChar() ;
+        // Pushback does not move line/col backwards.
+        checkLineCol(r, INIT_LINE, INIT_COL+1) ;
+        assertEquals('b', r.peekChar()) ;
+        checkLineCol(r, INIT_LINE, INIT_COL+1) ;
+        r.pushbackChar('a') ;
+        checkLineCol(r, INIT_LINE, INIT_COL+1) ;
+        contains(r, "abc") ;
+    }
+    
+    @Test public void unread3()
+    {
+        PeekReader r = make("") ;
+        int ch = r.readChar() ;
+        assertEquals(-1, r.peekChar()) ;
+        r.pushbackChar('a') ;
+        contains(r, "a") ;
+    }
+
+    @Test public void unread4()
+    {
+        PeekReader r = make("") ;
+        int ch = r.readChar() ;
+        assertEquals(-1, r.peekChar()) ;
+        r.pushbackChar('0') ;
+        r.pushbackChar('1') ;
+        r.pushbackChar('2') ;
+        r.pushbackChar('3') ;
+        contains(r, "3210") ;   // Backwards!
+    }
+
+    @Test public void unread5()
+    {
+        PeekReader r = make("") ;
+        long lineNum = r.getLineNum() ;
+        long colNum = r.getColNum() ;
+        
+        checkLineCol(r, lineNum, colNum) ;
+        
+        r.pushbackChar('0') ;
+        checkLineCol(r, lineNum, colNum) ;  // Unmoved.
+        r.pushbackChar('1') ;
+        checkLineCol(r, lineNum, colNum) ;
+        assertEquals('1', r.readChar()) ;
+        
+        checkLineCol(r, lineNum, colNum) ;  // Unmoved.
+        r.pushbackChar('2') ;
+        r.pushbackChar('3') ;
+        checkLineCol(r, lineNum, colNum) ;  // Unmoved.
+        assertEquals('3', r.peekChar()) ;
+        contains(r, "320") ;
+    }
+
+    private void checkLineCol(PeekReader r, long lineNum, long colNum)
+    {
+        assertEquals("Line", lineNum, r.getLineNum()) ; 
+        assertEquals("Column", colNum, r.getColNum()) ;
+    }
+    
+    private void position(String contents)
+    {
+        PeekReader r = make(contents) ;
+        
+        int line = INIT_LINE ;
+        int col = INIT_COL ;
+        checkLineCol(r, line, col) ;
+        assertEquals(0, r.getPosition()) ;
+        
+        for ( int i = 0 ; i < contents.length(); i++ )
+        {
+            int x = r.readChar() ;
+            if ( x != -1 )
+            {
+                if ( x == '\n' )
+                {
+                    line++ ;
+                    col = INIT_COL ;
+                }
+                else
+                    col++ ;
+            }
+            assertEquals(contents.charAt(i), x) ;
+            assertEquals(i+1, r.getPosition()) ;
+            checkLineCol(r, line, col) ;
+        }
+        assertTrue(r.eof()) ;
+    }
+    
+    private void contains(PeekReader r, String contents)
+    {
+        for ( int i = 0 ; i < contents.length(); i++ )
+        {
+            int x = r.readChar() ;
+            assertEquals("\""+contents+"\" -- Index "+i+" 
Expected:'"+contents.charAt(i)+"' Got: '"+(char)x+"'", contents.charAt(i), x) ;
+        }
+        assertTrue(r.eof()) ;
+    }
+    
+    private PeekReader make(String contents)
+    { return make(contents, 2) ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TS_IO.java
----------------------------------------------------------------------
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/io/TS_IO.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TS_IO.java
new file mode 100644
index 0000000..4479243
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/io/TS_IO.java
@@ -0,0 +1,45 @@
+/*
+ * 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.atlas.io;
+
+import org.junit.runner.RunWith ;
+import org.junit.runners.Suite ;
+
+@RunWith(Suite.class)
[email protected]( {
+    // Basic classes
+    TestIndentedWriter.class
+    , TestStreamUTF8.class
+    , TestBlockUTF8.class
+    , TestInputStreamBuffered.class
+
+    // Peek readers.
+    , TestPeekReaderSource.class
+    , TestPeekReaderCharSequence.class
+    , TestPeekInputStreamSource.class
+
+    // Writers
+    , TestBufferingWriter.class
+    // Other
+    , TestPrintUtils.class
+} )
+public class TS_IO
+{
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestBlockUTF8.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestBlockUTF8.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestBlockUTF8.java
new file mode 100644
index 0000000..e29d78a
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/io/TestBlockUTF8.java
@@ -0,0 +1,254 @@
+/*
+ * 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.atlas.io;
+
+import java.io.ByteArrayOutputStream ;
+import java.io.IOException ;
+import java.io.OutputStreamWriter ;
+import java.io.Writer ;
+import java.nio.Buffer ;
+import java.nio.ByteBuffer ;
+import java.nio.CharBuffer ;
+import java.nio.charset.Charset ;
+import java.nio.charset.CharsetDecoder ;
+import java.nio.charset.CharsetEncoder ;
+
+import org.apache.jena.atlas.io.BlockUTF8 ;
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.apache.jena.atlas.lib.Chars ;
+import org.junit.Test ;
+
+public class TestBlockUTF8 extends BaseTest
+{
+    // Need array and non-array versions.
+    
+    static Charset utf8 = Chars.charsetUTF8 ;
+    static CharsetDecoder dec = utf8.newDecoder() ;
+    static CharsetEncoder enc = utf8.newEncoder() ;
+
+    // UTF-8 encoding.
+    // character '¢' = code point U+00A2 -> C2 A2
+    // character '€' = code point U+20AC -> E2 82 AC
+
+    static private final String asciiBase             = "abc" ;
+    static private final String latinBase             = "Àéíÿ" ;
+    static private final String latinExtraBase        = "ỹfifl" ;  // 
fi-ligature, fl-ligature
+    static private final String greekBase             = "αβγ" ;
+    static private final String hewbrewBase           = "אבג" ;
+    static private final String arabicBase            = "ءآأ";
+    static private final String symbolsBase           = "☺☻♪♫" ;
+    static private final String chineseBase           = "孫子兵法" ; // 
The Art of War 
+    static private final String japaneseBase          = "日本" ;    // 
Japanese
+    static private final String binaryStr1            = "abc\uD800xyz" ;    // 
A single surrogate, without it's pair. 
+    static private final String binaryStr2            = "\uD800" ;          // 
A single surrogate, without it's pair. 
+    static private final String binaryStr3            = "\u0000" ;          // 
A zero character  
+
+    static private final byte[] binaryBytes1 = {} ;         
+    static private final byte[] binaryBytes2 = { (byte)0x00 } ;             // 
Java encoding of 0 codepoint is 0         
+    static private final byte[] binaryBytes3 = { (byte)0xC0, (byte)0x80 } ;    
 // Modifed unicode zero codepoint.         
+
+    @Test public void convert_in_00() { testIn("") ; }
+    @Test public void convert_in_01() { testIn(asciiBase) ; }
+    @Test public void convert_in_02() { testIn(latinBase) ; }
+    @Test public void convert_in_03() { testIn(latinExtraBase) ; }
+    @Test public void convert_in_04() { testIn(greekBase) ; }
+    @Test public void convert_in_05() { testIn(hewbrewBase) ; }
+    @Test public void convert_in_06() { testIn(arabicBase) ; }
+    @Test public void convert_in_07() { testIn(symbolsBase) ; }
+    @Test public void convert_in_08() { testIn(chineseBase) ; }
+    @Test public void convert_in_09() { testIn(japaneseBase) ; }
+    @Test public void convert_in_10() { testInOutBinary(binaryStr1) ; }  
+    @Test public void convert_in_11() { testInOutBinary(binaryStr2) ; }  
+    @Test public void convert_in_12() { testInOutBinary(binaryStr3) ; }  
+
+    @Test public void convert_out_00() { testOut("") ; }
+    @Test public void convert_out_01() { testOut(asciiBase) ; }
+    @Test public void convert_out_02() { testOut(latinBase) ; }
+    @Test public void convert_out_03() { testOut(latinExtraBase) ; }
+    @Test public void convert_out_04() { testOut(greekBase) ; }
+    @Test public void convert_out_05() { testOut(hewbrewBase) ; }
+    @Test public void convert_out_06() { testOut(arabicBase) ; }
+    @Test public void convert_out_07() { testOut(symbolsBase) ; }
+    @Test public void convert_out_08() { testOut(chineseBase) ; }
+    @Test public void convert_out_09() { testOut(japaneseBase) ; }
+    @Test public void convert_out_10() { testOut(binaryStr1) ; }
+    @Test public void convert_out_11() { testOut(binaryStr2) ; }
+    @Test public void convert_out_12() { testOut(binaryStr3) ; }
+
+    // While it is key is chars->bytes-chars, we also test bytes->bytes 
+    @Test public void binary_01() { testBinary(binaryBytes1) ; }
+    @Test public void binary_02() { testBinary(binaryBytes2) ; }
+    @Test public void binary_03() { testBinary(binaryBytes3, binaryBytes2) ; }
+
+    @Test public void binary_10() { testBinary(binaryBytes2, 
CharBuffer.wrap(binaryStr3)) ; }
+    @Test public void binary_11() { testBinary(binaryBytes3, 
CharBuffer.wrap(binaryStr3)) ; }
+
+    
+    static void testIn(String x)
+    {
+        testIn(x, allocByteBufferArray, allocCharBufferArray) ;
+        testIn(x, allocByteBufferDirect, allocCharBufferDirect) ;
+
+    }
+    static void testIn(String x, Alloc<ByteBuffer> allocBB, Alloc<CharBuffer> 
allocCB)
+    {
+        // Test as binary.
+        testInOutBinary(x) ;
+
+        // Now test, comparing to std Java.
+        // Correct answer, in bytes
+        ByteBuffer bytes = ByteBuffer.wrap(stringAsBytes(x)) ;
+        // To bytes.stringAsBytes
+        int N = x.length() ;
+        CharBuffer cb = CharBuffer.wrap(x.toCharArray()) ;
+        ByteBuffer bb = allocBB.allocate(4*N) ;
+        BlockUTF8.fromChars(cb, bb) ;
+        bb.flip() ;
+
+        assertTrue("Bytes", sameBytes(bytes, bb)) ;
+        // From bytes.
+        CharBuffer cb2 = allocCB.allocate(N) ;
+        BlockUTF8.toChars(bb, cb2) ;
+        cb2.flip() ;
+        String str = cb2.toString() ;
+        assertEquals(x, str) ;
+    }
+
+    // Tesing, but not against what Java would do (it replaces bad chars, we 
want binary).
+    static void testInOutBinary(String x)
+    {
+        int N = x.length() ;
+        CharBuffer cb = CharBuffer.wrap(x.toCharArray()) ;
+        ByteBuffer bb = ByteBuffer.allocate(4*N) ;
+        BlockUTF8.fromChars(cb, bb) ;
+        bb.flip() ;
+        CharBuffer cb2 = CharBuffer.allocate(N) ;
+        BlockUTF8.toChars(bb, cb2) ;
+        // compare cb and cb2.
+        String str = new String(cb2.array(), 0, cb2.position()) ;
+        assertEquals(x, str) ;
+
+        // And re-code as bytes.
+        CharBuffer cb3 = CharBuffer.wrap(x.toCharArray()) ;
+        ByteBuffer bb3 = ByteBuffer.allocate(4*N) ;
+        BlockUTF8.fromChars(cb3, bb3) ;
+        bb3.flip() ;
+        assertArrayEquals(bb.array(), bb3.array()) ;
+    }
+
+    static void testOut(String x)
+    {
+        testOut(x, allocByteBufferArray, allocCharBufferArray) ;
+        testOut(x, allocByteBufferDirect, allocCharBufferDirect) ;
+    }
+    
+    static interface Alloc<T extends Buffer> { T allocate(int len) ; } 
+    static Alloc<ByteBuffer> allocByteBufferArray = new Alloc<ByteBuffer>() {
+        @Override public ByteBuffer allocate(int len) { return 
ByteBuffer.allocate(len) ; }
+     } ;
+     static Alloc<ByteBuffer> allocByteBufferDirect = new Alloc<ByteBuffer>() {
+         @Override public ByteBuffer allocate(int len) { return 
ByteBuffer.allocateDirect(len) ; }
+     } ;
+     static Alloc<CharBuffer> allocCharBufferArray = new Alloc<CharBuffer>() {
+         @Override public CharBuffer allocate(int len) { return 
CharBuffer.allocate(len) ; }
+      } ;
+      static Alloc<CharBuffer> allocCharBufferDirect = new Alloc<CharBuffer>() 
{
+          @Override public CharBuffer allocate(int len) { return 
ByteBuffer.allocateDirect(2*len).asCharBuffer() ; }
+    } ;
+    
+    static void testOut(String x, Alloc<ByteBuffer> allocBB, Alloc<CharBuffer> 
allocCB)
+    {
+        testBinary(stringAsBytes(x)) ;
+
+        int N = x.length() ;
+        // First - get bytes the Java way.
+        ByteBuffer bytes = ByteBuffer.wrap(stringAsBytes(x)) ;
+        CharBuffer cb = allocCB.allocate(N) ;
+
+        BlockUTF8.toChars(bytes, cb) ;
+        cb.flip() ;
+        bytes.flip() ;
+
+        String str = cb.toString() ;
+        ByteBuffer bytes2 = allocBB.allocate(bytes.capacity()) ;
+        BlockUTF8.fromChars(cb, bytes2) ;
+        bytes2.flip() ;
+        assertTrue("Chars", sameBytes(bytes, bytes2)) ;
+    }
+
+    static void testBinary(byte[] binary, CharBuffer chars)
+    {
+        int N = binary.length ;
+        ByteBuffer bytes = ByteBuffer.wrap(binary) ;
+        CharBuffer cb = CharBuffer.allocate(N) ;
+        BlockUTF8.toChars(bytes, cb) ;
+        cb.flip() ;
+        assertTrue("Binary", sameChars(chars, cb));
+    }
+
+    static void testBinary(byte[] binary)
+    {
+        testBinary(binary, binary) ;
+    }
+
+    static void testBinary(byte[] binary, byte[] expected)
+    {
+        int N = binary.length ;
+        ByteBuffer bytes = ByteBuffer.wrap(binary) ;
+        CharBuffer cb = CharBuffer.allocate(N) ;
+        BlockUTF8.toChars(bytes, cb) ;
+        cb.flip() ;
+        bytes.position(0) ;
+        ByteBuffer bytes2 = ByteBuffer.allocate(2*N) ;  // Null bytes get 
expanded.
+        BlockUTF8.fromChars(cb, bytes2) ;
+        bytes2.flip() ;
+        sameBytes(bytes, bytes2) ;
+        assertTrue("Binary", sameBytes(ByteBuffer.wrap(expected), bytes2)) ;
+    }
+
+    // Does not move position.
+    static boolean sameBytes(ByteBuffer bb1, ByteBuffer bb2)
+    {
+        if ( bb1.remaining() != bb2.remaining() ) return false ;
+    
+        for ( int i = 0 ; i < bb1.remaining() ; i++ )
+            if ( bb1.get(i+bb1.position()) != bb2.get(i+bb2.position()) ) 
return false ;
+        return true ;
+    }
+    // Does not move position.
+    static boolean sameChars(CharBuffer cb1, CharBuffer cb2)
+    {
+        if ( cb1.remaining() != cb2.remaining() ) return false ;
+    
+        for ( int i = 0 ; i < cb1.remaining() ; i++ )
+            if ( cb1.get(i+cb1.position()) != cb2.get(i+cb2.position()) ) 
return false ;
+        return true ;
+    }
+    static byte[] stringAsBytes(String x)
+    {
+        try {
+            ByteArrayOutputStream bout = new ByteArrayOutputStream() ;
+            try(Writer out = new OutputStreamWriter(bout, utf8)) {
+                out.write(x) ;
+            }
+            byte[] bytes = bout.toByteArray() ;
+            return bytes ;
+        } catch (IOException ex) { throw new RuntimeException(ex) ; } 
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestBufferingWriter.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestBufferingWriter.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestBufferingWriter.java
new file mode 100644
index 0000000..08e81c6
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/io/TestBufferingWriter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.atlas.io ;
+
+import java.io.StringWriter ;
+
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.junit.Test ;
+
+public class TestBufferingWriter extends BaseTest {
+    StringWriter    sw = null ;
+    BufferingWriter w  = null ;
+
+    public void create(int size, int blobSize) {
+        sw = new StringWriter() ;
+        w = new BufferingWriter(sw, size, blobSize) ;
+    }
+
+    public String string() {
+        return sw.toString() ;
+    }
+
+    @Test
+    public void write_01() {
+        create(10, 5) ;
+        w.output("x") ;
+        w.flush() ;
+        String x = string() ;
+        assertEquals("x", x) ;
+    }
+
+    @Test
+    public void write_02() {
+        create(10, 5) ;
+        w.output("foofoo") ; // Large object
+        w.flush() ;
+        String x = string() ;
+        assertEquals("foofoo", x) ;
+    }
+
+    @Test
+    public void write_03() {
+        create(10, 8) ;
+        w.output("a") ;
+        w.output("b") ;
+        w.output("c") ;
+        w.flush() ;
+        String x = string() ;
+        assertEquals("abc", x) ;
+    }
+
+    @Test
+    public void write_04() {
+        create(10, 8) ;
+        w.output("abcdefghijklmnopqrstuvwxyz") ;
+        w.output("XYZ") ;
+        w.flush() ;
+        String x = string() ;
+        assertEquals("abcdefghijklmnopqrstuvwxyzXYZ", x) ;
+    }
+
+    @Test
+    public void write_05() {
+        create(10, 8) ;
+        w.output("") ;
+        w.flush() ;
+        String x = string() ;
+        assertEquals("", x) ;
+    }
+
+    @Test
+    public void write_06() {
+        // Test closing the stream without flushing (the flush should be done
+        // implicitly)
+        create(100, 50) ;
+        w.output("test") ;
+        w.close() ;
+        String x = string() ;
+        assertEquals("test", x) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestIndentedWriter.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestIndentedWriter.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestIndentedWriter.java
new file mode 100644
index 0000000..77608d4
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/io/TestIndentedWriter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.atlas.io;
+
+import org.apache.jena.atlas.io.IndentedLineBuffer ;
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.junit.Test ;
+
+public class TestIndentedWriter extends BaseTest
+{
+    @Test public void write01()
+    {
+        try(IndentedLineBuffer b = new IndentedLineBuffer()) {
+            b.print("hell") ;
+            b.print("o") ;
+            assertEquals("hello", b.asString()) ;
+        }
+    }
+    
+    @Test public void write02()
+    {
+        try(IndentedLineBuffer b = new IndentedLineBuffer()) {
+            b.incIndent() ;
+            b.print("hell") ;
+            b.print("o") ;
+            b.decIndent() ;
+            assertEquals("  hello", b.asString()) ;
+        }
+    }
+    
+    @Test public void write03()
+    {
+        try(IndentedLineBuffer b = new IndentedLineBuffer()) {
+            b.incIndent() ;
+            b.printf("0x%04X", 1) ;
+            b.println() ;
+            b.print("XX") ;
+            b.decIndent() ;
+            assertEquals("  0x0001\n  XX", b.asString()) ;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestInputStreamBuffered.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestInputStreamBuffered.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestInputStreamBuffered.java
new file mode 100644
index 0000000..292bb64
--- /dev/null
+++ 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestInputStreamBuffered.java
@@ -0,0 +1,109 @@
+/*
+ * 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.atlas.io;
+
+import java.io.ByteArrayInputStream ;
+import java.io.IOException ;
+import java.io.InputStream ;
+
+import org.apache.jena.atlas.io.InputStreamBuffered ;
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.apache.jena.atlas.lib.Bytes ;
+import org.junit.Test ;
+
+public class TestInputStreamBuffered extends BaseTest
+{
+    @Test public void test_01() throws IOException
+    {
+        InputStream in = stream("") ;
+        InputStream in2 = new InputStreamBuffered(in) ;
+        int x = count(in2) ;
+        assertEquals(0, x) ;
+    }
+    
+    @Test public void test_02() throws IOException
+    {
+        InputStream in = stream(1,2,3,4) ;
+        InputStream in2 = new InputStreamBuffered(in) ;
+        check(in2, 1,2,3,4) ;
+    }
+    
+    @Test public void test_03() throws IOException
+    {
+        InputStream in = stream(1,2,3,4) ;
+        InputStream in2 = new InputStreamBuffered(in, 2) ;
+        check(in2, 1,2,3,4) ;
+    }
+    
+    @Test public void test_04() throws IOException
+    {
+        InputStream in = stream(1,2,3,4) ;
+        InputStream in2 = new InputStreamBuffered(in, 1) ;
+        check(in2, 1,2,3,4) ;
+        assertEquals(-1, in.read()) ;
+    }
+    
+    
+    private static InputStream stream(String data)
+    {
+        byte[] b = Bytes.string2bytes(data) ;
+        return new ByteArrayInputStream(b) ;
+    }
+    
+    private static InputStream stream(byte...bytes)
+    {
+        return new ByteArrayInputStream(bytes) ;
+    }
+
+    private static InputStream stream(int...bytes)
+    {
+        return stream(ints2bytes(bytes)) ;
+    }
+
+    // Convenience.
+    private static byte[] ints2bytes(int...values)
+    {
+        byte b[] = new byte[values.length] ;
+        for ( int i = 0 ; i < b.length ; i++ )
+            b[i] = (byte)values[i] ;
+        return b ;
+    }
+
+    private static int count(InputStream in) throws IOException
+    {
+        int count = 0 ;
+        while(in.read() != -1 )
+            count++ ;
+        return count ;
+    }
+
+    private static void check(InputStream in, int ...bytes) throws IOException
+    {
+        check(in, ints2bytes(bytes)) ;
+    }
+
+    private static void check(InputStream in, byte ...bytes) throws IOException
+    {
+        for ( byte b : bytes )
+        {
+            assertEquals(b, (byte)in.read()) ;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekInputStreamSource.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekInputStreamSource.java
 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekInputStreamSource.java
new file mode 100644
index 0000000..379a78a
--- /dev/null
+++ 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekInputStreamSource.java
@@ -0,0 +1,44 @@
+/*
+ * 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.atlas.io;
+
+import java.io.ByteArrayInputStream ;
+import java.io.UnsupportedEncodingException ;
+
+import org.apache.jena.atlas.io.PeekInputStream ;
+
+public class TestPeekInputStreamSource extends AbstractTestPeekInputStream
+{
+    @Override
+    PeekInputStream make(String contents, int size)
+    {
+        // Very carefuly ensure this is not a byte array-based PeekReader
+        ByteArrayInputStream bin ;
+        try
+        {
+            bin = new ByteArrayInputStream(contents.getBytes("ASCII")) ;
+        } catch (UnsupportedEncodingException ex)
+        {
+            ex.printStackTrace();
+            return null ;
+        }
+        
+        return PeekInputStream.make(bin, size) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekReaderCharSequence.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekReaderCharSequence.java
 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekReaderCharSequence.java
new file mode 100644
index 0000000..f6c5c42
--- /dev/null
+++ 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekReaderCharSequence.java
@@ -0,0 +1,32 @@
+/*
+ * 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.atlas.io;
+
+import org.apache.jena.atlas.io.PeekReader ;
+
+
+
+public class TestPeekReaderCharSequence extends AbstractTestPeekReader
+{
+    @Override
+    PeekReader make(String contents, int size)
+    {
+        return PeekReader.readString(contents) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekReaderSource.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekReaderSource.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekReaderSource.java
new file mode 100644
index 0000000..2624351
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/io/TestPeekReaderSource.java
@@ -0,0 +1,34 @@
+/*
+ * 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.atlas.io;
+
+import java.io.StringReader ;
+
+import org.apache.jena.atlas.io.PeekReader ;
+
+public class TestPeekReaderSource extends AbstractTestPeekReader
+{
+    @Override
+    PeekReader make(String contents, int size)
+    {
+        // Very carefuly ensure this is not a string-based PeekReader
+        StringReader r = new StringReader(contents) ;
+        return PeekReader.make(r, size) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestPrintUtils.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestPrintUtils.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestPrintUtils.java
new file mode 100644
index 0000000..aa6f3a6
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/io/TestPrintUtils.java
@@ -0,0 +1,59 @@
+/*
+ * 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.atlas.io;
+
+import java.io.ByteArrayOutputStream ;
+import java.io.IOException ;
+import java.io.OutputStreamWriter ;
+import java.io.Writer ;
+
+import org.apache.jena.atlas.io.OutputUtils ;
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.junit.Test ;
+
+public class TestPrintUtils extends BaseTest
+{
+    @Test public void hex1()
+    {
+        String s = test(0,4) ;
+        assertEquals("0000", s) ;
+    }
+    
+    @Test public void hex2()
+    {
+        String s = test(1,8) ;
+        assertEquals("00000001", s) ;
+    }
+
+    @Test public void hex3()
+    {
+        String s = test(0xFF,2) ;
+        assertEquals("FF", s) ;
+    }
+
+    private static String test(int value, int width)
+    {
+        ByteArrayOutputStream x = new ByteArrayOutputStream() ;
+        Writer out = new OutputStreamWriter(x) ;
+        OutputUtils.printHex(out, value, width) ;
+        try { out.flush() ; } catch (IOException ex) {}
+        String s = x.toString() ;
+        return s ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/test/java/org/apache/jena/atlas/io/TestStreamUTF8.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/test/java/org/apache/jena/atlas/io/TestStreamUTF8.java 
b/jena-base/src/test/java/org/apache/jena/atlas/io/TestStreamUTF8.java
new file mode 100644
index 0000000..4c92978
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/io/TestStreamUTF8.java
@@ -0,0 +1,118 @@
+/*
+ * 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.atlas.io;
+
+import java.io.ByteArrayInputStream ;
+import java.io.ByteArrayOutputStream ;
+import java.io.IOException ;
+import java.io.OutputStreamWriter ;
+import java.io.Writer ;
+import java.nio.charset.Charset ;
+import java.nio.charset.CharsetDecoder ;
+import java.nio.charset.CharsetEncoder ;
+
+import org.apache.jena.atlas.io.InStreamUTF8 ;
+import org.apache.jena.atlas.io.OutStreamUTF8 ;
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.apache.jena.atlas.lib.Chars ;
+import org.junit.Test ;
+
+public class TestStreamUTF8 extends BaseTest
+    {
+        static Charset utf8 = Chars.charsetUTF8 ;
+        static CharsetDecoder dec = utf8.newDecoder() ;
+        static CharsetEncoder enc = utf8.newEncoder() ;
+        
+        // UTF-8 encoding.
+        // character '¢' = code point U+00A2 -> C2 A2
+        // character '€' = code point U+20AC -> E2 82 AC
+        
+        static private final String asciiBase             = "abc" ;
+        static private final String latinBase             = "Àéíÿ" ;
+        static private final String latinExtraBase        = "ỹfifl" ;  // 
fi-ligature, fl-ligature
+        static private final String greekBase             = "αβγ" ;
+        static private final String hewbrewBase           = "אבג" ;
+        static private final String arabicBase            = "ءآأ";
+        static private final String symbolsBase           = "☺☻♪♫" ;
+        static private final String chineseBase           = "孫子兵法" ; 
// The Art of War 
+        static private final String japaneseBase          = "日本" ;    // 
Japanese
+        
+        @Test public void test_in_00() { testIn("") ; }
+        @Test public void test_in_01() { testIn(asciiBase) ; }
+        @Test public void test_in_02() { testIn(latinBase) ; }
+        @Test public void test_in_03() { testIn(latinExtraBase) ; }
+        @Test public void test_in_04() { testIn(greekBase) ; }
+        @Test public void test_in_05() { testIn(hewbrewBase) ; }
+        @Test public void test_in_06() { testIn(arabicBase) ; }
+        @Test public void test_in_07() { testIn(symbolsBase) ; }
+        @Test public void test_in_08() { testIn(chineseBase) ; }
+        @Test public void test_in_09() { testIn(japaneseBase) ; }
+        
+        @Test public void test_out_00() { testIn("") ; }
+        @Test public void test_out_01() { testOut(asciiBase) ; }
+        @Test public void test_out_02() { testOut(latinBase) ; }
+        @Test public void test_out_03() { testOut(latinExtraBase) ; }
+        @Test public void test_out_04() { testOut(greekBase) ; }
+        @Test public void test_out_05() { testOut(hewbrewBase) ; }
+        @Test public void test_out_06() { testOut(arabicBase) ; }
+        @Test public void test_out_07() { testOut(symbolsBase) ; }
+        @Test public void test_out_08() { testOut(chineseBase) ; }
+        @Test public void test_out_09() { testOut(japaneseBase) ; }
+        
+        static void testIn(String x)
+        {
+            try {
+                byte[] bytes = stringAsBytes(x) ;
+
+                ByteArrayInputStream bin = new ByteArrayInputStream(bytes) ;
+                // Create string from bytes
+                try(InStreamUTF8 r = new InStreamUTF8(bin)) {
+                    char[] cbuff = new char[x.length()*10] ;    // Way too big
+                    int len = r.read(cbuff) ;
+                    String str = new String(cbuff, 0 , len) ;
+                    assertEquals(x, str) ;
+                }
+            } catch (IOException ex) { throw new RuntimeException(ex) ; } 
+        }
+
+        static void testOut(String x)
+        {
+            try {
+                byte[] bytes = stringAsBytes(x) ;
+                ByteArrayOutputStream bout = new ByteArrayOutputStream() ;
+                try(Writer out = new OutStreamUTF8(bout)) {
+                    out.write(x) ;
+                }
+                byte[] bytes2 = bout.toByteArray() ;
+                assertArrayEquals(bytes, bytes2) ;
+            } catch (IOException ex) { throw new RuntimeException(ex) ; } 
+        }
+
+        static byte[] stringAsBytes(String x)
+        {
+            try {
+                ByteArrayOutputStream bout = new ByteArrayOutputStream() ;
+                try(Writer out = new OutputStreamWriter(bout, utf8)) {
+                    out.write(x) ;
+                }
+                byte[] bytes = bout.toByteArray() ;
+                return bytes ;
+            } catch (IOException ex) { throw new RuntimeException(ex) ; } 
+        }
+    }

Reply via email to