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 ; + } + +}
