http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/BitsLong.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/BitsLong.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/BitsLong.java
new file mode 100644
index 0000000..e147033
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/BitsLong.java
@@ -0,0 +1,311 @@
+/*
+ * 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.lib;
+
+// NB shifting is "mod 64" -- <<64 is a no-op (not a clear).
+// http://mindprod.com/jgloss/masking.html
+
+/** Utilities for manipulating a bit pattern which are held in a 64 bit long
+ *  @see BitsInt
+ *  (java.util.BitSet does not allow getting the pattern as a long) 
+ */ 
+public final class BitsLong
+{
+    private BitsLong() {}
+
+    private static int LongLen = Long.SIZE ;
+    
+    /** Extract the value packed into bits start (inclusive) and finish 
(exclusive),
+     *  the value is returned the low part of the returned long.
+     *  The low bit is bit zero.
+     * @param bits
+     * @param start
+     * @param finish
+     * @return long
+     */ 
+    
+    public static final
+    long unpack(long bits, int start, int finish)
+    {
+        check(start, finish) ;
+        if ( finish == 0 ) return 0 ;
+        // Remove top bits by moving up.  Clear bottom bits by them moving 
down.
+        return (bits<<(LongLen-finish)) >>> ((LongLen-finish)+start) ;
+    }
+
+    /** Place the value into the bit pattern between start and finish
+     * and returns the new value.  Leaves other bits alone.
+     * @param bits
+     * @param value
+     * @param start
+     * @param finish
+     * @return long
+     */
+    public static final
+    long pack(long bits, long value, int start, int finish)
+    {
+        check(start, finish) ;
+        bits = clear$(bits, start, finish) ;
+        long mask = mask(start, finish) ;
+        bits = bits | ( (value<<start) & mask ) ;
+        return bits ;
+    }
+
+    /** Get bits from a hex string.
+     * 
+     * @param str
+     * @param startChar     Index of first character (counted from the left, 
string style). 
+     * @param finishChar    Index after the last character (counted from the 
left, string style).
+     * @return long
+     */
+    
+    public static final
+    long unpack(String str, int startChar, int finishChar)
+    {
+        String s = str.substring(startChar, finishChar) ;
+        return Long.parseLong(s, 16) ;
+    }
+
+    /** Set the bits specificied.
+     * 
+     * @param bits      Pattern
+     * @param bitIndex 
+     * @return          Modified pattern
+     */
+    public static final
+    long set(long bits, int bitIndex)
+    { 
+        check(bitIndex) ;
+        return set$(bits, bitIndex) ;
+    }
+
+    /** Set the bits from string (inc) to finish (exc) to one
+     * 
+     * @param bits      Pattern
+     * @param start     start  (inclusive)
+     * @param finish    finish (exclusive)
+     * @return          Modified pattern
+     */
+    public static final
+    long set(long bits, int start, int finish)
+    { 
+        check(start, finish) ;
+        return set$(bits, start, finish) ;
+    }
+
+    /** Test whether a bit is the same as isSet 
+     * @param bits      Pattern
+     * @param isSet     Test whether is set or not. 
+     * @param bitIndex  Bit index
+     * @return          Boolean
+     */
+    public static final
+    boolean test(long bits, boolean isSet, int bitIndex)
+    {
+        check(bitIndex) ;
+        return test$(bits, isSet, bitIndex) ;
+    }
+    
+    /** Test whether a bit is set 
+     * @param bits      Pattern
+     * @param bitIndex  Bit index
+     * @return          Boolean
+     */
+    public static final
+    boolean isSet(long bits, int bitIndex)
+    {
+        check(bitIndex) ;
+        return test$(bits, true, bitIndex) ;
+    }
+    
+    /** Test whether a range has a specific value or not   
+     * @param bits      Pattern
+     * @param value     Value to test for
+     * @param start     start  (inclusive)
+     * @param finish    finish (exclusive)
+     * @return          Boolean
+     */
+    public static final
+    boolean test(long bits, long value, int start, int finish)
+    {
+        check(start, finish) ;
+        return test$(bits, value, start, finish) ;
+    }
+    
+    /** Get the bits from start (inclusive) to finish (exclusive),
+     *  leaving them aligned in the long.  See also unpack, returns
+     *  the value found at that place.
+     *  @see #unpack(long, int, int)
+     *  @param bits
+     *  @param start
+     *  @param finish
+     *  @return lon     */
+    
+    public static final
+    long access(long bits, int start, int finish)
+    {
+        check(start, finish) ;
+        return access$(bits, start, finish) ; 
+    }
+    
+    /**
+     * Clear the bits specified.
+     *  @param bits
+     *  @param start
+     *  @param finish
+     *  @return long
+     */
+  public static final
+    long clear(long bits, int start, int finish)
+    {
+        check(start, finish) ;
+        return clear$(bits, start, finish) ;
+    }
+
+    /**
+     * Create a mask that has ones between bit positions start (inc) and 
finish (exc),
+     * and zeros elsewhere.
+     * @param start
+     * @param finish
+     * @return long
+     */
+    public static final
+    long mask(int start, int finish)
+    {
+        check(start, finish) ;
+        return mask$(start, finish) ;
+    }
+    
+    /**
+     * Create a mask that has zeros between bit positions start (inc) and 
finish (exc),
+     * and ones elsewhere
+     * @param start
+     * @param finish
+     * @return long
+     */
+    public static final
+    long maskZero(int start, int finish)
+    {
+        check(start, finish) ;
+        return maskZero$(start, finish) ;
+    }
+    
+    private static final
+    long clear$(long bits, int start, int finish)
+    {
+        long mask = maskZero$(start, finish) ;
+        bits = bits & mask ;
+        return bits ;
+    }
+
+    private static final
+    long set$(long bits, int bitIndex)
+    { 
+        long mask = mask$(bitIndex) ;
+        return bits | mask ;
+    }
+
+    private static final
+    long set$(long bits, int start, int finish)
+    { 
+        long mask = mask$(start, finish) ;
+        return bits | mask ;
+    }
+
+    private static
+    boolean test$(long bits, boolean isSet, int bitIndex)
+    {
+        return isSet == access$(bits, bitIndex) ;
+    }
+
+    private static
+    boolean test$(long bits, long value, int start, int finish)
+    {
+        long v = access$(bits, start, finish) ;
+        return v == value ;
+    }
+
+
+    
+    private static final
+    boolean access$(long bits, int bitIndex)
+    {
+        long mask = mask$(bitIndex) ;
+        return (bits & mask) != 0L ;
+    }
+    
+    private static final
+    long access$(long bits, int start, int finish)
+    {
+        // Two ways:
+//        long mask = mask$(start, finish) ;
+//        return bits & mask ;
+        
+        return ( (bits<<(LongLen-finish)) >>> (LongLen-finish+start) ) << 
start  ;
+    }
+    
+
+    private static final
+    long mask$(int bitIndex)
+    {
+        return 1L << bitIndex ;
+    }
+
+    private static final
+    long mask$(int start, int finish)
+    {
+    //        long mask = 0 ;
+    //        if ( finish == Long.SIZE )
+    //            // <<Long.SIZE is a no-op 
+    //            mask = -1 ;
+    //        else
+    //            mask = (1L<<finish)-1 ;
+        if ( finish == 0 )
+            // So start is zero and so the mask is zero.
+            return 0 ;
+
+        
+        long mask = -1 ;
+//        mask = mask << (LongLen-finish) >>> (LongLen-finish) ;      // Clear 
the top bits
+//        return mask >>> start << start ;                  // Clear the 
bottom bits
+        return mask << (LongLen-finish) >>> (LongLen-finish+start) << start ; 
+    }
+
+    private static final
+    long maskZero$(int start, int finish)
+    {
+
+        return ~mask$(start, finish) ;
+    }
+    
+    private static final
+    void check(long bitIndex)
+    {
+        if ( bitIndex < 0 || bitIndex >= LongLen ) throw new 
IllegalArgumentException("Illegal bit index: "+bitIndex) ;
+    }
+
+    private static final
+    void check(long start, long finish)
+    {
+        if ( start < 0 || start >= LongLen ) throw new 
IllegalArgumentException("Illegal start: "+start) ;
+        if ( finish < 0 || finish > LongLen ) throw new 
IllegalArgumentException("Illegal finish: "+finish) ;
+        if ( start > finish )  throw new IllegalArgumentException("Illegal 
range: ("+start+", "+finish+")") ;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/ByteBufferLib.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/lib/ByteBufferLib.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/ByteBufferLib.java
new file mode 100644
index 0000000..c619fed
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/ByteBufferLib.java
@@ -0,0 +1,211 @@
+/*
+ * 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.lib ;
+
+import static java.lang.System.arraycopy ;
+
+import java.io.PrintStream ;
+import java.nio.ByteBuffer ;
+import java.util.Arrays ;
+
+public class ByteBufferLib {
+    public static boolean allowArray = true ;
+
+    private ByteBufferLib() {}
+
+    public static void fill(ByteBuffer bb, byte v) {
+        fill(bb, bb.position(), bb.limit(), v) ;
+    }
+
+    public static void fill(ByteBuffer bb, int start, int finish, byte v) {
+        for ( int i = start ; i < finish ; i++ )
+            bb.put(i, v) ;
+    }
+
+    public static String details(ByteBuffer byteBuffer) {
+        // Like ByteBuffer.toString but without the class.
+        return "[pos=" + byteBuffer.position() + " lim=" + byteBuffer.limit() 
+ " cap=" + byteBuffer.capacity() + "]" ;
+    }
+
+    public static void print(ByteBuffer byteBuffer) {
+        print(System.out, byteBuffer) ;
+    }
+
+    public static void print(PrintStream out, ByteBuffer byteBuffer) {
+        byteBuffer = byteBuffer.duplicate() ;
+
+        out.printf("ByteBuffer[pos=%d lim=%d cap=%d]", byteBuffer.position(), 
byteBuffer.limit(), byteBuffer.capacity()) ;
+
+        // Print bytes.
+        int i = 0 ;
+        int maxBytes = 3 * 20 ;
+        for ( ; i < maxBytes && i < byteBuffer.limit() ; i++ ) {
+            if ( i % 20 == 0 )
+                out.println() ;
+            out.printf(" 0x%02X", byteBuffer.get(i)) ; // Does not move 
position
+        }
+        if ( i < byteBuffer.limit() ) {
+            if ( i % 24 == 0 )
+                out.println() ;
+            out.print(" ...") ;
+        }
+        // Print as 4-byte ints
+        // int maxSlots = 8 ;
+        // int i = 0 ;
+        // for ( ; i < maxSlots && 4*i < byteBuffer.limit() ; i++ )
+        // out.printf(" 0x%04X", byteBuffer.getInt(4*i)) ;
+        // if ( i < maxSlots )
+        // out.print(" ...") ;
+        out.println() ;
+    }
+
+    public static boolean sameValue(ByteBuffer bb1, ByteBuffer bb2) {
+        if ( bb1.capacity() != bb2.capacity() )
+            return false ;
+
+        for ( int i = 0 ; i < bb1.capacity() ; i++ )
+            if ( bb1.get(i) != bb2.get(i) )
+                return false ;
+        return true ;
+    }
+
+    /**
+     * Copy of a ByteBuffer - the contents are copied (unlike
+     * ByteBuffer.duplicate)
+     */
+    final public static ByteBuffer duplicate(ByteBuffer bb) {
+        ByteBuffer bb2 = ByteBuffer.allocate(bb.limit() - bb.position()) ;
+        int x = bb.position() ;
+        bb2.put(bb) ;
+        bb.position(x) ;
+
+        bb2.position(0) ;
+        bb2.limit(bb2.capacity()) ;
+        return bb2 ;
+    }
+
+    /** Copy from a byte buffer */
+    final public static byte[] bb2array(ByteBuffer bb, int start, int finish) {
+        byte[] b = new byte[finish - start] ;
+        bb2array(bb, start, finish, b) ;
+        return b ;
+    }
+
+    private static void bb2array(ByteBuffer bb, int start, int finish, byte[] 
b) {
+        for ( int j = 0, i = start ; i < finish ; i++ )
+            b[j] = bb.get(i) ;
+    }
+
+    // For non-array versions : beware of overlaps.
+    final public static void bbcopy(ByteBuffer bb, int src, int dst, int 
length, int slotLen) {
+        if ( src == dst )
+            return ;
+
+        if ( allowArray && bb.hasArray() ) {
+            acopyArray(bb, src, dst, length, slotLen) ;
+            return ;
+        }
+
+        if ( src < dst )
+            bbcopy1(bb, src, dst, length, slotLen) ;
+        else
+            bbcopy2(bb, src, dst, length, slotLen) ;
+    }
+
+    private final static void bbcopy1(ByteBuffer bb, int src, int dst, int 
length, int slotLen) {
+        int bDst = dst * slotLen ;
+        int bSrc = src * slotLen ;
+        int bLen = length * slotLen ;
+        // src < dst so top dst is not in the overlap : work backwards
+        for ( int i = bLen - 1 ; i >= 0 ; i-- )
+            bb.put(bDst + i, bb.get(bSrc + i)) ;
+    }
+
+    private final static void bbcopy2(ByteBuffer bb, int src, int dst, int 
length, int slotLen) {
+        int bDst = dst * slotLen ;
+        int bSrc = src * slotLen ;
+        int bLen = length * slotLen ;
+        // src > dst so dst[0] is not in the overlap
+        for ( int i = 0 ; i < bLen ; i++ )
+            bb.put(bDst + i, bb.get(bSrc + i)) ;
+    }
+
+    public final static void bbcopy(ByteBuffer bb1, int src, ByteBuffer bb2, 
int dst, int length, int slotLen) {
+        // Assume bb1 and bb2 are different and do not overlap.
+        if ( allowArray && bb1.hasArray() && bb2.hasArray() ) {
+            acopyArray(bb1, src, bb2, dst, length, slotLen) ;
+            return ;
+        }
+        // One or both does not have an array.
+
+        int bSrc = src * slotLen ;
+        int bDst = dst * slotLen ;
+        int bLen = length * slotLen ;
+
+        for ( int i = 0 ; i < bLen ; i++ )
+            bb2.put(bDst + i, bb1.get(bSrc + i)) ;
+    }
+
+    final public static void bbfill(ByteBuffer bb, int fromIdx, int toIdx, 
byte fillValue, int slotLen) {
+        if ( allowArray && bb.hasArray() ) {
+            afillArray(bb, fromIdx, toIdx, fillValue, slotLen) ;
+            return ;
+        }
+
+        int bStart = fromIdx * slotLen ;
+        int bFinish = toIdx * slotLen ;
+
+        for ( int i = bStart ; i < bFinish ; i++ )
+            bb.put(i, fillValue) ;
+    }
+
+    // To ArrayOps?
+
+    final private static void acopyArray(ByteBuffer bb, int src, int dst, int 
length, int slotLen) {
+        byte[] b = bb.array() ;
+
+        int offset = bb.arrayOffset() ;
+
+        int bSrc = src * slotLen ;
+        int bDst = dst * slotLen ;
+        int bLen = length * slotLen ;
+
+        arraycopy(b, offset + bSrc, b, offset + bDst, bLen) ;
+    }
+
+    final private static void acopyArray(ByteBuffer bb1, int src, ByteBuffer 
bb2, int dst, int length, int slotLen) {
+        byte[] b1 = bb1.array() ;
+        byte[] b2 = bb2.array() ;
+        int offset1 = bb1.arrayOffset() ;
+        int offset2 = bb2.arrayOffset() ;
+
+        int bSrc = src * slotLen ;
+        int bDst = dst * slotLen ;
+        int bLen = length * slotLen ;
+
+        arraycopy(b1, offset1 + bSrc, b2, offset2 + bDst, bLen) ;
+    }
+
+    final private static void afillArray(ByteBuffer bb, int fromIdx, int 
toIdx, byte fillValue, int slotLen) {
+        int offset = bb.arrayOffset() ;
+        int bStart = fromIdx * slotLen ;
+        int bFinish = toIdx * slotLen ;
+        Arrays.fill(bb.array(), bStart + offset, bFinish + offset, fillValue) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/Bytes.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Bytes.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/Bytes.java
new file mode 100644
index 0000000..789aeeb
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/Bytes.java
@@ -0,0 +1,357 @@
+/*
+ * 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.lib;
+
+import java.io.UnsupportedEncodingException ;
+import java.nio.ByteBuffer ;
+import java.nio.CharBuffer ;
+import java.nio.charset.CharsetDecoder ;
+import java.nio.charset.CharsetEncoder ;
+import java.nio.charset.CoderResult ;
+
+/** Byte-oriented operations.  Packing and unpacking integers
+ *  is in network order (Big endian - which is the preferred order in Java)
+ *  {@link "http://en.wikipedia.org/wiki/Endianness"}
+ */  
+
+public class Bytes
+{
+    private Bytes() {}
+    
+    /** Compare two byte arrays which may be of different lengths */ 
+    public static int compare(byte[] x1, byte[] x2)
+    {
+        int n = Math.min(x1.length, x2.length) ;
+        
+        for ( int i = 0 ; i < n ; i++ )
+        {
+            byte b1 = x1[i] ;
+            byte b2 = x2[i] ;
+            if ( b1 == b2 )
+                continue ;
+            // Treat as unsigned values in the bytes. 
+            return (b1&0xFF) - (b2&0xFF) ;  
+        }
+
+        return x1.length - x2.length ;
+    }
+
+    public static int compareByte(byte b1, byte b2)
+    {
+        return (b1&0xFF) - (b2&0xFF) ;  
+    }
+    
+    public static byte[] copyOf(byte[] bytes)
+    {
+        return copyOf(bytes, 0, bytes.length) ;
+    }
+    
+    public static byte[] copyOf(byte[] bytes, int start)
+    {
+        return copyOf(bytes, start, bytes.length-start) ;
+    }
+    
+    public static byte[] copyOf(byte[] bytes, int start, int length)
+    {
+        byte[] newByteArray = new byte[length] ;
+        System.arraycopy(bytes, start, newByteArray, 0, length) ;
+        return newByteArray ;
+    }
+    
+    final public static byte[] hexDigitsUC = {
+        '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' ,
+        '9' , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' };
+
+    final public static byte[] hexDigitsLC = {
+        '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' ,
+        '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' };
+
+    /** Get an int from a byte array (network order)
+     * @param b Byte Array
+     */
+    public static final int getInt(byte[]b)
+    { return getInt(b, 0) ; }
+    
+    /** Get an int from a byte array (network order)
+     * @param b Byte Array
+     * @param idx Starting point of bytes 
+     */
+    public static final int getInt(byte[]b, int idx)
+    {
+        return assembleInt(b[idx+0],
+                           b[idx+1],
+                           b[idx+2],
+                           b[idx+3]) ;
+    }
+
+    /** Get a long from a byte array (network order)
+     * @param b Byte Array
+     */
+    public static final long getLong(byte[]b)
+    { return getLong(b, 0) ; }
+    
+    /** Get a long from a byte array (network order)
+     * @param b Byte Array
+     * @param idx Starting point of bytes 
+     */
+    public static final long getLong(byte[]b, int idx)
+    {
+        return assembleLong(b[idx+0],
+                            b[idx+1],
+                            b[idx+2],
+                            b[idx+3],
+                            b[idx+4],
+                            b[idx+5],
+                            b[idx+6],
+                            b[idx+7]) ;
+
+    }
+
+    /** Put an int into a byte array
+     * @param value The integer
+     * @param b byte array 
+     */
+    public static final void setInt(int value, byte[]b)
+    { setInt(value, b, 0) ; }
+    
+    /** Put an int into a byte array from a given position
+     * @param x The integer
+     * @param b byte array 
+     * @param idx starting point
+     */
+    public static final void setInt(int x, byte[]b, int idx)
+    {
+//        b[idx+0] = byte3(value) ;
+//        b[idx+1] = byte2(value) ;
+//        b[idx+2] = byte1(value) ;
+//        b[idx+3] = byte0(value) ;
+      b[idx+0] = (byte)((x >> 24)&0xFF) ;
+      b[idx+1] = (byte)((x >> 16)&0xFF);
+      b[idx+2] = (byte)((x >>  8)&0xFF);
+      b[idx+3] = (byte)(x &0xFF);
+
+    }
+    
+    
+    /** Put a long into a byte array
+     * @param value The integer
+     * @param b byte array 
+     */
+    public static final void setLong(long value, byte[]b)
+    { setLong(value, b, 0) ; }
+    
+    /** Put a long into a byte array from a given position
+     * @param value The integer
+     * @param b byte array 
+     * @param idx starting point
+     */
+    public static final void setLong(long value, byte[] b, int idx) {
+        int lo = (int)(value & 0xFFFFFFFFL) ;
+        int hi = (int)(value >>> 32) ;
+        setInt(hi, b, idx) ;
+        setInt(lo, b, idx + 4) ;
+    }
+
+    /** int to byte array */
+    public static byte[] packInt(int val) {
+        byte[] valBytes = new byte[Integer.SIZE / Byte.SIZE] ;
+        setInt(val, valBytes, 0) ;
+        return valBytes ;
+    }
+
+    /** long to byte array */
+    public static byte[] packLong(long val) {
+        byte[] valBytes = new byte[Long.SIZE / Byte.SIZE] ;
+        setLong(val, valBytes, 0) ;
+        return valBytes ;
+    }
+
+    /** Make an int order of args -- high to low */
+    static private int assembleInt(byte b3, byte b2, byte b1, byte b0) {
+        return ( ((b3 & 0xFF) << 24) |
+                 ((b2 & 0xFF) << 16) |
+                 ((b1 & 0xFF) <<  8) |
+                 ((b0 & 0xFF) <<  0)
+            );
+    }
+
+    /** Make a long order of args -- high to low */
+    static private Long assembleLong(byte b7, byte b6, byte b5, byte b4, byte 
b3, byte b2, byte b1, byte b0)
+    {
+        
+        return  (((long)b7 & 0xFF) << 56) |
+                (((long)b6 & 0xFF) << 48) |
+                (((long)b5 & 0xFF) << 40) |
+                (((long)b4 & 0xFF) << 32) |
+                (((long)b3 & 0xFF) << 24) |
+                (((long)b2 & 0xFF) << 16) |
+                (((long)b1 & 0xFF) <<  8) |
+                (((long)b0 & 0xFF) <<  0) ;
+    }
+
+    private static byte byte3(int x) { return (byte)(x >> 24); }
+    private static byte byte2(int x) { return (byte)(x >> 16); }
+    private static byte byte1(int x) { return (byte)(x >>  8); }
+    private static byte byte0(int x) { return (byte)(x >>  0); }
+
+    /** Return the UTF-8 bytes for a string */
+    public static byte[] string2bytes(String x) {
+        try {
+            return x.getBytes("UTF-8") ;
+        }
+        catch (UnsupportedEncodingException ex) {
+            // Impossible.
+            ex.printStackTrace() ;
+            return null ;
+        }
+    }
+    
+    /** Return the string for some UTF-8 bytes */
+    public static String bytes2string(byte[] x) {
+        try {
+            return new String(x, "UTF-8") ;
+        }
+        catch (UnsupportedEncodingException ex) {
+            // Impossible-ish.
+            ex.printStackTrace() ;
+            return null ;
+        }
+    }
+
+    /** Encode a string into a ByteBuffer : on return position is the end of 
the encoding */
+    public static int toByteBuffer(CharSequence s, ByteBuffer bb) {
+        //BlockUTF8.fromChars(s, bb) ;
+        CharsetEncoder enc = Chars.allocEncoder();
+        int x = toByteBuffer(s, bb, enc) ;
+        Chars.deallocEncoder(enc) ;
+        return x ;
+    }
+    
+    /** Encode a string into a ByteBuffer : on return position is the end of 
the encoding */
+    public static int toByteBuffer(CharSequence s, ByteBuffer bb, 
CharsetEncoder enc) {
+        int start = bb.position() ;
+        CharBuffer cBuff = CharBuffer.wrap(s);
+        enc.reset();
+        CoderResult r = enc.encode(cBuff, bb, true) ;
+        if ( r.isOverflow() )
+            throw new InternalErrorException("Bytes.toByteBuffer: encode 
overflow (1)") ;
+        r = enc.flush(bb) ;
+        if ( r.isOverflow() )
+            throw new InternalErrorException("Bytes.toByteBuffer: encode 
overflow (2)") ;
+//        if ( r.isUnderflow() )
+//            throw new InternalErrorException("Bytes.toByteBuffer: encode 
underflow") ;
+        int finish = bb.position() ;
+        return finish-start ;
+    }
+    
+    /** Decode a string into a ByteBuffer */
+    public static String fromByteBuffer(ByteBuffer bb)
+    {
+        //return BlockUTF8.toString(bb) ;
+        // To be removed (Dec 2011)
+        CharsetDecoder dec = Chars.allocDecoder();
+        String x = fromByteBuffer(bb, dec) ;
+        Chars.deallocDecoder(dec) ;
+        return x ;
+    }
+    
+    /** Decode a string into a ByteBuffer */
+    public static String fromByteBuffer(ByteBuffer bb, CharsetDecoder dec) {
+        if ( bb.remaining() == 0 )
+            return "" ;
+        dec.reset() ;
+        CharBuffer cBuff = CharBuffer.allocate(bb.remaining()) ;
+        CoderResult r = dec.decode(bb, cBuff, true) ;
+        if ( r.isOverflow() )
+            throw new InternalErrorException("fromByteBuffer: decode overflow 
(1)") ;
+        r = dec.flush(cBuff) ;
+        if ( r.isOverflow() )
+            throw new InternalErrorException("fromByteBuffer: decode overflow 
(2)") ;
+        cBuff.flip() ;
+        return cBuff.toString() ;
+    }
+
+    /**
+     * Return a hex string representing the bytes, zero padded to length of 
byte
+     * array.
+     */
+    public static String asHex(byte[] bytes) {
+        return asHexUC(bytes) ;
+    }
+
+    public static String asHexUC(byte[] bytes) {
+        return asHex(bytes, 0, bytes.length, Chars.hexDigitsUC) ;
+    }
+
+    public static String asHexLC(byte[] bytes) {
+        return asHex(bytes, 0, bytes.length, Chars.hexDigitsLC) ;
+    }
+
+    public static String asHex(byte[] bytes, int start, int finish, char[] 
hexDigits) {
+        StringBuilder sw = new StringBuilder() ;
+        for ( int i = start ; i < finish ; i++ ) {
+            byte b = bytes[i] ;
+            int hi = (b & 0xF0) >> 4 ;
+            int lo = b & 0xF ;
+            // if ( i != start ) sw.append(' ') ;
+            sw.append(hexDigits[hi]) ;
+            sw.append(hexDigits[lo]) ;
+        }
+        return sw.toString() ;
+    }
+    
+    /** Return a hex string representing the bytes, zero padded to length of 
byte array. */
+    public static String asHex(byte b)
+    {
+        return asHexUC(b) ; 
+    }
+
+    public static String asHexUC(byte b)
+    {
+        return asHex(b, Chars.hexDigitsUC) ; 
+    }
+
+    public static String asHexLC(byte b)
+    {
+        return asHex(b, Chars.hexDigitsLC) ; 
+    }
+    
+    private static String asHex(byte b, char[] hexDigits)
+    {
+        int hi = (b & 0xF0) >> 4 ;
+        int lo = b & 0xF ;
+        char[] chars = new char[2] ;
+        chars[0] = hexDigits[hi] ;
+        chars[1] = hexDigits[lo] ;
+        return new String(chars) ;
+    }
+
+    
+    public static int hexCharToInt(char c)
+    {
+        if ( '0' <= c && c <= '9' )   
+            return c-'0' ;
+        else if ( 'A' <= c && c <= 'F' )
+            return c-'A'+10 ;
+        else if ( 'a' <= c && c <= 'f' )
+            return c-'a'+10 ;
+        else
+            throw new IllegalArgumentException("Bad index char : "+c) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/Cache.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Cache.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/Cache.java
new file mode 100644
index 0000000..d7fa711
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/Cache.java
@@ -0,0 +1,56 @@
+/*
+ * 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.lib;
+
+import java.util.Iterator ;
+import java.util.concurrent.Callable ;
+
+/** A cache */
+public interface Cache<Key, Value>
+{
+    
+    /** Does the cache contain the key? */
+    public boolean containsKey(Key key) ;
+    
+    /** Get from cache - or return null. */  
+    public Value getIfPresent(Key key) ;
+    
+    /** Get from cache, of not present, call the {@code callable}
+     *  to try to fill the cache.
+     */
+    public Value getOrFill(Key key, Callable<Value> callable) ;
+
+    /** Insert into the cache */
+    public void put(Key key, Value thing) ;
+
+    /** Remove from cache - return true if key referenced an entry */
+    public void remove(Key key) ;
+    
+    /** Iterate over all keys. Iteratering over the keys requires the caller 
be thread-safe. */ 
+    public Iterator<Key> keys() ;
+    
+    public boolean isEmpty() ;
+    public void clear() ;
+    
+    /** Current size of cache */
+    public long size() ;
+    
+    /** Register a callback - called when an object is dropped from the cache 
(optional operation) */ 
+    public void setDropHandler(ActionKeyValue<Key,Value> dropHandler) ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheFactory.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheFactory.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheFactory.java
new file mode 100644
index 0000000..e913a9d
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheFactory.java
@@ -0,0 +1,67 @@
+/*
+ * 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.lib ;
+
+import org.apache.jena.atlas.lib.cache.* ;
+
+public class CacheFactory {
+    /**
+     * Create a cache which has space for up to a certain number of objects.
+     * This is an LRU cache, or similar.
+     * The cache returns null for a cache miss.
+     * The cache is thread-safe for single operations.
+     */
+    public static <Key, Value> Cache<Key, Value> createCache(int maxSize) {
+        return new CacheGuava<>(maxSize) ;
+    }
+
+    /** Create a null cache */
+    public static <Key, Value> Cache<Key, Value> createNullCache() {
+        return new Cache0<>() ;
+    }
+
+    /** Create a lightweight cache (e.g. slot replacement) */
+    public static <Key, Value> Cache<Key, Value> createSimpleCache(int size) {
+        return new CacheSimple<>(size) ;
+    }
+
+    /** One slot cache */
+    public static <Key, Value> Cache<Key, Value> createOneSlotCache() {
+        return new Cache1<>() ;
+    }
+
+    /**
+     * Create set-cache, rather than a map-cache.
+     * The cache is thread-safe for single operations.
+     * 
+     * @see Pool
+     */
+    public static <Obj> CacheSet<Obj> createCacheSet(int size) {
+        Cache<Obj, Object> c = createCache(size) ;
+        return new CacheSetImpl<Obj>(c) ;
+    }
+
+    /** Add a synchronization wrapper to an existing set-cache */
+    public static <Obj> CacheSet<Obj> createSync(CacheSet<Obj> cache) {
+        if ( cache instanceof CacheSetSync<? > )
+            return cache ;
+        return new CacheSetSync<>(cache) ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheSet.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheSet.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheSet.java
new file mode 100644
index 0000000..f5c3d22
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheSet.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.lib;
+
+import org.apache.jena.atlas.iterator.Action ;
+
+public interface CacheSet<T>
+{
+    public void add(T e) ;
+    public void clear() ;
+    public boolean contains(T obj) ;
+    public boolean isEmpty() ;
+//    public Iterator<T> iterator() ;
+    public void remove(T obj) ;
+    public long size() ;
+    /** Register a callback - called when an object is dropped from the cache 
(optional operation) */ 
+    public void setDropHandler(Action<T> dropHandler) ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheStats.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheStats.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheStats.java
new file mode 100644
index 0000000..d71c1a0
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/CacheStats.java
@@ -0,0 +1,28 @@
+/*
+ * 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.lib;
+
+/** A cache with some readable statistics */ 
+public interface CacheStats<K,V> extends Cache<K,V>
+{
+    public long getCacheEntries() ;
+    public long getCacheHits() ;
+    public long getCacheMisses() ;
+    public long getCacheEjects() ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/Callback.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Callback.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/Callback.java
new file mode 100644
index 0000000..d2f04f9
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/Callback.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.atlas.lib;
+
+public interface Callback<T>
+{
+    public void proc(T arg) ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/Cell.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Cell.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/Cell.java
new file mode 100644
index 0000000..6910c0c
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/Cell.java
@@ -0,0 +1,99 @@
+/*
+ * 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.lib ;
+
+
+/** Key-value slot, with chaining for lookup. */  
+public class Cell<K,V>
+{
+    private final Cell<K,V> previous ;
+    private final K key ;
+    private final V value ;
+
+    /** Create a slot with no key, value or parent - can be used a slot chain 
root */  
+    public Cell()               { this(null, null, null); }
+
+    public Cell(K key, V value) { this(key, value, null); }
+
+    private Cell(K key, V value, Cell<K, V> previous)
+    {
+        this.key = key ;
+        this.value = value ;
+        this.previous = previous ;
+    }
+
+    public Cell<K,V> extend(K key, V value)
+    {
+        return new Cell<>(key, value, this) ;
+    }
+    
+    public final V find(K k)
+    {
+        // Java, tail recursion, lack thereof.
+        Cell<K,V> slot = this ;
+
+        while (slot != null)
+        {
+            // Defend against null keys (e.g. the root of a slot chain). 
+            if ( k.equals(slot.key) )
+                return slot.value ;
+//            if ( previous == null )
+//              return null ;
+            slot = slot.previous ;
+        }
+        return null ;
+    }
+    
+      /* As it should be ... */
+//    public final V find(K k)
+//    {
+//        if ( k.equals(key) )
+//            return value ;
+//        if ( previous == null )
+//            return null ;
+//        return previous.find(k) ;
+//    }
+
+    private static final String sep = ", " ;
+    private void str(int level, StringBuilder acc)
+    {
+        if ( key == null && value == null )
+            return ;
+
+        if ( level != 0 )
+            acc.append(sep) ;
+        acc.append("(") ;
+        acc.append(key.toString()) ;
+        acc.append("->") ;
+        acc.append(value.toString()) ;
+        acc.append(")") ;
+        if ( previous != null )
+            previous.str(level+1, acc) ;
+    }
+
+    @Override
+    public String toString()
+    { 
+        StringBuilder sb = new StringBuilder() ;
+        sb.append("{ ") ;
+        str(0, sb) ;
+        sb.append(" }") ;
+        return sb.toString() ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/Chars.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Chars.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/Chars.java
new file mode 100644
index 0000000..e9d4f55
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/Chars.java
@@ -0,0 +1,271 @@
+/*
+ * 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.lib;
+
+import java.nio.charset.Charset ;
+import java.nio.charset.CharsetDecoder ;
+import java.nio.charset.CharsetEncoder ;
+import java.nio.charset.CodingErrorAction ;
+
+public class Chars
+{
+    private Chars() {}
+    
+ // So also Bytes.hexDigits to get bytes.
+    final public static char[] digits10 = {
+        '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9'
+    } ;
+    
+    /** Hex digits : upper case **/ 
+    final public static char[] hexDigitsUC = {
+        '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' ,
+        '9' , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' } ;
+
+    /** Hex digits : lower case **/ 
+    final public static char[] hexDigitsLC = {
+        '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' ,
+        '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' } ;
+
+    
+//         , 'g' , 'h' ,
+//        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
+//        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
+//        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
+    
+    /** Java name for UTF-8 encoding */
+    private static final String encodingUTF8     = "utf-8" ;
+    /** Java name for ASCII encoding */
+    private static final String encodingASCII    = "ascii" ;
+    
+    public static final Charset charsetUTF8 = Charset.forName(encodingUTF8) ;
+    public static final Charset charsetASCII = Charset.forName(encodingASCII) ;
+    
+    // Pools for encoders/decoder.
+    // Better? use a ThreadLocal.
+    // Initial pool size. Any additional encoder/decoder are later
+    // placed in the pool - it's an infinite, reusing, growing pool.
+    
+    // Better?  If so, use these!
+    
+    private static final ThreadLocal<CharsetEncoder> threadCharsetEncoder =
+        new ThreadLocal<CharsetEncoder>() {
+        @Override protected CharsetEncoder initialValue() {
+            return createEncoder() ;
+        }
+    };
+
+    private static final ThreadLocal<CharsetDecoder> threadCharsetDecoder =
+        new ThreadLocal<CharsetDecoder>() {
+        @Override protected CharsetDecoder initialValue() {
+            return createDecoder() ;
+        }
+    };
+
+    /** Return a per-thread CharsetEncoder */ 
+    public static CharsetEncoder getThreadEncoder() { return 
threadCharsetEncoder.get() ; }
+
+    /** Return a per-thread CharsetDecoder */ 
+    public static CharsetDecoder getThreadDecoder() { return 
threadCharsetDecoder.get() ; }
+
+    private static final int PoolSize = 2 ;
+    private static Pool<CharsetEncoder> encoders = PoolSync.create(new 
PoolBase<CharsetEncoder>()) ;
+    private static Pool<CharsetDecoder> decoders = PoolSync.create(new 
PoolBase<CharsetDecoder>()) ;
+
+    static {
+        ThreadLocal<CharsetEncoder> t ;
+        
+        // Fill the pool.
+        for ( int i = 0 ; i < PoolSize ; i++ )
+        {
+            putEncoder(createEncoder()) ;
+            putDecoder(createDecoder()) ;
+        }
+    }
+    
+    /** Create a UTF-8 encoder */
+    public static CharsetEncoder createEncoder()    { return 
charsetUTF8.newEncoder() ; }
+    /** Create a UTF-8 decoder */
+    public static CharsetDecoder createDecoder()    { return 
charsetUTF8.newDecoder() ; }
+
+    /** Get a UTF-8 encoder from the pool (null if pool empty) */ 
+    public static CharsetEncoder getEncoder()       { return encoders.get() ; }
+    /** Add/return a UTF-8 encoder to the pool */
+    public static void putEncoder(CharsetEncoder encoder) { 
encoders.put(encoder) ; }
+
+    /** Get a UTF-8 decoder from the pool (null if pool empty) */ 
+    public static CharsetDecoder getDecoder()       { return decoders.get() ; }
+    /** Add/return a UTF-8 decoder to the pool */
+    public static void putDecoder(CharsetDecoder decoder) { 
decoders.put(decoder) ; }
+    
+    /** Allocate a CharsetEncoder, creating as necessary */
+    public static CharsetEncoder allocEncoder()
+    {
+        CharsetEncoder enc = Chars.getEncoder();
+        // Blocking finite Pool - does not happen.
+        // Plain Pool (sync wrapped) - might - allocate an extra one. 
+        if ( enc == null ) 
+            enc = Chars.createEncoder() ;
+        enc
+          .onMalformedInput(CodingErrorAction.REPLACE)
+          .onUnmappableCharacter(CodingErrorAction.REPLACE)
+          .reset() ;
+        
+        return enc ;
+    }
+    /** Deallocate a CharsetEncoder, may increase pool size */
+    public static void deallocEncoder(CharsetEncoder enc) { putEncoder(enc) ; }
+        
+    /** Allocate a CharsetDecoder, creating as necessary */
+    public static CharsetDecoder allocDecoder()
+    {
+        CharsetDecoder dec = Chars.getDecoder();
+        // Blocking finite Pool - does not happen.
+        // Plain Pool (sync wrapped) - might - allocate an extra one. 
+        if ( dec == null ) 
+            dec = Chars.createDecoder() ;
+        dec
+          .onMalformedInput(CodingErrorAction.REPLACE)
+          .onUnmappableCharacter(CodingErrorAction.REPLACE)
+          .reset() ;
+        
+        return dec ;
+    }
+    /** Deallocate a CharsetDecoder, may increase pool size */
+    public static void deallocDecoder(CharsetDecoder dec) { putDecoder(dec) ; }
+    
+    /** Is char in the array? */
+    public static boolean charInArray(int ch, char[] chars) {
+        for ( int xch : chars ) {
+            if ( ch == xch )
+                return true ;
+        }
+        return false ;
+    }
+
+    public static void encodeAsHex(StringBuilder buff, char marker, char ch)
+    {
+        if ( ch < 256 )
+        {
+            buff.append(marker) ;
+            int lo = ch & 0xF ;
+            int hi = (ch >> 4) & 0xF ;
+            buff.append(Chars.hexDigitsUC[hi]) ;                
+            buff.append(Chars.hexDigitsUC[lo]) ;
+            return ;
+        }
+        int n4 = ch & 0xF ;
+        int n3 = (ch >> 4) & 0xF ;
+        int n2 = (ch >> 8) & 0xF ;
+        int n1 = (ch >> 12) & 0xF ;
+        buff.append(marker) ;
+        buff.append(Chars.hexDigitsUC[n1]) ;                
+        buff.append(Chars.hexDigitsUC[n2]) ;
+        buff.append(marker) ;
+        buff.append(Chars.hexDigitsUC[n3]) ;
+        buff.append(Chars.hexDigitsUC[n4]) ;
+        
+    }
+
+    /** End of file - not a Unicode codepoint */
+    public static final int EOF             = -1 ;
+    // BOM  : U+FEFF encoded in bytes as xEF,0xBB,0xBF
+    public static final char BOM            = 0xFEFF ;  
+    
+    /** undefined character (exact meaning depends on use) - not a Unicode 
codepoint */
+    public static final int  UNSET           =  -2 ;
+    public static final char NL              = '\n' ;
+    public static final char CR              = '\r' ;
+    public static final char TAB             = '\t' ;
+    public static final char SPC             = ' ' ;
+    public static final char BSPACE          = '\b' ;
+    
+    public static final char CH_ZERO         =  (char)0 ;
+
+    public static final char CH_LBRACKET     = '[' ;
+    public static final char CH_RBRACKET     = ']' ;
+    
+    public static final char CH_LBRACE       = '{' ;
+    public static final char CH_RBRACE       = '}' ;
+
+    public static final char CH_LPAREN       = '(' ;
+    public static final char CH_RPAREN       = ')' ;
+
+    public static final char CH_LT           = '<' ;
+    public static final char CH_GT           = '>' ;
+    public static final char CH_UNDERSCORE   = '_' ;
+
+    public static final char CH_QUOTE1       = '\'' ;
+    public static final char CH_QUOTE2       = '"' ;
+
+    public static final char CH_EQUALS       = '=' ;
+    public static final char CH_STAR         = '*' ;
+    public static final char CH_DOT          = '.' ;
+    public static final char CH_COMMA        = ',' ;
+    public static final char CH_SEMICOLON    = ';' ;
+    public static final char CH_COLON        = ':' ;
+    public static final char CH_AMPHERSAND   = '&' ;
+    public static final char CH_AT           = '@' ;
+    public static final char CH_QMARK        = '?' ;
+    public static final char CH_HASH         = '#' ;
+    public static final char CH_PLUS         = '+' ;
+    public static final char CH_MINUS        = '-' ;
+    public static final char CH_DASH         = '-' ; // Alt name
+    public static final char CH_SLASH        = '/' ;
+    public static final char CH_RSLASH       = '\\' ;
+    public static final char CH_PERCENT      = '%' ;
+    
+    // Byte versions of the above
+    public static final byte B_NL            = NL ;
+    public static final byte B_CR            = CR ;
+    
+    public static final byte B_LBRACKET      = '[' ;
+    public static final byte B_RBRACKET      = ']' ;
+    
+    public static final byte B_LBRACE        = '{' ;
+    public static final byte B_RBRACE        = '}' ;
+
+    public static final byte B_LPAREN        = '(' ;
+    public static final byte B_RPAREN        = ')' ;
+
+    public static final byte B_LT            = '<' ;
+    public static final byte B_GT            = '>' ;
+    public static final byte B_UNDERSCORE    = '_' ;
+
+    public static final byte B_QUOTE1        = '\'' ;
+    public static final byte B_QUOTE2        = '"' ;
+
+    public static final byte B_EQUALS        = '=' ;
+    public static final byte B_STAR          = '*' ;
+    public static final byte B_DOT           = '.' ;
+    public static final byte B_COMMA         = ',' ;
+    public static final byte B_SEMICOLON     = ';' ;
+    public static final byte B_COLON         = ':' ;
+    public static final byte B_AT            = '@' ;
+    public static final byte B_AMPHERSAND    = '&' ;
+    public static final byte B_QMARK         = '?' ;
+    public static final byte B_HASH          = '#' ;
+    public static final byte B_PLUS          = '+' ;
+    public static final byte B_MINUS         = '-' ;
+    public static final byte B_DASH          = '-' ; // Alt name
+    public static final byte B_SLASH         = '/' ;
+    public static final byte B_RSLASH        = '\\' ;
+    public static final byte B_PERCENT       = '%' ;
+    
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/Closeable.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Closeable.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/Closeable.java
new file mode 100644
index 0000000..2b6002a
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/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.atlas.lib;
+
+/** 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/atlas/lib/CollectionUtils.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/lib/CollectionUtils.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/CollectionUtils.java
new file mode 100644
index 0000000..8bae6a4
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/CollectionUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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.lib;
+
+import java.util.Collection ;
+import java.util.Iterator ;
+
+public class CollectionUtils
+{
+    static public <T> void removeNulls(Collection<T> list)
+    {
+        for ( Iterator<T> iter = list.iterator() ; iter.hasNext() ; )
+        {
+            T e = iter.next() ;
+            if ( e == null )
+                iter.remove() ;
+        }
+    }
+    
+    static public <T> boolean disjoint(Collection<T> c1, Collection<T> c2)
+    {
+        if ( c2.size() < c1.size() )
+        {
+            Collection<T> t = c1 ;
+            c1 = c2 ;
+            c2 = t ;
+        }
+        
+        for ( T t : c1 )
+        {
+            if ( c2.contains(t) )
+                return false ;
+        }
+        return true ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/ColumnMap.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/ColumnMap.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/ColumnMap.java
new file mode 100644
index 0000000..18db563
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/ColumnMap.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.lib;
+
+import static java.lang.String.format ;
+
+import java.util.Arrays ;
+import java.util.List ;
+
+import org.apache.jena.atlas.AtlasException ;
+
+
+
+/** General descriptor of a reordering (mapping) of columns in tuples to 
columns in indexes, 
+ * for example, from triples to triple index order. 
+ *
+ * Naming: map is convert to the reordered form, fetch is get back.
+ */
+public class ColumnMap
+{
+    // Map from tuple order to index order
+    // So SPO->POS is (0->2, 1->0, 2->1)
+    // i.e. the location of the element after mapping.  
+    private int[] insertOrder ;            
+    
+    // The mapping from index to tuple order
+    // For POS->SPO, is (0->1, 1->2, 2->0)
+    // i.e. the location to fetch the mapped element from. 
+    private int[] fetchOrder ;
+
+    private String label ;
+
+    /** Construct a column mapping that maps the input (one col, one char) to 
the output */  
+    public ColumnMap(String input, String output)
+    {
+        this(input+"->"+output, compileMapping(input, output)) ;
+    }
+    
+    public <T> ColumnMap(String label, List<T> input, List<T> output)
+    {
+        this(label, compileMapping(input, output)) ;
+    }
+    
+    public <T> ColumnMap(String label, T[] input, T[] output)
+    {
+        this(label, compileMapping(input, output)) ;
+    }
+    
+    /** Construct a column map - the elements are the 
+     * mappings of a tuple originally in the order 0,1,2,...
+     * so SPO->POS is 2,0,1 (SPO->POS so S->2, P->0, O->1)   
+     * and not 1,2,0 (which is the extraction mapping).
+     * The label is just a lable and is not interpretted.
+     */
+    public ColumnMap(String label, int...elements)
+    {
+        this.label = label ;
+
+        this.insertOrder = new int[elements.length] ;
+        System.arraycopy(elements, 0, elements, 0, elements.length) ;
+        Arrays.fill(insertOrder, -1) ;
+        
+        this.fetchOrder = new int[elements.length] ;
+        Arrays.fill(fetchOrder, -1) ;
+    
+        for ( int i = 0 ; i < elements.length ; i++ )
+        {
+            int x = elements[i] ;
+            if ( x < 0 || x >= elements.length)
+                throw new IllegalArgumentException("Out of range: "+x) ;
+            // Checking
+            if ( insertOrder[i] != -1 || fetchOrder[x] != -1 )
+                throw new IllegalArgumentException("Inconsistent: 
"+ListUtils.str(elements)) ;
+            
+            insertOrder[i] = x ;
+            fetchOrder[x] = i ;
+        }
+    }
+    
+    /** Length of mapping */
+    
+    public int length() { return fetchOrder.length ; }
+    
+    /** Apply to an <em>unmapped</em> tuple to get the i'th slot after mapping 
: SPO->POS : 0'th slot is P from SPO */
+    public <T> T fetchSlot(int idx, Tuple<T> tuple)
+    { 
+        idx = fetchOrder[idx] ;     // Apply the reverse mapping as we are 
doing zero is P, so it's an unmap.
+        return tuple.get(idx) ;
+    }
+
+    /** Apply to an <em>unmapped</em> tuple to get the i'th slot after mapping 
: SPO->POS : 0'th slot is P from SPO */
+    public <T> T fetchSlot(int idx, T[] tuple)
+    { 
+        idx = fetchOrder[idx] ;     // Apply the reverse mapping as we are 
doing zero is P, so it's an unmap.
+        return tuple[idx] ;
+    }
+    
+    /** Apply to a <em>mapped</em> tuple to get the i'th slot as it appears 
after mapping : SPO->POS : 0'th slot is S from POS */
+    public <T> T mapSlot(int idx, Tuple<T> tuple)
+    { 
+        idx = insertOrder[idx] ;
+        return tuple.get(idx) ;
+    }
+    
+    /** Apply to a <em>mapped</em> tuple to get the i'th slot as it appears 
after mapping : SPO->POS : 0'th slot is S from POS */
+    public <T> T mapSlot(int idx, T[] tuple)
+    { 
+        idx = insertOrder[idx] ;        // Yes - it's the insert location we 
want to access 
+        return tuple[idx] ;
+    }
+    
+    /** Get the index of the i'th slot as it appears after mapping : SPO->POS 
: 0'th slot is S from POS so 2->0 */
+    public int mapSlotIdx(int idx)
+    { 
+        return insertOrder[idx] ;        // Yes - it's the insert location we 
want to access 
+    }
+
+    /** Get the index of the i'th slot as it appears from a mapping : for 
SPO->POS : 0'th slot is P so 1->0 */
+    public int fetchSlotIdx(int idx)
+    { 
+        return fetchOrder[idx] ;        // Yes - it's the insert location we 
want to access 
+    }
+
+    /** Apply to an <em>unmapped</em> tuple to get a tuple with the column 
mapping applied */
+    public <T> Tuple<T> map(Tuple<T> src)
+    {
+        return map(src, insertOrder) ;
+    }
+
+    /** Apply to a <em>mapped</em> tuple to get a tuple with the column 
mapping reverse-applied */
+    public <T> Tuple<T> unmap(Tuple<T> src)
+    {
+        return map(src, fetchOrder) ;
+    }
+
+    private <T> Tuple<T> map(Tuple<T> src, int[] map)
+    {
+        @SuppressWarnings("unchecked")
+        T[] elts = (T[])new Object[src.size()] ;
+        
+        for ( int i = 0 ; i < src.size() ; i++ )
+        {
+            int j = map[i] ;
+            elts[j] = src.get(i) ;
+        }
+        return Tuple.create(elts) ;
+    }
+    
+    /** Compile a mapping encoded as single charcaters e.g. "SPO", "POS" */
+    static int[] compileMapping(String domain, String range)
+    {
+        List<Character> input = StrUtils.toCharList(domain) ;
+        List<Character> output = StrUtils.toCharList(range) ;
+        return compileMapping(input, output) ;
+    }
+
+    /** Compile a mapping, encoded two list, the domain and range of the 
mapping function  */
+    static <T> int[] compileMapping(T[] domain, T[] range)
+    {
+        return compileMapping(Arrays.asList(domain), Arrays.asList(range)) ;
+    }
+    
+    /** Compile a mapping */
+    static <T> int[] compileMapping(List<T> domain, List<T>range)
+    {
+        if ( domain.size() != range.size() )
+            throw new AtlasException("Bad mapping: lengths not the same: 
"+domain+" -> "+range) ; 
+        
+        int[] cols = new int[domain.size()] ;
+        boolean[] mapped = new boolean[domain.size()] ;
+        //Arrays.fill(mapped, false) ;
+        
+        for ( int i = 0 ; i < domain.size() ; i++ )
+        {
+            T input = domain.get(i) ;
+            int j = range.indexOf(input) ;
+            if ( j < 0 )
+                throw new AtlasException("Bad mapping: missing mapping: 
"+domain+" -> "+range) ;
+            if ( mapped[j] )
+                throw new AtlasException("Bad mapping: duplicate: "+domain+" 
-> "+range) ;
+            cols[i] = j ;
+            mapped[j] = true ;
+        }
+        return cols ;
+    }
+    
+    @Override
+    public String toString()
+    {
+        //return label ; 
+        return format("%s:%s%s", label, mapStr(insertOrder), 
mapStr(fetchOrder)) ;
+    }
+
+    private Object mapStr(int[] map)
+    {
+        StringBuilder buff = new StringBuilder() ;
+        String sep = "{" ;
+        
+        for ( int i = 0 ; i < map.length ; i++ )
+        {
+            buff.append(sep) ;
+            sep = ", " ; 
+            buff.append(format("%d->%d", i, map[i])) ;
+        }
+        buff.append("}") ;
+        
+        return buff.toString() ;
+    }
+
+    public String getLabel()
+    {
+        return label ;
+    }
+    
+    /** Reorder the letters of a string by the same rules as this column map 
(forward, map direction)*/ 
+    public String mapName(String word)
+    {
+        return mapString(word, insertOrder) ;
+    }
+    
+    /** Reorder the letters of a string by the same rules as this column map 
(backward, fetch direction) */ 
+    public String unmapName(String word)
+    {
+        return mapString(word, fetchOrder) ;
+    }
+    
+    // Map is get from i and put to j
+    private String mapString(String src, int[] map)
+    {
+        char[] chars = new char[src.length()] ;
+        for ( int i = 0 ; i < src.length() ; i++ )
+        {
+            int j = map[i] ;
+            chars[j] = src.charAt(i) ;
+        }
+        return new String(chars) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/DS.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/DS.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/DS.java
new file mode 100644
index 0000000..f9c7095
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/DS.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.lib;
+
+
+import java.util.* ;
+
+public class DS
+{
+    private DS() {}
+ 
+    public static <X> Set<X> setOfNone()            { return 
Collections.emptySet()  ; }
+    public static <X> Set<X> setOfOne(X element)    { return 
Collections.singleton(element) ; }
+    public static <X> Set<X> set()                  { return new HashSet<>(); }
+    public static <X> Set<X> set(int initialSize)   { return new 
HashSet<>(initialSize); }
+    public static <X> Set<X> set(Set<X> other)      { return new 
HashSet<>(other); }
+
+    public static <K, V> Map<K,V> mapOfNone()       { return 
Collections.emptyMap() ; }
+    public static <K, V> Map<K,V> mapOfOne(K key, V value) { return 
Collections.singletonMap(key, value) ; }
+    public static <K, V> Map<K,V> map()             { return new HashMap<>(); }
+    public static <K, V> Map<K,V> map(int initialSize) { return new 
HashMap<>(initialSize); }
+    public static <K, V> Map<K,V> map(Map<K,V> other) { return new 
HashMap<>(other); }
+
+    public static <T> List<T> listOfNone()          { return 
Collections.emptyList() ; }
+    public static <T> List<T> listOfOne(T element)  { return 
Collections.singletonList(element) ; }
+    public static <T> List<T> list()                { return new 
ArrayList<>(); }
+    public static <T> List<T> list(int initialSize) { return new 
ArrayList<>(initialSize); }
+    public static <T> List<T> list(List<T> other)   { return new 
ArrayList<>(other); }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/DateTimeUtils.java
----------------------------------------------------------------------
diff --git 
a/jena-base/src/main/java/org/apache/jena/atlas/lib/DateTimeUtils.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/DateTimeUtils.java
new file mode 100644
index 0000000..b33a514
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/DateTimeUtils.java
@@ -0,0 +1,127 @@
+/**
+ * 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.lib;
+
+import java.util.Calendar ;
+import java.util.Date ;
+import java.util.GregorianCalendar ;
+
+import org.apache.commons.lang3.time.FastDateFormat ;
+
+public class DateTimeUtils {
+
+    // Include timezone (even xsd:dates have timezones; Calendars have 
timezones)
+    // NB in SimpleDateFormat != FastDateFormat
+    //  SimpleDateFormat does not format Calendars.
+    //  SimpleDateFormat has "X" for  ISO format tmezones (+00:00)  
+    //    FastDateFormat uses "ZZ" for this.
+    private static final FastDateFormat dateTimeFmt_display = 
FastDateFormat.getInstance("yyyy/MM/dd HH:mm:ss z") ;
+    private static final FastDateFormat dateFmt_yyyymmdd    = 
FastDateFormat.getInstance("yyyy-MM-ddZZ") ;
+    // For milliseconds == 0
+    private static final FastDateFormat dateTimeFmt_XSD_ms0     = 
FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ") ;
+    // For milliseconds != 0
+    private static final FastDateFormat dateTimeFmt_XSD_ms      = 
FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ") ;
+    // For milliseconds == 0
+    private static final FastDateFormat timeFmt_XSD_ms0         = 
FastDateFormat.getInstance("HH:mm:ssZZ") ;
+    // For milliseconds != 0
+    private static final FastDateFormat timeFmt_XSD_ms          = 
FastDateFormat.getInstance("HH:mm:ss.SSSZZ") ;
+
+    public static String nowAsXSDDateTimeString() {
+        return calendarToXSDDateTimeString(new GregorianCalendar()) ;
+    }
+
+    public static String todayAsXSDDateString() {
+        return calendarToXSDDateString(new GregorianCalendar()) ;
+    }
+
+    /** Return "now" as readable string (date in yyyy/MM/dd format) */
+    public static String nowAsString() {
+        return nowAsString(dateTimeFmt_display) ;
+    }
+
+    public static String nowAsString(String formatString) {
+        FastDateFormat df = FastDateFormat.getInstance(formatString) ;
+        return df.format(new Date()) ;
+    }
+
+    public static String nowAsString(FastDateFormat dateFormat) {
+        return dateFormat.format(new Date()) ;
+    }
+
+    private static boolean hasZeroMilliSeconds(Calendar cal) {
+        return ! cal.isSet(Calendar.MILLISECOND) || 
cal.get(Calendar.MILLISECOND) == 0 ;
+    }
+
+    // Canonical fom : if ms == 0, don't include in the string.
+    public static String calendarToXSDDateTimeString(Calendar cal) {
+        FastDateFormat fmt = hasZeroMilliSeconds(cal) 
+            ? dateTimeFmt_XSD_ms0 
+            : dateTimeFmt_XSD_ms ;
+        return calendarToXSDString(cal, fmt) ;
+    }
+
+    public static String calendarToXSDDateString(Calendar cal) {
+        return calendarToXSDString(cal, dateFmt_yyyymmdd) ;
+    }
+
+    // Canonical fom : if ms == 0, don't include in the string.
+    public static String calendarToXSDTimeString(Calendar cal) {
+        FastDateFormat fmt = hasZeroMilliSeconds(cal) 
+            ? timeFmt_XSD_ms0 
+            : timeFmt_XSD_ms ;
+        return calendarToXSDString(cal, fmt) ;
+    }
+
+    private static String calendarToXSDString(Calendar cal, FastDateFormat 
fmt) {
+        String lex = fmt.format(cal) ;
+        // lex = lex + calcTimezone(cal) ;
+        return lex ;
+    }
+
+    // Not needed because of FastDateFormat
+//    private static String calcTimezone(Calendar cal) {
+//        Date date = cal.getTime() ;
+//        TimeZone z = cal.getTimeZone() ;
+//        int tz = z.getRawOffset() ;
+//
+//        if ( z.inDaylightTime(date) ) {
+//            int tzDst = z.getDSTSavings() ;
+//            tz = tz + tzDst ;
+//        }
+//
+//        String sign = "+" ;
+//        if ( tz < 0 ) {
+//            sign = "-" ;
+//            tz = -tz ;
+//        }
+//
+//        int tzH = tz / (60 * 60 * 1000) ; // Integer divide towards zero.
+//        int tzM = (tz - tzH * 60 * 60 * 1000) / (60 * 1000) ;
+//
+//        String tzH_str = Integer.toString(tzH) ;
+//        String tzM_str = Integer.toString(tzM) ;
+//
+//        if ( tzH < 10 )
+//            tzH_str = "0" + tzH_str ;
+//        if ( tzM < 10 )
+//            tzM_str = "0" + tzM_str ;
+//        return sign + tzH_str + ":" + tzM_str ;
+//    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/lib/EscapeStr.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/EscapeStr.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/EscapeStr.java
new file mode 100644
index 0000000..cd387f6
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/EscapeStr.java
@@ -0,0 +1,235 @@
+/*
+ * 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.lib;
+
+import org.apache.jena.atlas.AtlasException ;
+import org.apache.jena.atlas.io.AWriter ;
+import org.apache.jena.atlas.io.IndentedLineBuffer ;
+import org.apache.jena.atlas.io.OutputUtils ;
+
+public class EscapeStr
+{
+    // Tests: TestOutput
+    // See also OutputLangUtils.outputEsc.
+//    private final boolean ascii ;
+//
+//    public EscapeStr(CharSpace charSpace) { this.ascii = ( charSpace == 
CharSpace.ASCII ) ; } 
+//
+//    public void writeURI(AWriter w, String s)
+//    {
+//        if ( ascii )
+//            stringEsc(w, s, true, ascii) ;
+//        else
+//            // It's a URI - assume legal.
+//            w.print(s) ;
+//    }
+//
+//    public void writeStr(AWriter w, String s) 
+//    {
+//        stringEsc(w, s, true, ascii) ;
+//    }
+//
+//    public void writeStrMultiLine(AWriter w, String s) 
+//    {
+//        // N-Triples does not have """
+//        stringEsc(w, s, false, ascii) ;
+//    }
+//
+    // Utility
+    /*
+     * Escape characters in a string according to Turtle rules. 
+     */
+    public static String stringEsc(String s)
+    { return stringEsc(s, true, false) ; }
+
+    private static String stringEsc(String s, boolean singleLineString, 
boolean asciiOnly)
+    {
+        IndentedLineBuffer sb = new IndentedLineBuffer() ;
+        stringEsc(sb, s, singleLineString, asciiOnly) ;
+        return sb.toString() ;
+    }
+
+    public static void stringEsc(AWriter out, String s, boolean 
singleLineString, boolean asciiOnly)
+    {
+        int len = s.length() ;
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt(i);
+
+            // \\ Escape always possible.
+            if (c == '\\') 
+            {
+                out.print('\\') ;
+                out.print(c) ;
+                continue ;
+            }
+            if ( singleLineString )
+            {
+                if ( c == '"' )         { out.print("\\\""); continue ; }
+                else if (c == '\n')     { out.print("\\n");  continue ; }
+                else if (c == '\t')     { out.print("\\t");  continue ; }
+                else if (c == '\r')     { out.print("\\r");  continue ; }
+                else if (c == '\f')     { out.print("\\f");  continue ; }
+            }
+            // Not \-style esacpe. 
+            if ( c >= 32 && c < 127 )
+                out.print(c);
+            else if ( !asciiOnly )
+                out.print(c);
+            else
+            {
+                // Outside the charset range.
+                // Does not cover beyond 16 bits codepoints directly
+                // (i.e. \U escapes) but Java keeps these as surrogate
+                // pairs and will print as characters
+                out.print( "\\u") ;
+                OutputUtils.printHex(out, c, 4) ;
+            }
+        }
+    }
+
+    // Utilities to remove escapes
+
+    /** Replace \ escapes (\\u, \t, \n etc) in a string */
+    public static String unescapeStr(String s)
+    { return unescapeStr(s, '\\') ; }
+    
+    /** Replace \ escapes (\\u, \t, \n etc) in a string */
+    public static String unescapeStr(String s, char escapeChar)
+    { return unescape(s, escapeChar, false) ; }
+
+    // Main worker function for unescaping strings.
+    public static String unescape(String s, char escape, boolean 
pointCodeOnly) {
+        int i = s.indexOf(escape) ;
+        
+        if ( i == -1 )
+            return s ;
+        
+        // Dump the initial part straight into the string buffer
+        StringBuilder sb = new StringBuilder(s.substring(0,i)) ;
+        
+        for ( ; i < s.length() ; i++ )
+        {
+            char ch = s.charAt(i) ;
+
+            if ( ch != escape )
+            {
+                sb.append(ch) ;
+                continue ;
+            }
+                
+            // Escape
+            if ( i >= s.length()-1 )
+                throw new AtlasException("Illegal escape at end of string") ;
+            char ch2 = s.charAt(i+1) ;
+            i = i + 1 ;
+            
+            // \\u and \\U
+            if ( ch2 == 'u' )
+            {
+                // i points to the \ so i+6 is next character
+                if ( i+4 >= s.length() )
+                    throw new AtlasException("\\u escape too short") ;
+                int x = hex(s, i+1, 4) ;
+                sb.append((char)x) ;
+                // Jump 1 2 3 4 -- already skipped \ and u
+                i = i+4 ;
+                continue ;
+            }
+            if ( ch2 == 'U' )
+            {
+                // i points to the \ so i+6 is next character
+                if ( i+8 >= s.length() )
+                    throw new AtlasException("\\U escape too short") ;
+                int x = hex(s, i+1, 8) ;
+                // Convert to UTF-16 codepoint pair.
+                sb.append((char)x) ;
+                // Jump 1 2 3 4 5 6 7 8 -- already skipped \ and u
+                i = i+8 ;
+                continue ;
+            }
+            
+            // Are we doing just point code escapes?
+            // If so, \X-anything else is legal as a literal "\" and "X" 
+            
+            if ( pointCodeOnly )
+            {
+                sb.append('\\') ;
+                sb.append(ch2) ;
+                i = i + 1 ;
+                continue ;
+            }
+            
+            // Not just codepoints.  Must be a legal escape.
+            char ch3 = 0 ;
+            switch (ch2)
+            {
+                case 'n': ch3 = '\n' ;  break ; 
+                case 't': ch3 = '\t' ;  break ;
+                case 'r': ch3 = '\r' ;  break ;
+                case 'b': ch3 = '\b' ;  break ;
+                case 'f': ch3 = '\f' ;  break ;
+                case '\'': ch3 = '\'' ; break ;
+                case '\"': ch3 = '\"' ; break ;
+                case '\\': ch3 = '\\' ; break ;
+                default:
+                    throw new AtlasException("Unknown escape: \\"+ch2) ;
+            }
+            sb.append(ch3) ;
+        }
+        return sb.toString() ;
+    }
+    
+    public static int hex(String s, int i, int len)
+    {
+//        if ( i+len >= s.length() )
+//        {
+//            
+//        }
+        int x = 0 ;
+        for ( int j = i ; j < i+len ; j++ )
+        {
+           char ch = s.charAt(j) ;
+           int k = 0  ;
+           switch (ch)
+           {
+               case '0': k = 0 ; break ; 
+               case '1': k = 1 ; break ;
+               case '2': k = 2 ; break ;
+               case '3': k = 3 ; break ;
+               case '4': k = 4 ; break ;
+               case '5': k = 5 ; break ;
+               case '6': k = 6 ; break ;
+               case '7': k = 7 ; break ;
+               case '8': k = 8 ; break ;
+               case '9': k = 9 ; break ;
+               case 'A': case 'a': k = 10 ; break ;
+               case 'B': case 'b': k = 11 ; break ;
+               case 'C': case 'c': k = 12 ; break ;
+               case 'D': case 'd': k = 13 ; break ;
+               case 'E': case 'e': k = 14 ; break ;
+               case 'F': case 'f': k = 15 ; break ;
+               default:
+                   throw new AtlasException("Illegal hex escape: "+ch) ;
+           }
+           x = (x<<4)+k ;
+        }
+        return x ;
+    }
+
+}

Reply via email to