Import of lib, io, logging and iterator packages
Project: http://git-wip-us.apache.org/repos/asf/jena/repo Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/1320f8db Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/1320f8db Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/1320f8db Branch: refs/heads/master Commit: 1320f8dbe2ed08f10fda6e48aac9177557b1ed03 Parents: 692118f Author: Andy Seaborne <[email protected]> Authored: Tue Apr 28 22:03:14 2015 +0100 Committer: Andy Seaborne <[email protected]> Committed: Tue Apr 28 22:03:14 2015 +0100 ---------------------------------------------------------------------- jena-base/pom.xml | 2 +- .../org/apache/jena/atlas/AtlasException.java | 27 + .../apache/jena/atlas/RuntimeIOException.java | 34 + .../java/org/apache/jena/atlas/io/AWriter.java | 43 + .../org/apache/jena/atlas/io/AWriterBase.java | 33 + .../org/apache/jena/atlas/io/BlockUTF8.java | 281 ++++++ .../apache/jena/atlas/io/BufferingWriter.java | 180 ++++ .../org/apache/jena/atlas/io/CharStream.java | 31 + .../apache/jena/atlas/io/CharStreamBasic.java | 58 ++ .../jena/atlas/io/CharStreamBuffered.java | 166 ++++ .../apache/jena/atlas/io/CharStreamReader.java | 56 ++ .../jena/atlas/io/CharStreamSequence.java | 47 + .../main/java/org/apache/jena/atlas/io/IO.java | 325 +++++++ .../org/apache/jena/atlas/io/InStreamASCII.java | 89 ++ .../org/apache/jena/atlas/io/InStreamUTF8.java | 254 +++++ .../jena/atlas/io/IndentedLineBuffer.java | 50 + .../apache/jena/atlas/io/IndentedWriter.java | 366 +++++++ .../jena/atlas/io/InputStreamBuffered.java | 104 ++ .../apache/jena/atlas/io/NullOutputStream.java | 40 + .../org/apache/jena/atlas/io/OutStreamUTF8.java | 179 ++++ .../org/apache/jena/atlas/io/OutputUtils.java | 77 ++ .../apache/jena/atlas/io/PeekInputStream.java | 233 +++++ .../org/apache/jena/atlas/io/PeekReader.java | 270 ++++++ .../org/apache/jena/atlas/io/PrintUtils.java | 31 + .../org/apache/jena/atlas/io/Printable.java | 24 + .../org/apache/jena/atlas/io/PrintableBase.java | 26 + .../org/apache/jena/atlas/io/StringWriterI.java | 30 + .../java/org/apache/jena/atlas/io/Writer2.java | 97 ++ .../apache/jena/atlas/iterator/AccString.java | 66 ++ .../apache/jena/atlas/iterator/Accumulate.java | 27 + .../org/apache/jena/atlas/iterator/Action.java | 24 + .../apache/jena/atlas/iterator/ActionCount.java | 31 + .../jena/atlas/iterator/ActionNothing.java | 26 + .../org/apache/jena/atlas/iterator/Filter.java | 22 + .../atlas/iterator/FilterDistinctAdjacent.java | 40 + .../jena/atlas/iterator/FilterOutNulls.java | 32 + .../apache/jena/atlas/iterator/FilterStack.java | 67 ++ .../jena/atlas/iterator/FilterUnique.java | 39 + .../org/apache/jena/atlas/iterator/Iter.java | 971 +++++++++++++++++++ .../jena/atlas/iterator/IteratorArray.java | 92 ++ .../atlas/iterator/IteratorBlockingQueue.java | 73 ++ .../jena/atlas/iterator/IteratorConcat.java | 104 ++ .../jena/atlas/iterator/IteratorCons.java | 122 +++ .../iterator/IteratorDelayedInitialization.java | 78 ++ .../jena/atlas/iterator/IteratorInteger.java | 61 ++ .../atlas/iterator/IteratorResourceClosing.java | 107 ++ .../jena/atlas/iterator/IteratorSlotted.java | 117 +++ .../jena/atlas/iterator/IteratorTruncate.java | 79 ++ .../jena/atlas/iterator/IteratorWithBuffer.java | 132 +++ .../atlas/iterator/IteratorWithHistory.java | 109 +++ .../jena/atlas/iterator/IteratorWrapper.java | 41 + .../apache/jena/atlas/iterator/MapUtils.java | 36 + .../jena/atlas/iterator/NullIterator.java | 49 + .../jena/atlas/iterator/PeekIterator.java | 100 ++ .../jena/atlas/iterator/PushbackIterator.java | 62 ++ .../atlas/iterator/RepeatApplyIterator.java | 86 ++ .../jena/atlas/iterator/SingletonIterator.java | 51 + .../apache/jena/atlas/iterator/Transform.java | 21 + .../jena/atlas/iterator/WrapperIterator.java | 45 + .../apache/jena/atlas/lib/ActionKeyValue.java | 24 + .../org/apache/jena/atlas/lib/AlarmClock.java | 69 ++ .../java/org/apache/jena/atlas/lib/Alg.java | 239 +++++ .../org/apache/jena/atlas/lib/Allocator.java | 28 + .../org/apache/jena/atlas/lib/ArrayUtils.java | 64 ++ .../java/org/apache/jena/atlas/lib/BitsInt.java | 309 ++++++ .../org/apache/jena/atlas/lib/BitsLong.java | 311 ++++++ .../apache/jena/atlas/lib/ByteBufferLib.java | 211 ++++ .../java/org/apache/jena/atlas/lib/Bytes.java | 357 +++++++ .../java/org/apache/jena/atlas/lib/Cache.java | 56 ++ .../org/apache/jena/atlas/lib/CacheFactory.java | 67 ++ .../org/apache/jena/atlas/lib/CacheSet.java | 34 + .../org/apache/jena/atlas/lib/CacheStats.java | 28 + .../org/apache/jena/atlas/lib/Callback.java | 24 + .../java/org/apache/jena/atlas/lib/Cell.java | 99 ++ .../java/org/apache/jena/atlas/lib/Chars.java | 271 ++++++ .../org/apache/jena/atlas/lib/Closeable.java | 27 + .../apache/jena/atlas/lib/CollectionUtils.java | 52 + .../org/apache/jena/atlas/lib/ColumnMap.java | 254 +++++ .../main/java/org/apache/jena/atlas/lib/DS.java | 45 + .../apache/jena/atlas/lib/DateTimeUtils.java | 127 +++ .../org/apache/jena/atlas/lib/EscapeStr.java | 235 +++++ .../java/org/apache/jena/atlas/lib/FileOps.java | 239 +++++ .../java/org/apache/jena/atlas/lib/Hex.java | 97 ++ .../java/org/apache/jena/atlas/lib/IRILib.java | 243 +++++ .../jena/atlas/lib/InternalErrorException.java | 27 + .../java/org/apache/jena/atlas/lib/Lib.java | 126 +++ .../org/apache/jena/atlas/lib/ListUtils.java | 150 +++ .../java/org/apache/jena/atlas/lib/Map2.java | 104 ++ .../org/apache/jena/atlas/lib/MapUtils.java | 45 + .../org/apache/jena/atlas/lib/MultiMap.java | 146 +++ .../apache/jena/atlas/lib/MultiMapToList.java | 43 + .../apache/jena/atlas/lib/MultiMapToSet.java | 47 + .../org/apache/jena/atlas/lib/MultiSet.java | 218 +++++ .../apache/jena/atlas/lib/NotImplemented.java | 27 + .../org/apache/jena/atlas/lib/NumberUtils.java | 133 +++ .../java/org/apache/jena/atlas/lib/Pair.java | 62 ++ .../java/org/apache/jena/atlas/lib/Pool.java | 30 + .../org/apache/jena/atlas/lib/PoolBase.java | 57 ++ .../org/apache/jena/atlas/lib/PoolSync.java | 53 + .../java/org/apache/jena/atlas/lib/Problem.java | 25 + .../apache/jena/atlas/lib/PropertiesSorted.java | 87 ++ .../apache/jena/atlas/lib/PropertyUtils.java | 143 +++ .../org/apache/jena/atlas/lib/RandomLib.java | 41 + .../java/org/apache/jena/atlas/lib/Ref.java | 54 ++ .../java/org/apache/jena/atlas/lib/RefLong.java | 38 + .../org/apache/jena/atlas/lib/Registry.java | 39 + .../jena/atlas/lib/ReverseComparator.java | 38 + .../org/apache/jena/atlas/lib/SetUtils.java | 84 ++ .../java/org/apache/jena/atlas/lib/Sink.java | 29 + .../org/apache/jena/atlas/lib/SinkCounting.java | 41 + .../org/apache/jena/atlas/lib/SinkLogging.java | 41 + .../org/apache/jena/atlas/lib/SinkNull.java | 33 + .../org/apache/jena/atlas/lib/SinkPrint.java | 45 + .../org/apache/jena/atlas/lib/SinkSplit.java | 55 ++ .../apache/jena/atlas/lib/SinkToCollection.java | 38 + .../org/apache/jena/atlas/lib/SinkToQueue.java | 52 + .../org/apache/jena/atlas/lib/SinkWrapper.java | 44 + .../org/apache/jena/atlas/lib/StrUtils.java | 292 ++++++ .../java/org/apache/jena/atlas/lib/Sync.java | 24 + .../org/apache/jena/atlas/lib/SystemUtils.java | 57 ++ .../java/org/apache/jena/atlas/lib/Timer.java | 72 ++ .../java/org/apache/jena/atlas/lib/Trie.java | 407 ++++++++ .../java/org/apache/jena/atlas/lib/Tuple.java | 153 +++ .../org/apache/jena/atlas/lib/TupleBuilder.java | 50 + .../java/org/apache/jena/atlas/lib/XMLLib.java | 62 ++ .../org/apache/jena/atlas/lib/cache/Cache0.java | 72 ++ .../org/apache/jena/atlas/lib/cache/Cache1.java | 127 +++ .../apache/jena/atlas/lib/cache/CacheGuava.java | 119 +++ .../apache/jena/atlas/lib/cache/CacheOps.java | 50 + .../jena/atlas/lib/cache/CacheSetImpl.java | 93 ++ .../jena/atlas/lib/cache/CacheSetSync.java | 65 ++ .../jena/atlas/lib/cache/CacheSetWrapper.java | 65 ++ .../jena/atlas/lib/cache/CacheSimple.java | 161 +++ .../jena/atlas/lib/cache/CacheWrapper.java | 65 ++ .../org/apache/jena/atlas/lib/cache/Getter.java | 25 + .../org/apache/jena/atlas/logging/FmtLog.java | 162 ++++ .../java/org/apache/jena/atlas/logging/Log.java | 116 +++ .../org/apache/jena/atlas/logging/LogCtl.java | 282 ++++++ .../jena/atlas/logging/ProgressLogger.java | 133 +++ .../logging/java/ConsoleHandlerStdout.java | 75 ++ .../jena/atlas/logging/java/TextFormatter.java | 53 + .../java/org/apache/jena/base/Closeable.java | 27 + .../src/main/java/org/apache/jena/base/Sys.java | 27 + .../java/org/apache/jena/atlas/TC_Atlas.java | 41 + .../atlas/io/AbstractTestPeekInputStream.java | 243 +++++ .../jena/atlas/io/AbstractTestPeekReader.java | 243 +++++ .../java/org/apache/jena/atlas/io/TS_IO.java | 45 + .../org/apache/jena/atlas/io/TestBlockUTF8.java | 254 +++++ .../jena/atlas/io/TestBufferingWriter.java | 97 ++ .../jena/atlas/io/TestIndentedWriter.java | 58 ++ .../jena/atlas/io/TestInputStreamBuffered.java | 109 +++ .../atlas/io/TestPeekInputStreamSource.java | 44 + .../atlas/io/TestPeekReaderCharSequence.java | 32 + .../jena/atlas/io/TestPeekReaderSource.java | 34 + .../apache/jena/atlas/io/TestPrintUtils.java | 59 ++ .../apache/jena/atlas/io/TestStreamUTF8.java | 118 +++ .../apache/jena/atlas/iterator/TS_Iterator.java | 38 + .../apache/jena/atlas/iterator/TestIter.java | 539 ++++++++++ .../jena/atlas/iterator/TestIteratorArray.java | 104 ++ .../jena/atlas/iterator/TestIteratorPeek.java | 107 ++ .../atlas/iterator/TestIteratorPushback.java | 93 ++ .../atlas/iterator/TestIteratorSlotted.java | 136 +++ .../atlas/iterator/TestIteratorWithBuffer.java | 97 ++ .../atlas/iterator/TestIteratorWithHistory.java | 87 ++ .../org/apache/jena/atlas/junit/BaseTest.java | 45 + .../java/org/apache/jena/atlas/lib/TS_Lib.java | 57 ++ .../apache/jena/atlas/lib/TestAlarmClock.java | 92 ++ .../java/org/apache/jena/atlas/lib/TestAlg.java | 226 +++++ .../org/apache/jena/atlas/lib/TestBitsInt.java | 468 +++++++++ .../org/apache/jena/atlas/lib/TestBitsLong.java | 468 +++++++++ .../org/apache/jena/atlas/lib/TestBytes.java | 198 ++++ .../org/apache/jena/atlas/lib/TestCache.java | 149 +++ .../org/apache/jena/atlas/lib/TestCache2.java | 88 ++ .../apache/jena/atlas/lib/TestColumnMap.java | 107 ++ .../jena/atlas/lib/TestDateTimeUtils.java | 82 ++ .../org/apache/jena/atlas/lib/TestFileOps.java | 78 ++ .../jena/atlas/lib/TestFilenameProcessing.java | 136 +++ .../java/org/apache/jena/atlas/lib/TestHex.java | 81 ++ .../apache/jena/atlas/lib/TestListUtils.java | 79 ++ .../org/apache/jena/atlas/lib/TestMultiSet.java | 143 +++ .../apache/jena/atlas/lib/TestNumberUtils.java | 106 ++ .../org/apache/jena/atlas/lib/TestRefLong.java | 70 ++ .../jena/atlas/lib/TestReverseComparator.java | 91 ++ .../org/apache/jena/atlas/lib/TestSetUtils.java | 146 +++ .../org/apache/jena/atlas/lib/TestStrUtils.java | 59 ++ .../org/apache/jena/atlas/lib/TestTrie.java | 346 +++++++ .../org/apache/jena/atlas/lib/TestXMLLib.java | 47 + 187 files changed, 20541 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/pom.xml ---------------------------------------------------------------------- diff --git a/jena-base/pom.xml b/jena-base/pom.xml index a8c05e5..0acf545 100644 --- a/jena-base/pom.xml +++ b/jena-base/pom.xml @@ -74,7 +74,7 @@ <artifactId>maven-surefire-plugin</artifactId> <configuration> <includes> - <include>org/apache/jena/iri/test/TestPackage.java</include> + <include>**/TS_*.java</include> </includes> </configuration> </plugin> http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/AtlasException.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/AtlasException.java b/jena-base/src/main/java/org/apache/jena/atlas/AtlasException.java new file mode 100644 index 0000000..2754060 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/AtlasException.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; + +public class AtlasException extends RuntimeException +{ + public AtlasException() { super() ; } + public AtlasException(String msg) { super(msg) ; } + public AtlasException(Throwable th) { super(th) ; } + public AtlasException(String msg, Throwable th) { super(msg, th) ; } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/RuntimeIOException.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/RuntimeIOException.java b/jena-base/src/main/java/org/apache/jena/atlas/RuntimeIOException.java new file mode 100644 index 0000000..1d9938b --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/RuntimeIOException.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; + +/** Runtime eception used to wrap IOExceptions */ +public class RuntimeIOException extends AtlasException +{ + public RuntimeIOException() { super() ; } + public RuntimeIOException(String msg) { super(msg) ; } + /** + * Wrap a Throwable - this is usually an IOException. + */ + public RuntimeIOException(Throwable th) { super(th) ; } + /** + * Wrap a Throwable - this is usually an IOException. + */ + public RuntimeIOException(String msg, Throwable th) { super(msg, th) ; } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/AWriter.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/AWriter.java b/jena-base/src/main/java/org/apache/jena/atlas/io/AWriter.java new file mode 100644 index 0000000..c046544 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/AWriter.java @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + +import org.apache.jena.atlas.lib.Closeable ; + + +/** Simple abstraction of a string/character output stream */ + +public interface AWriter extends Closeable, AutoCloseable +{ + public void write(char ch) ; + public void write(char[] cbuf) ; + public void write(String string) ; + + public void print(char ch) ; + public void print(char[] cbuf) ; + public void print(String string) ; + public void printf(String fmt, Object ...arg) ; + public void println(String object) ; + public void println() ; + + public void flush() ; + @Override + public void close() ; +} + http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/AWriterBase.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/AWriterBase.java b/jena-base/src/main/java/org/apache/jena/atlas/io/AWriterBase.java new file mode 100644 index 0000000..bc80fc2 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/AWriterBase.java @@ -0,0 +1,33 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + + +/** Simple abstraction of a string/character output stream */ + +public abstract class AWriterBase implements AWriter +{ + @Override + public final void write(char ch) { print(ch) ; } + @Override + public final void write(char[] cbuf) { print(cbuf) ; } + @Override + public final void write(String string) { print(string) ; } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/BlockUTF8.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/BlockUTF8.java b/jena-base/src/main/java/org/apache/jena/atlas/io/BlockUTF8.java new file mode 100644 index 0000000..2d5560b --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/BlockUTF8.java @@ -0,0 +1,281 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io ; + +import java.io.IOException ; +import java.nio.ByteBuffer ; +import java.nio.CharBuffer ; + +import org.apache.jena.atlas.lib.NotImplemented ; + +/** + * Convert between bytes and chars, UTF-8 only. + * + * This code is just the UTF-8 encoding rules - it does not check for legality + * of the Unicode data. The standard codec do, so do not round-trip with binary + * compatibility. (Example: a single element of a surrogate pair will + * be encoded/decoded without lost. + * + * + * The usual Charset encoders/decoders can be expensive to start up - they are also + * not thread safe. Sometimes we want to convert 10's of chars and UTF-8 can be + * done in code with no lookup tables (which, if used, are cache-unfriendly). + * + * This code is thread safe. It uses code in the hope that JITting will + * make it fast if used heavily. + */ + +public class BlockUTF8 +{ + // Looking in java.lang.StringCoding (Sun RT) is illuminating. + // The actual encode/decode code is in sun.nio.cs.UTF_8.(Decoder|Encoder) + // which has special cases for ByteBuffer, ByteBuffer with array (needs offsets) + // and byte[] <-> char[] + + // It seems that chars -> bytes (on <100char strings) is faster with BlockUTF8 + // but the conversion from bytes to string is faster with Java decoders (not by much though). + + /* + * Bits + * 7 U+007F 1 to 127 0xxxxxxx + * 11 U+07FF 128 to 2,047 110xxxxx 10xxxxxx + * 16 U+FFFF 2,048 to 65,535 1110xxxx 10xxxxxx 10xxxxxx + * 21 U+1FFFFF 65,536 to 1,114,111 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 26 U+3FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 31 U+7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + + + /** Convert the bytes in the ByteBuffer to characters in the CharBuffer. + * The CharBuffer must be large enough. + */ + public static void toChars(ByteBuffer bb, CharBuffer cb) + { +// if ( bb.hasArray() && cb.hasArray() ) +// { +// toCharsArray(bb.array(), cb.array()) ; +// return ; +// } + toCharsBuffer(bb, cb) ; + } + + /** Convert characters to UTF-8 bytes in the ByteBuffer. + * The ByteBuffer must be large enough. + */ + public static void fromChars(CharBuffer cb, ByteBuffer bb) + { +// if ( bb.hasArray() && cb.hasArray() ) +// { +// fromCharsArray(cb.array(), bb.array()) ; +// return ; +// } + fromCharsBuffer(cb, bb) ; + } + + /** Make a string from UTF-8 bytes in a ByteBuffer */ + public static String toString(ByteBuffer bb) + { + // I think that the copy from some mutable collector to immutable string is inevitable in java. + int len = bb.remaining() ; + CharBuffer cb = CharBuffer.allocate(len) ; + toChars(bb, cb) ; + return new String(cb.array(), 0, cb.position()) ; + } + + // Using buffer access. + private static void toCharsBuffer(ByteBuffer bb, CharBuffer cb) + { + int idx = bb.position(); + int limit = bb.limit() ; + + for ( ; idx < limit ; ) + { + int x = bb.get() ; + if ( x > 0 && x <= 127 ) + { + cb.put((char)x) ; + idx += 1 ; + } else if ( x == 0 ) + { + // Pass through a null byte as the null character (illegal Unicode, Java compatible). + cb.put((char)x) ; + idx += 1 ; + } + else if ( (x & 0xE0) == 0xC0 ) + { + // 10 => extension byte + // 110..... => 2 bytes + // Unroll common path + //int ch = readMultiBytes(bb, x & 0x1F, 2) ; + int x2 = bb.get() ; + if ( (x2 & 0xC0) != 0x80 ) + exception("Illegal UTF-8 processing character: 0x%04X",x2) ; + // 6 bits of x2 + int ch = ( (x&0x1F) << 6) | (x2 & 0x3F); + cb.put((char)ch) ; + idx += 2 ; + } + else if ( (x & 0xF0) == 0xE0 ) + { + // 1110.... => 3 bytes : 16 bits : not outside 16bit chars + int ch = readMultiBytes(bb, x & 0x0F, 3) ; + cb.put((char)ch) ; + idx += 3 ; + } + else if ( (x & 0xF8) == 0xF0 ) + { + // Looking like 4 byte charcater. + // 11110zzz => 4 bytes. + int ch = readMultiBytes(bb, x & 0x08, 4) ; + char chars[] = Character.toChars(ch) ; + cb.put(chars) ; + idx += 4 ; + } + else + { + exception("Illegal UTF-8: 0x%04X",x) ; + return ; + } + } + } + + private static void toCharsArray(byte[] bytes, char[] chars) + { + throw new NotImplemented() ; + } + + private static void fromCharsBuffer(CharBuffer cb, ByteBuffer bb) + { + // CharBuffers are CharSequences but charAt(i) adds a layer of work. + //int bytesStart = bb.position() ; + int idx = cb.position() ; + int limit = cb.limit() ; + for ( ; idx < limit ; idx ++ ) + { + char ch = cb.get() ; + if ( ch != 0 && ch <= 127 ) + { + // 7 bits + bb.put((byte)ch) ; + } + else if ( ch == 0 ) + { + // Java. + bb.put((byte)0x00) ; + // Modified UTF-8. + //bb.put((byte)0xC0) ; + //bb.put((byte)0x80) ; + } + else if ( ch <= 0x07FF ) + { + // 11 bits : 110yyyyy 10xxxxxx + // int x1 = ( ((ch>>(11-5))&0x7) | 0xC0 ) ; outputBytes(out, x1, 2, ch) ; return ; + int x1 = ( ((ch>>(11-5))&0x01F ) | 0xC0 ) ; + int x2 = ( (ch&0x3F) | 0x80 ) ; + bb.put((byte)x1) ; + bb.put((byte)x2) ; + } + else if ( ch <= 0xFFFF ) + { + // 16 bits : 1110aaaa 10bbbbbb 10cccccc + // int x1 = ( ((ch>>(16-4))&0x7) | 0xE0 ) ; outputBytes(out, x1, 3, ch) ; return ; + int x1 = ( ((ch>>(16-4))&0x0F) | 0xE0 ) ; + int x2 = ( ((ch>>6)&0x3F) | 0x80 ) ; + int x3 = ( (ch&0x3F) | 0x80 ) ; + bb.put((byte)x1) ; + bb.put((byte)x2) ; + bb.put((byte)x3) ; + } + // if ( Character.isDefined(ch) ) + // throw new AtlasException("not a character") ; + //if ( true ) throw new InternalErrorException("Valid code point for Java but not encodable") ; + // Not java, where chars are 16 bit. + else if ( ch <= 0x1FFFFF ) + { + // 21 bits : 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + int x1 = ( ((ch>>(21-3))&0x7) | 0xF0 ) ; + outputBytes(bb, x1, 4, ch) ; + } + else if ( ch <= 0x3FFFFFF ) + { + // 26 bits : 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + int x1 = ( ((ch>>(26-2))&0x3) | 0xF8 ) ; + outputBytes(bb, x1, 5, ch) ; + } + else if ( ch <= 0x7FFFFFFF ) + { + // 32 bits : 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + int x1 = ( ((ch>>(32-1))&0x1) | 0xFC ) ; + outputBytes(bb, x1, 6, ch) ; + } + } + //int bytesFinish = bb.position() ; + } + + private static void fromCharsArray(char[] array, byte[] array2) + { + throw new NotImplemented() ; + } + + public static void fromChars(CharSequence cs, ByteBuffer bb) + { + fromChars(CharBuffer.wrap(cs), bb) ; + } + + private static int readMultiBytes(ByteBuffer input, int start, int len) + { + // We have already read one byte. + if ( input.remaining() < (len-1) ) + exception("Premature end to UTF-8 sequence at end of input") ; + int x = start ; + for ( int i = 0 ; i < len-1 ; i++ ) + { + int x2 = input.get() ; + if ( (x2 & 0xC0) != 0x80 ) + exception("Illegal UTF-8 processing character: 0x%04X",x2) ; + // 6 bits of x2 + x = (x << 6) | (x2 & 0x3F); + } + return x ; + } + + /** Put bytes to the output ByteBuffer for charcater ch. + * The first byte is in x1 and already has the needed bits set. + */ + private static void outputBytes(ByteBuffer bb, int x1, int byteLength, int ch) + { + bb.put((byte)x1) ; + byteLength-- ; // remaining bytes + for ( int i = 0 ; i < byteLength ; i++ ) + { + // 6 Bits, loop from high to low + int shift = 6*(byteLength-i-1) ; + int x = (ch>>shift) & 0x3F ; + x = x | 0x80 ; // 10xxxxxx + bb.put((byte)x) ; + } + } + + // Does not return + private static void exception(String fmt, Object ...args) + { + String str = String.format(fmt,args) ; + IO.exception(new IOException(str)) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/BufferingWriter.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/BufferingWriter.java b/jena-base/src/main/java/org/apache/jena/atlas/io/BufferingWriter.java new file mode 100644 index 0000000..b17e930 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/BufferingWriter.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io ; + +import java.io.IOException ; +import java.io.Writer ; + +import org.apache.jena.atlas.io.IO ; + +/** + * A buffering writer. Like BufferedWriter but with no synchronization. A + * "synchronized" per character can be expensive. + * <p> + * The standard java.io classes have hidden synchronization so in some very + * critical situations, this can be expensive. + * </p> + * This class is not thread safe. + */ + +public final class BufferingWriter extends Writer { + // Default sizes + private static final int SIZE = 8 * 1024 ; // Unit size in bytes. + private static final int BLOB_SIZE = SIZE / 2 ; // Large object size, + // worse case, bytes + // Sizes for this instance + private final int blockSize ; + private final int blobSize ; + + private char[] buffer = new char[SIZE] ; + private int idx = 0 ; + private Writer out ; + + /** Create a buffering writer */ + public BufferingWriter(Writer dest) { + this(dest, SIZE, BLOB_SIZE) ; + } + + /** Create a buffering writer */ + public BufferingWriter(Writer dest, int size) { + this(dest, size, size/2) ; + } + + /** Create a buffering writer */ + public BufferingWriter(Writer dest, int size, int blobSize) { + this.out = dest ; + this.blockSize = size ; + this.blobSize = blobSize ; + } + + /** + * Output a string + * @param string Characters + */ + public void output(String string) { + output(string, 0, string.length()) ; + } + + /** + * Output a string + * + * @param string Characters + * @param off Starting point in the string + * @param length Length + */ + public void output(String string, int off, int length) { + boolean largeBlob = (length > blobSize) ; + + // There is no space or too big + if ( largeBlob || (blockSize - idx) < length ) + flushBuffer() ; + // If too big, do directly. + if ( largeBlob /* too big */) { + try { out.write(string, off, length) ; } + catch (IOException ex) { IO.exception(ex) ; } + return ; + } + int n = string.length() ; + string.getChars(off, (n + off), buffer, idx) ; + idx += n ; + } + + /** Output an array of characters */ + public void output(char chars[]) { + output(chars, 0, chars.length) ; + } + + /** + * Output an array of characters + * + * @param chars Characters + * @param start Start + * @param length Length + */ + public void output(char chars[], int start, int length) { + boolean largeBlob = (length > blobSize) ; + + // There is no space or too big + if ( largeBlob || (blockSize - idx) < length ) + flushBuffer() ; + // If too big, do directly. + if ( largeBlob /* too big */) { + try { out.write(chars) ; } + catch (IOException ex) { IO.exception(ex) ; } + return ; + } + System.arraycopy(chars, start, buffer, idx, length) ; + idx += length ; + } + + /** Output a single character */ + public void output(char ch) { + if ( blockSize == idx ) + flushBuffer() ; + buffer[idx++] = ch ; + } + + private void flushBuffer() { + if ( idx > 0 ) { + try { out.write(buffer, 0, idx) ; } + catch (IOException ex) { IO.exception(ex) ; } + idx = 0 ; + } + + } + + // ---- Writer + + @Override + public void close() { + flushBuffer() ; + IO.close(out) ; + } + + @Override + public void flush() { + flushBuffer() ; + IO.flush(out) ; + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + output(cbuf, off, len) ; + } + + @Override + public void write(char[] cbuf) throws IOException { + write(cbuf, 0, cbuf.length) ; + } + + @Override + public void write(String string, int off, int len) throws IOException { + output(string, off, len) ; + } + + @Override + public void write(String string) throws IOException { + output(string, 0, string.length()) ; + } + + @Override + public void write(int ch) throws IOException { + output((char)ch) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/CharStream.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/CharStream.java b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStream.java new file mode 100644 index 0000000..f066843 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStream.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + + +/** A simplifed reader interface without IOExceptions. + * And it's an interface, not an abstract class + */ +public interface CharStream +{ + public int advance() ; + + /** Close the stream - different name from java.io.Reader.close */ + public void closeStream() ; +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamBasic.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamBasic.java b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamBasic.java new file mode 100644 index 0000000..27416d9 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamBasic.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + +import java.io.IOException ; +import java.io.Reader ; + +/** A PeekReaderSource that does no buffering - just wraps a reader. */ +public final class CharStreamBasic extends CharStreamReader +{ + private Reader reader ; + + CharStreamBasic(Reader reader) + { + this.reader = reader ; + } + + @Override + public int advance() + { + try + { + return reader.read() ; + } catch (IOException ex) + { + ex.printStackTrace(); + return -1 ; + } + } + + @Override + public void closeStream() + { + try + { + reader.close() ; + } catch (IOException ex) + { + ex.printStackTrace(); + } + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamBuffered.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamBuffered.java b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamBuffered.java new file mode 100644 index 0000000..519dc36 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamBuffered.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + +import static org.apache.jena.atlas.io.IO.EOF ; + +import java.io.IOException ; +import java.io.Reader ; + +/** Buffering reader without the (hidden) sync overhead in BufferedReader + * + * @see java.io.BufferedReader + */ + + +public final class CharStreamBuffered extends CharStreamReader +{ + /*package*/ static final int CB_SIZE = 128 * 1024 ; + + private final char[] chars ; // CharBuffer? + private int buffLen = 0 ; + private int idx = 0 ; + + private final Source source; + + public CharStreamBuffered(Reader r) + { this(r, CB_SIZE) ; } + + /** + * @param r + * @param buffSize + */ + public CharStreamBuffered(Reader r, int buffSize) + { + super() ; + source = new SourceReader(r) ; + chars = new char[buffSize] ; + } + + // Local adapter/encapsulation + private interface Source + { + int fill(char[] array) ; + void close() ; + } + + static final class SourceReader implements Source + { + final Reader reader ; + SourceReader(Reader r) { reader = r ; } + + @Override + public void close() + { + try { reader.close() ; } catch (IOException ex) { IO.exception(ex) ; } + } + + @Override + public int fill(char[] array) + { + try { return reader.read(array) ; } catch (IOException ex) { IO.exception(ex) ; return -1 ; } + } + } + +// /** Faster?? for ASCII */ +// static final class SourceASCII implements Source +// { +// final InputStream input ; +// SourceASCII(InputStream r) { input = r ; } +// public void close() +// { try { input.close() ; } catch (IOException ex) { exception(ex) ; } } +// +// public final int fill(char[] array) +// { +// try { +// // Recycle. +// byte[] buff = new byte[array.length] ; +// int len = input.read(buff) ; +// for ( int i = 0 ; i < len ; i++ ) +// { +// byte b = buff[i] ; +// if ( b < 0 ) +// throw new AtlasException("Illegal ASCII charcater: "+b) ; +// array[i] = (char)b ; +// } +// return len ; +// } catch (IOException ex) { exception(ex) ; return -1 ; } +// } +// } +// +// static final class SourceChannel implements Source +// { +// final ReadableByteChannel channel ; +// CharsetDecoder decoder = Chars.createDecoder() ; +// SourceChannel(ReadableByteChannel r) { channel = r ; } +// +// @Override +// public void close() +// { +// try { channel.close() ; } catch (IOException ex) { exception(ex) ; } +// } +// +// @Override +// public int fill(char[] array) +// { +// // Encoding foo. +//// Bytes +//// +//// ByteBuffer b = ByteBuffer.wrap(null) ; +//// +//// try { return channel.read(null).read(array) ; } catch (IOException ex) { exception(ex) ; return -1 ; } +// return -1 ; +// } +// } + + @Override + public final int advance() + { + if ( idx >= buffLen ) + // Points outside the array. Refill it + fillArray() ; + + // Advance one character. + if ( buffLen >= 0 ) + { + char ch = chars[idx] ; + // Advance the lookahead character + idx++ ; + return ch ; + } + else + // Buffer empty, end of stream. + return EOF ; + } + + private int fillArray() + { + int x = source.fill(chars) ; + idx = 0 ; + buffLen = x ; // Maybe -1 + return x ; + } + + + @Override + public void closeStream() + { + source.close() ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamReader.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamReader.java b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamReader.java new file mode 100644 index 0000000..d7dce11 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamReader.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.io; + +import java.io.IOException ; +import java.io.Reader ; + +/** Machinary to add Reader functionality to a CharStream */ +public abstract class CharStreamReader extends Reader implements CharStream +{ + @Override + public int read(char[] cbuf, int off, int len) throws IOException + { + for ( int i = 0 ; i < len ; i++ ) + { + int x = advance() ; + if ( x == -1 ) + return (i==0)? -1 : i ; + cbuf[i] = (char)x ; + } + return len ; + } + + @Override + public int read() throws IOException + { + return advance() ; + } + + + @Override + public void close() throws IOException + { closeStream() ; } + + @Override + public abstract int advance() ; + + @Override + public abstract void closeStream() ; +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamSequence.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamSequence.java b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamSequence.java new file mode 100644 index 0000000..c0e9f40 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/CharStreamSequence.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; +import static org.apache.jena.atlas.io.IO.EOF ; + +public final class CharStreamSequence implements CharStream +{ + private CharSequence string ; + // Next character. + private int idx ; + + public CharStreamSequence(CharSequence string) + { + this.string = string ; + this.idx = 0 ; + } + + @Override + public int advance() + { + if ( idx >= string.length() ) + return EOF ; + return string.charAt(idx++); + } + + @Override + public void closeStream() + { + string = null ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/IO.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/IO.java b/jena-base/src/main/java/org/apache/jena/atlas/io/IO.java new file mode 100644 index 0000000..ac312e8 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/IO.java @@ -0,0 +1,325 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + +import java.io.* ; +import java.nio.charset.Charset ; +import java.nio.charset.StandardCharsets ; +import java.util.zip.GZIPInputStream ; +import java.util.zip.GZIPOutputStream ; + +import org.apache.jena.atlas.RuntimeIOException ; +import org.apache.jena.atlas.lib.IRILib ; + +public class IO +{ + public static final int EOF = -1 ; + public static final int UNSET = -2 ; + + private static Charset utf8 = StandardCharsets.UTF_8 ; + private static Charset ascii = StandardCharsets.US_ASCII ; + + /** Open an input stream to a file. + * If the filename is null or "-", return System.in + * If the filename ends in .gz, wrap in GZIPInputStream + */ + static public InputStream openFile(String filename) { + try { + return openFileEx(filename) ; + } + catch (Exception ex) { throw new RuntimeIOException(ex) ; } + } + + /** Open an input stream to a file; do not mask IOExceptions. + * If the filename is null or "-", return System.in + * If the filename ends in .gz, wrap in GZIPInputStream + * @param filename + * @throws FileNotFoundException + * @throws IOException + */ + static public InputStream openFileEx(String filename) throws IOException, FileNotFoundException { + if ( filename == null || filename.equals("-") ) + return System.in ; + if ( filename.startsWith("file:") ) + { + filename = filename.substring("file:".length()) ; + filename = IRILib.decode(filename) ; + } + InputStream in = new FileInputStream(filename) ; + if ( filename.endsWith(".gz") ) + in = new GZIPInputStream(in) ; + return in ; + } + + /** Open a UTF8 Reader for a file. + * If the filename is null or "-", use System.in + * If the filename ends in .gz, use GZIPInputStream + */ + static public Reader openFileUTF8(String filename) { return openFileReader(filename, utf8) ; } + + /** Open an ASCII Reader for a file. + * If the filename is null or "-", use System.in + * If the filename ends in .gz, use GZIPInputStream + */ + static public Reader openFileASCII(String filename) { return openFileReader(filename, ascii) ; } + + private static Reader openFileReader(String filename, Charset charset) + { + InputStream in = openFile(filename) ; + return new InputStreamReader(in, charset) ; + } + + /** Create an unbuffered reader that uses UTF-8 encoding */ + static public Reader asUTF8(InputStream in) + { + return new InputStreamReader(in, utf8.newDecoder()); + } + + /** Create a unbuffered reader that uses ASCII encoding */ + static public Reader asASCII(InputStream in) + { + return new InputStreamReader(in, ascii.newDecoder()); + } + + /** Create an buffered reader that uses UTF-8 encoding */ + static public BufferedReader asBufferedUTF8(InputStream in) { + return new BufferedReader(asUTF8(in)) ; + } + + /** Create a writer that uses UTF-8 encoding */ + static public Writer asUTF8(OutputStream out) { + return new OutputStreamWriter(out, utf8.newEncoder()); + } + + /** Create a writer that uses ASCII encoding */ + static public Writer asASCII(OutputStream out) { + return new OutputStreamWriter(out, ascii.newEncoder()); + } + + /** Create a writer that uses UTF-8 encoding and is buffered. */ + static public Writer asBufferedUTF8(OutputStream out) { + Writer w = new OutputStreamWriter(out, utf8.newEncoder()); + return new BufferingWriter(w) ; + } + + /** Open a file for output - may include adding gzip processing. */ + static public OutputStream openOutputFile(String filename) + { + try { + return openOutputFileEx(filename) ; + } + catch (IOException ex) { IO.exception(ex) ; return null ; } + } + + /** Open an input stream to a file; do not mask IOExceptions. + * If the filename ends in .gz, wrap in GZIPOutputStream + * @param filename + * @throws FileNotFoundException If the output can't be opened. + * @throws IOException for bad gzip encoded data + */ + static public OutputStream openOutputFileEx(String filename) throws FileNotFoundException,IOException + { + if ( filename == null || filename.equals("-") ) + return System.out ; + if ( filename.startsWith("file:") ) + { + filename = filename.substring("file:".length()) ; + filename = IRILib.decode(filename) ; + } + OutputStream out = new FileOutputStream(filename) ; + if ( filename.endsWith(".gz") ) + out = new GZIPOutputStream(out) ; + return out ; + } + + /** Wrap in a general writer interface */ + static public AWriter wrap(Writer w) { + return Writer2.wrap(w) ; + } + + /** Wrap in a general writer interface */ + static public AWriter wrapUTF8(OutputStream out) { return wrap(asUTF8(out)) ; } + + /** Wrap in a general writer interface */ + static public AWriter wrapASCII(OutputStream out) { return wrap(asASCII(out)) ; } + + /** Create a print writer that uses UTF-8 encoding */ + static public PrintWriter asPrintWriterUTF8(OutputStream out) { + return new PrintWriter(asUTF8(out)); + } + + public static void close(org.apache.jena.atlas.lib.Closeable resource) { + resource.close() ; + } + + public static void closeSilent(org.apache.jena.atlas.lib.Closeable resource) { + try { resource.close(); } catch (Exception ex) { } + } + + public static void close(java.io.Closeable resource) { + if ( resource == null ) + return ; + try { resource.close(); } catch (IOException ex) { exception(ex) ; } + } + + public static void closeSilent(java.io.Closeable resource) { + if ( resource == null ) + return ; + try { resource.close(); } catch (IOException ex) { } + } + + public static void close(AWriter resource) { + if ( resource == null ) + return ; + resource.close(); + } + + public static void closeSilent(AWriter resource) { + if ( resource == null ) + return ; + try { resource.close(); } catch (Exception ex) { } + } + + public static void close(IndentedWriter resource) { + if ( resource == null ) + return ; + resource.close(); + } + + public static void closeSilent(IndentedWriter resource) { + if ( resource == null ) + return ; + try { resource.close(); } catch (Exception ex) { } + } + + public static void exception(IOException ex) { + throw new RuntimeIOException(ex) ; + } + + public static void exception(String msg, IOException ex) { + throw new RuntimeIOException(msg, ex) ; + } + + public static void flush(OutputStream out) { + if ( out == null ) + return ; + try { out.flush(); } catch (IOException ex) { exception(ex) ; } + } + + public static void flush(Writer out) { + if ( out == null ) + return ; + try { out.flush(); } catch (IOException ex) { exception(ex) ; } + } + + public static void flush(AWriter out) { + if ( out == null ) + return ; + out.flush(); + } + + private static final int BUFFER_SIZE = 32*1024 ; + + public static byte[] readWholeFile(InputStream in) { + try(ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE)) { + byte buff[] = new byte[BUFFER_SIZE] ; + while (true) { + int l = in.read(buff) ; + if ( l <= 0 ) + break ; + out.write(buff, 0, l) ; + } + return out.toByteArray() ; + } + catch (IOException ex) { + exception(ex) ; + return null ; + } + } + + /** Read a whole file as UTF-8 + * @param filename + * @return String + * @throws IOException + */ + + public static String readWholeFileAsUTF8(String filename) throws IOException { + try ( InputStream in = new FileInputStream(filename) ) { + return readWholeFileAsUTF8(in) ; + } + } + + /** Read a whole stream as UTF-8 + * + * @param in InputStream to be read + * @return String + * @throws IOException + */ + public static String readWholeFileAsUTF8(InputStream in) throws IOException { + // Don't buffer - we're going to read in large chunks anyway + try ( Reader r = asUTF8(in) ) { + return readWholeFileAsUTF8(r) ; + } + } + + /** Read a whole file as UTF-8 + * + * @param r + * @return String The whole file + * @throws IOException + */ + + // Private worker as we are trying to force UTF-8. + private static String readWholeFileAsUTF8(Reader r) throws IOException { + try(StringWriter sw = new StringWriter(BUFFER_SIZE)) { + char buff[] = new char[BUFFER_SIZE]; + for (;;) + { + int l = r.read(buff); + if (l < 0) + break; + sw.write(buff, 0, l); + } + return sw.toString(); + } + } + + public static String uniqueFilename(String directory, String base, String ext) { + File d = new File(directory) ; + if ( !d.exists() ) + throw new IllegalArgumentException("Not found: " + directory) ; + try { + String fn0 = d.getCanonicalPath() + File.separator + base ; + String fn = fn0 ; + int x = 1 ; + while (true) { + if ( ext != null ) + fn = fn + "."+ext ; + File f = new File(fn) ; + if ( ! f.exists() ) + return fn ; + fn = fn0 + "-" + (x++) ; + } + } catch (IOException e) { + IO.exception(e) ; + return null ; + } + } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/InStreamASCII.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/InStreamASCII.java b/jena-base/src/main/java/org/apache/jena/atlas/io/InStreamASCII.java new file mode 100644 index 0000000..049449c --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/InStreamASCII.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + +import java.io.IOException ; +import java.io.InputStream ; +import java.io.Reader ; + +import org.apache.jena.atlas.AtlasException ; + +/** Fast and streaming. + */ +public final class InStreamASCII extends Reader implements CharStream +{ + private InputStreamBuffered input ; + private long count = 0 ; + + public InStreamASCII(InputStream in) + { + if ( in instanceof InputStreamBuffered ) + { + input = (InputStreamBuffered)in ; + return ; + } + input = new InputStreamBuffered(in) ; + } + + public InStreamASCII(InputStreamBuffered in) { input = in ; } + + @Override + public boolean ready() throws IOException + { + return input.available() > 0 ; + } + + @Override + public void close() throws IOException + { input.close() ; } + + @Override + public void closeStream() + { IO.close(input) ; } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException + { + for ( int i = off ; i < off+len ; i++ ) + { + int x = read() ; + if ( x == -1 ) + { + if ( i == off ) + return -1 ; + return (i-off) ; + } + if ( x > 128 ) + throw new AtlasException("Illegal ASCII character : "+x) ; + cbuf[i] = (char)x ; + } + return len ; + } + + @Override + public int read() throws IOException + { return advance() ; } + + @Override + public int advance() + { + count++ ; + return input.advance() ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/InStreamUTF8.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/InStreamUTF8.java b/jena-base/src/main/java/org/apache/jena/atlas/io/InStreamUTF8.java new file mode 100644 index 0000000..5903882 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/InStreamUTF8.java @@ -0,0 +1,254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + +import java.io.ByteArrayInputStream ; +import java.io.IOException ; +import java.io.InputStream ; +import java.io.Reader ; + +import org.apache.jena.atlas.AtlasException ; + +/** Fast and streaming UTF-8 */ +public final class InStreamUTF8 extends Reader implements CharStream +{ + // TODO Add line and col counts. + // See arq.utf8. + // TODO Better ready()/available() in InputStreamBuffered + + // The standard Java way of doing this is via charset decoders. + // One small disadvantage is that bad UTF-8 does not get flagged as to + // the byte position of the error. + + // This class collects knowledge of how UTF-8 encoding works; + // the Java classes are usually slightly faster compared to using + // this class with an InputStreamBuffered but the difference is small. + // This class generated meaningful error messages (when line/col added). + + // The Java classes copy-convert a byte buffer into a char buffer. + // Sometimes, for example in a parser, this isn't a convenient model + // because the app is looking one character at a time and accumulating + // the chars until it sees the end of a token of arbitrary length + // or processes escape sequences. + // + // The app might use a StringBuilder so the bytes get copied into + // a char buffer and out again. Instead, this code assumes the + // app is in charge of that. + + // UTF-8 (UTF-16) is different from other character sets because + // the relationship with Java's internal character representation is + // arithmetic, not a character mapping. + + // Todo: chars > 16 bits -> surrogate pairs. + + /* + * http://en.wikipedia.org/wiki/UTF-8 + * http://tools.ietf.org/html/rfc3629 + * http://www.ietf.org/rfc/rfc3629.txt + * + * Unicode Byte1 Byte2 Byte3 Byte4 + * U+0000âU+007F 0 to 127 0xxxxxxx + * U+0080âU+07FF 128 to 2,047 110yyyxx 10xxxxxx + * U+0800âU+FFFF 2,048 to 65,535 1110yyyy 10yyyyxx 10xxxxxx + * U+10000âU+10FFFF 65,536 to 1,114,111 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx + * + * Restricted cases (RFC 3629) + * 11110101-11110111 F5-F7 245-247 start of 4-byte sequence for codepoint above 10FFFF + * 11111000-11111011 F8-FB 248-251 start of 5-byte sequence + * 11111100-11111101 FC-FD 252-253 start of 6-byte sequence + * + * Illegal: + * 11000000-11000001 C0-C1 192-193 Overlong encoding: start of a 2-byte sequence, but code point <= 127 + * 11111110-11111111 FE-FF 254-255 Invalid: not defined by original UTF-8 specification + */ + + // There is some sort of stream decoder backing the Sun implementation + // of CharsetDecoder (sun.io.StreamDecoder) but it's not on all platforms + // I want a known decoder specifically for UTF8 + + private InputStreamBuffered input ; + //private long count = 0 ; + + public InStreamUTF8(InputStream in) + { + if ( in instanceof InputStreamBuffered ) + { + input = (InputStreamBuffered)in ; + return ; + } + input = new InputStreamBuffered(in) ; + } + + public InStreamUTF8(InputStreamBuffered in) { input = in ; } + + + @Override + public boolean ready() throws IOException + { + return input.available() > 0 ; + } + + @Override + public void close() throws IOException + { input.close() ; } + + @Override + public void closeStream() + { IO.close(input) ; } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException + { + // Doing this on a block of bytes may be faster. + for ( int i = off ; i < off+len ; i++ ) + { + int x = read() ; + if ( x == -1 ) + { + if ( i == off ) + return -1 ; + return (i-off) ; + } + cbuf[i] = (char)x ; + } + return len ; + } + + @Override + public final int read() throws IOException + { + int ch = advance(input) ; + //if ( ! Character.isDefined(ch) ) throw new AtlasException(String.format("Undefined codepoint: 0x%04X", ch)) ; + return ch ; + } + + + /** Next codepoint, given the first byte of any UTF-8 byte sequence is already known. + * Not necessarily a valid char (this function can be used a straight UTF8 decoder + */ + @Override + public final int advance() + { return advance(input) ; } + + /** Next codepoint */ + public static final int advance(InputStreamBuffered input) + { + int x = input.advance() ; + if ( x == -1 ) return -1 ; + return advance(input, x) ; + } + + /** Next codepoint, given the first byte of any UTF-8 byte sequence is already known. + * Not necessarily a valid char (this function can be used a straight UTF8 decoder + */ + + public static final int advance(InputStreamBuffered input, int x) + { + //count++ ; + // Fastpath + if ( x == -1 || x <= 127 ) + { + //count++ ; + return x ; + } + + // 10 => extension byte + // 110..... => 2 bytes + if ( (x & 0xE0) == 0xC0 ) + { + int ch = readMultiBytes(input, x & 0x1F, 2) ; + // count += 2 ; + return ch ; + + } + // 1110.... => 3 bytes : 16 bits : not outside 16bit chars + if ( (x & 0xF0) == 0xE0 ) + { + int ch = readMultiBytes(input, x & 0x0F, 3) ; + // count += 3 ; + //if ( ! Character.isDefined(ch) ) throw new AtlasException(String.format("Undefined codepoint: 0x%04X", ch)) ; + return ch ; + } + + // Looking like 4 byte charcater. + int ch = -2 ; + // 11110zzz => 4 bytes. + if ( (x & 0xF8) == 0xF0 ) + { + ch = readMultiBytes(input, x & 0x08, 4) ; + // Opsp - need two returns. Character.toChars(ch, chars, 0) ; + // count += 4 ; + } + + else + IO.exception(new IOException("Illegal UTF-8: "+x)) ; + + // This test will go off. We're processing a 4 byte sequence but Java only supports 16 bit chars. + if ( ch > Character.MAX_VALUE ) + throw new AtlasException("Out of range character (must use a surrogate pair)") ; + if ( ! Character.isDefined(ch) ) throw new AtlasException(String.format("Undefined codepoint: 0x%04X", ch)) ; + return ch ; + } + + private static int readMultiBytes(InputStreamBuffered input, int start, int len) //throws IOException + { + //System.out.print(" -("+len+")") ; p(start) ; + + int x = start ; + for ( int i = 0 ; i < len-1 ; i++ ) + { + int x2 = input.advance() ; + if ( x2 == -1 ) + throw new AtlasException("Premature end to UTF-8 sequence at end of input") ; + + if ( (x2 & 0xC0) != 0x80 ) + //throw new AtlasException("Illegal UTF-8 processing character "+count+": "+x2) ; + throw new AtlasException(String.format("Illegal UTF-8 processing character: 0x%04X",x2)) ; + // 6 bits of x2 + x = (x << 6) | (x2 & 0x3F); + } + return x ; + } + + private static void p(int ch) + { + System.out.printf(" %02X", ch) ; + if ( ch == -1 ) + System.out.println(); + } + + public static String decode(byte[] bytes) + { + try + { + char[] chars = new char[bytes.length] ; + InputStream in = new ByteArrayInputStream(bytes) ; + StringBuilder buff = new StringBuilder() ; + Reader r = new InStreamUTF8(in) ; + int len ; + len = r.read(chars) ; + IO.close(r) ; + return new String(chars, 0, len) ; + } catch (IOException ex) + { + IO.exception(ex) ; + return null ; + } + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/IndentedLineBuffer.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/IndentedLineBuffer.java b/jena-base/src/main/java/org/apache/jena/atlas/io/IndentedLineBuffer.java new file mode 100644 index 0000000..7c7b5f5 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/IndentedLineBuffer.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; + +import java.io.StringWriter ; + +/** IndentLineBuffer is a buffer that records an indent level + * and uses that to insert a prefix at each line. + * It can also insert line numbers at the beginning of lines. + */ + +public class IndentedLineBuffer extends IndentedWriter +{ + StringWriter sw ; + public IndentedLineBuffer() { this(false) ; } + + public IndentedLineBuffer(boolean withLineNumbers) + { + super(new StringWriter(), withLineNumbers) ; + sw = (StringWriter)super.out ; + } + + public StringBuffer getBuffer() { return sw.getBuffer(); } + + public String asString() { return sw.toString() ; } + @Override + public String toString() { return asString() ; } + + // Names more usually used for a buffer. + public void append(String fmt, Object... args) { printf(fmt, args) ; } + public void append(char ch) { print(ch) ;} + + public void clear() { sw.getBuffer().setLength(0) ; } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/IndentedWriter.java ---------------------------------------------------------------------- diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/IndentedWriter.java b/jena-base/src/main/java/org/apache/jena/atlas/io/IndentedWriter.java new file mode 100644 index 0000000..51414f9 --- /dev/null +++ b/jena-base/src/main/java/org/apache/jena/atlas/io/IndentedWriter.java @@ -0,0 +1,366 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.atlas.io; +import static java.lang.String.format ; + +import java.io.IOException ; +import java.io.OutputStream ; +import java.io.Writer ; + +import org.apache.jena.atlas.RuntimeIOException ; +import org.apache.jena.atlas.lib.Closeable ; + +/** A writer that records what the current indentation level is, and + * uses that to insert a prefix at each line. + * It can also insert line numbers at the beginning of lines. */ + +public class IndentedWriter extends AWriterBase implements AWriter, Closeable +{ + /** Stdout wrapped in an IndentedWriter - no line numbers */ + public static final IndentedWriter stdout = new IndentedWriter(System.out) ; + /** Stderr wrapped in an IndentedWriter - no line numbers */ + public static final IndentedWriter stderr = new IndentedWriter(System.err) ; + + static { + stdout.setFlushOnNewline(true) ; + stderr.setFlushOnNewline(true) ; + } + // Note cases:if (!flatMode) + // 1/ incIndent - decIndent with no output should not cause any padding + // 2/ newline() then no text, then finish should not cause a line number. + + protected Writer out = null ; + + protected static final int INDENT = 2 ; + protected int unitIndent = INDENT ; + protected int currentIndent = 0 ; + protected int column = 0 ; + protected int row = 1 ; + protected boolean lineNumbers = false ; + protected boolean startingNewLine = true ; + private char padChar = ' ' ; + private String endOfLineMarker = null ; // Null mean none. + private String padString = null ; + + protected boolean flatMode = false ; + private boolean flushOnNewline = false ; + + private IndentedWriter() { this(System.out, false) ; } + + /** Construct a UTF8 IndentedWriter around an OutputStream */ + public IndentedWriter(OutputStream outStream) { this(outStream, false) ; } + + /** Construct a UTF8 IndentedWriter around an OutputStream */ + public IndentedWriter(OutputStream outStream, boolean withLineNumbers) + { + this(makeWriter(outStream), withLineNumbers) ; + } + + private static Writer makeWriter(OutputStream out) + { + // return BufferingWriter.create(out) ; + return IO.asBufferedUTF8(out) ; + } + + /** Using Writers directly is discouraged */ + protected IndentedWriter(Writer writer) { this(writer, false) ; } + + /** Using Writers directly is discouraged */ + protected IndentedWriter(Writer writer, boolean withLineNumbers) + { + out = writer ; + lineNumbers = withLineNumbers ; + startingNewLine = true ; + } + + @Override + public void print(String str) + { + if ( str == null ) + str = "null" ; + if ( false ) + { + // Don't check for embedded newlines. + write$(str) ; + return ; + } + for ( int i = 0 ; i < str.length() ; i++ ) + printOneChar(str.charAt(i)) ; + } + + @Override + public void printf(String formatStr, Object... args) + { + print(format(formatStr, args)) ; + } + + @Override + public void print(char ch) { printOneChar(ch) ; } + + @Override + public void println(String str) { print(str) ; newline() ; } + public void println(char ch) { print(ch) ; newline() ; } + + @Override + public void println() { newline() ; } + + @Override + public void print(char[] cbuf) + { + for ( char aCbuf : cbuf ) + { + printOneChar( aCbuf ); + } + } + + /** Print a string N times */ + public void print(String s, int n) + { + for ( int i = 0 ; i < n ; i++ ) print(s) ; + } + + /** Print a char N times */ + public void print(char ch, int n) + { + lineStart() ; + for ( int i = 0 ; i < n ; i++ ) printOneChar(ch) ; + } + + private char lastChar = '\0' ; + // Worker + private void printOneChar(char ch) + { + // Turn \r\n into a single newline call. + // Assumes we don't get \r\r\n etc + if ( ch == '\n' && lastChar == '\r' ) + { + lastChar = ch ; + return ; + } + + lineStart() ; + lastChar = ch ; + + // newline + if ( ch == '\n' || ch == '\r' ) + { + newline() ; + return ; + } + write$(ch) ; + column += 1 ; + } + + private void write$(char ch) + { try { out.write(ch) ; } catch (IOException ex) { throw new RuntimeIOException(ex) ; } } + + private void write$(String s) + { try { out.write(s) ; } catch (IOException ex) { throw new RuntimeIOException(ex) ; } } + + public void newline() + { + lineStart() ; + + if ( endOfLineMarker != null ) + print(endOfLineMarker) ; + if ( ! flatMode ) + write$('\n') ; + startingNewLine = true ; + row++ ; + column = 0 ; + // Note that PrintWriters do not autoflush by default + // so if layered over a PrintWirter, need to flush that as well. + if (flushOnNewline) flush() ; + } + + private boolean atStartOfLine() { return column <= currentIndent ; } + + public void ensureStartOfLine() + { + if ( !atStartOfLine() ) + newline() ; + } + + @Override + public void close() { try { out.close(); } catch (IOException ex) {} } + + @Override + public void flush() { try { out.flush(); } catch (IOException ex) {} } + + /** Pad to the indent (if we are before it) */ + public void pad() + { + if ( startingNewLine && currentIndent > 0 ) + lineStart() ; + padInt() ; + } + + /** Pad to a given number of columns EXCLUDING the indent. + * + * @param col Column number (first column is 1). + */ + public void pad(int col) { pad(col, false) ; } + + /** Pad to a given number of columns maybe including the indent. + * + * @param col Column number (first column is 1). + * @param absoluteColumn Whether to include the indent + */ + public void pad(int col, boolean absoluteColumn ) + { + // Make absolute + if ( !absoluteColumn ) + col = col+currentIndent ; + int spaces = col - column ; + for ( int i = 0 ; i < spaces ; i++ ) + { + write$(' ') ; // Always a space. + column++ ; + } + } + + + private void padInt() + { + if ( padString == null ) + { + for ( int i = column ; i < currentIndent ; i++ ) + { + write$(padChar) ; + column++ ; + } + } + else + { + for ( int i = column ; i < currentIndent ; i += padString.length() ) + { + write$(padString) ; + column += padString.length() ; + } + } + } + + /** Get row/line (counts from 1) */ + public int getRow() { return row ; } + /** Get the absolute column. + * This is the location where the next charcter on the line will be printed. + * The IndentedWriter may not yet have padded to this place. + */ + public int getCol() { + if ( currentIndent > column ) + return currentIndent ; + return column ; + } + + /** Get indent from the left hand edge */ + public int getAbsoluteIndent() { return currentIndent ; } + /** Set indent from the left hand edge */ + public void setAbsoluteIndent(int x) { currentIndent = x ; } + + /** Position past current indent */ + public int getCurrentOffset() + { + int x = getCol() - getAbsoluteIndent() ; + if ( x >= 0 ) + return x ; + // At start of line somehow. + return 0 ; + } + + public boolean hasLineNumbers() + { + return lineNumbers ; + } + + public void setLineNumbers(boolean lineNumbers) + { + this.lineNumbers = lineNumbers ; + } + + public String getEndOfLineMarker() { return endOfLineMarker ; } + + /** Set the marker included at end of line - set to null for "none". Usually used for debugging. */ + public void setEndOfLineMarker(String marker) { endOfLineMarker = marker ; } + + /** Flat mode - print without NL, for a more compact representation - depends on caller */ + public boolean inFlatMode() { return flatMode ; } + public void setFlatMode(boolean flatMode) { this.flatMode = flatMode ; } + + /** Flush on newline **/ + public boolean getFlushOnNewline() { return flushOnNewline; } + public void setFlushOnNewline(boolean flushOnNewline) { this.flushOnNewline = flushOnNewline; } + + public char getPadChar() { return padChar ; } + public void setPadChar(char ch) { this.padChar = ch ; } + public String getPadString() { return padString ; } + public void setPadString(String str) { this.padString = str ; unitIndent = str.length(); } + + public void incIndent() { incIndent(unitIndent) ; } + public void incIndent(int x) + { + currentIndent += x ; + } + + public void decIndent() { decIndent(unitIndent) ; } + public void decIndent(int x) + { + currentIndent -= x ; + } + + public void setUnitIndent(int x) { unitIndent = x ; } + public int getUnitIndent() { return unitIndent ; } + public boolean atLineStart() { return startingNewLine ; } + + private void lineStart() + { + if ( flatMode ) + { + if ( startingNewLine && row > 1 ) + // Space between each line. + write$(' ') ; + startingNewLine = false ; + return ; + } + + // Need to do its just before we append anything, not after a NL, + // so that a final blank does not cause a line number + if ( startingNewLine ) + insertLineNumber() ; + padInt() ; + startingNewLine = false ; + } + + private static int WidthLineNumber = 3 ; + + private void insertLineNumber() + { + if ( ! lineNumbers ) + return ; + String s = Integer.toString(row) ; + for ( int i = 0 ; i < WidthLineNumber-s.length() ; i++ ) + write$(' ') ; + write$(s) ; + write$(' ') ; + } + + @Override + public String toString() { + return String.format("Indent = %d : [%d, %d]", currentIndent, row, column) ; + } +}
