You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2015/04/28 23:03:35 UTC
[8/9] jena git commit: Import of lib, io,
logging and iterator packages
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/InputStreamBuffered.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/InputStreamBuffered.java b/jena-base/src/main/java/org/apache/jena/atlas/io/InputStreamBuffered.java
new file mode 100644
index 0000000..9b03d43
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/InputStreamBuffered.java
@@ -0,0 +1,104 @@
+/*
+ * 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.InputStream ;
+
+/** InputStream optimizing for one byte at a time operation.
+ * BufferedInputStream operations have synchronization making
+ * reading one byte at a time expensive.
+ *
+ * @see java.io.InputStream
+ * @see java.io.BufferedInputStream
+ */
+public final class InputStreamBuffered extends InputStream
+{
+ public static int DFT_BUFSIZE = 16*1024 ;
+ private InputStream source ;
+ private byte[] buffer ;
+ private int buffLen = 0 ;
+ private int idx = 0 ;
+ private long count = 0 ;
+
+ public InputStreamBuffered(InputStream input)
+ {
+ this(input, DFT_BUFSIZE) ;
+ }
+
+ public InputStreamBuffered(InputStream input, int bufsize)
+ {
+ super() ;
+ this.source = input ;
+ this.buffer = new byte[bufsize] ;
+ this.idx = 0 ;
+ this.buffLen = 0 ;
+ }
+
+// @Override
+// public int read(byte b[], int off, int len) throws IOException
+// {
+// }
+
+ @Override
+ public int read() throws IOException
+ {
+ return advance() ;
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ source.close() ;
+ }
+
+ public final int advance()
+ {
+ if ( idx >= buffLen )
+ // Points outside the array. Refill it
+ fillArray() ;
+
+ // Advance one character.
+ if ( buffLen >= 0 )
+ {
+ byte ch = buffer[idx] ;
+ // Advance the lookahead character
+ idx++ ;
+ count++ ;
+ return ch & 0xFF ;
+ }
+ else
+ // Buffer empty, end of stream.
+ return EOF ;
+ }
+
+ private int fillArray()
+ {
+ try
+ {
+ int x = source.read(buffer) ;
+ idx = 0 ;
+ buffLen = x ; // Maybe -1
+ return x ;
+ }
+ catch (IOException ex) { IO.exception(ex) ; return -1 ; }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/NullOutputStream.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/NullOutputStream.java b/jena-base/src/main/java/org/apache/jena/atlas/io/NullOutputStream.java
new file mode 100644
index 0000000..8986f9e
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/NullOutputStream.java
@@ -0,0 +1,40 @@
+/**
+ * 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.OutputStream ;
+
+/** /dev/null */
+public class NullOutputStream extends OutputStream {
+
+ private static final NullOutputStream DEV_NULL = new NullOutputStream();
+
+ public static OutputStream sink() {
+ return DEV_NULL ;
+ }
+
+ public NullOutputStream() {}
+
+ @Override public void write(int b) {}
+
+ @Override public void write(byte b[]) {}
+
+ @Override public void write(byte b[], int off, int len) { }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/OutStreamUTF8.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/OutStreamUTF8.java b/jena-base/src/main/java/org/apache/jena/atlas/io/OutStreamUTF8.java
new file mode 100644
index 0000000..d4d15d4
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/OutStreamUTF8.java
@@ -0,0 +1,179 @@
+/*
+ * 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.OutputStream ;
+import java.io.Writer ;
+
+/** Output UTF-8 encoded data.
+ * This class implements the "Modified UTF8" encoding rules (null -> C0 80)
+ * It will encode any 16 bit value.
+ * It can be used as a pure UTf-8 encoder.
+ *
+ * @see InStreamUTF8
+ */
+public final class OutStreamUTF8 extends Writer
+{
+ private OutputStream out ;
+
+ public OutStreamUTF8(OutputStream out)
+ {
+ // Buffer?
+ this.out = out ;
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException
+ {
+ for ( int i = 0 ; i < len; i++ )
+ write(cbuf[off+i]) ;
+ }
+
+ @Override
+ public void write(int ch) throws IOException
+ { output(out, ch) ; }
+
+ @Override
+ public void write(char[] b) throws IOException
+ { write(b, 0, b.length) ; }
+
+ @Override
+ public void write(String str) throws IOException
+ { write(str,0, str.length()) ; }
+
+ @Override
+ public void write(String str, int idx, int len) throws IOException
+ {
+ for ( int i = 0 ; i < len; i++ )
+ write(str.charAt(idx+i)) ;
+ }
+
+ public void output(int x)
+ {
+ try {
+ output(out, x) ;
+ } catch (IOException ex) { IO.exception(ex) ; }
+ }
+
+ /*
+ * 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
+ */
+ public static void output(OutputStream out, int ch) throws IOException
+ {
+ if ( ch != 0 && ch <= 127 )
+ {
+ // 7 bits
+ out.write(ch) ;
+ return ;
+ }
+
+ if ( ch == 0 )
+ {
+ // Modified UTF-8.
+ out.write(0xC0) ;
+ out.write(0x80) ;
+ return ;
+ }
+
+ // Better? output(int HiMask, int byte length, int value)
+
+ 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 ) ;
+ out.write(x1) ;
+ out.write(x2) ;
+ return ;
+ }
+ 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 ) ;
+ out.write(x1) ;
+ out.write(x2) ;
+ out.write(x3) ;
+ return ;
+ }
+
+// 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.
+ if ( ch <= 0x1FFFFF )
+ {
+ // 21 bits : 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ int x1 = ( ((ch>>(21-3))&0x7) | 0xF0 ) ;
+ outputBytes(out, x1, 4, ch) ;
+ return ;
+ }
+ if ( ch <= 0x3FFFFFF )
+ {
+ // 26 bits : 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ int x1 = ( ((ch>>(26-2))&0x3) | 0xF8 ) ;
+ outputBytes(out, x1, 5, ch) ;
+ return ;
+ }
+
+ if ( ch <= 0x7FFFFFFF )
+ {
+ // 32 bits : 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+ int x1 = ( ((ch>>(32-1))&0x1) | 0xFC ) ;
+ outputBytes(out, x1, 6, ch) ;
+ return ;
+ }
+ }
+
+ private static void outputBytes(OutputStream out, int x1, int byteLength, int ch) throws IOException
+ {
+ // ByteLength = 3 => 2 byteLenth => shift=6 and shift=0
+ out.write(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
+ out.write(x) ;
+ }
+ }
+
+ @Override
+ public void flush() throws IOException
+ { out.flush(); }
+
+ @Override
+ public void close() throws IOException
+ { out.close() ; }
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/OutputUtils.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/OutputUtils.java b/jena-base/src/main/java/org/apache/jena/atlas/io/OutputUtils.java
new file mode 100644
index 0000000..9659643
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/OutputUtils.java
@@ -0,0 +1,77 @@
+/*
+ * 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.lib.BitsInt ;
+import org.apache.jena.atlas.lib.Chars ;
+
+
+public class OutputUtils
+{
+ /** Print the number x in width hex chars. x must fit */
+ public static void printHex(StringBuilder out, int x, int width)
+ {
+ for ( int i = width-1 ; i >= 0 ; i-- )
+ x = oneHex(out, x, i) ;
+ }
+
+ /** Print one hex digit of the number */
+ public static int oneHex(StringBuilder out, int x, int i)
+ {
+ int y = BitsInt.unpack(x, 4*i, 4*i+4) ;
+ char charHex = Chars.hexDigitsUC[y] ;
+ out.append(charHex) ;
+ return BitsInt.clear(x, 4*i, 4*i+4) ;
+ }
+
+ /** Print the number x in width hex chars. x must fit */
+ public static void printHex(Writer out, int x, int width)
+ {
+ for ( int i = width-1 ; i >= 0 ; i-- )
+ x = oneHex(out, x, i) ;
+ }
+
+ /** Print one hex digit of the number */
+ public static int oneHex(Writer out, int x, int i)
+ {
+ int y = BitsInt.unpack(x, 4*i, 4*i+4) ;
+ char charHex = Chars.hexDigitsUC[y] ;
+ try { out.write(charHex) ; } catch (IOException ex) {}
+ return BitsInt.clear(x, 4*i, 4*i+4) ;
+ }
+
+ /** Print the number x in width hex chars. x must fit */
+ public static void printHex(AWriter out, int x, int width)
+ {
+ for ( int i = width-1 ; i >= 0 ; i-- )
+ x = oneHex(out, x, i) ;
+ }
+
+ /** Print one hex digit of the number */
+ public static int oneHex(AWriter out, int x, int i)
+ {
+ int y = BitsInt.unpack(x, 4*i, 4*i+4) ;
+ char charHex = Chars.hexDigitsUC[y] ;
+ out.print(charHex) ;
+ return BitsInt.clear(x, 4*i, 4*i+4) ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/PeekInputStream.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/PeekInputStream.java b/jena-base/src/main/java/org/apache/jena/atlas/io/PeekInputStream.java
new file mode 100644
index 0000000..4a6403c
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/PeekInputStream.java
@@ -0,0 +1,233 @@
+/*
+ * 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 static org.apache.jena.atlas.io.IO.UNSET ;
+
+import java.io.FileInputStream ;
+import java.io.FileNotFoundException ;
+import java.io.IOException ;
+import java.io.InputStream ;
+
+import org.apache.jena.atlas.AtlasException ;
+import org.apache.jena.atlas.RuntimeIOException ;
+
+/** Parsing-centric input stream.
+ * @see PeekReader
+ */
+
+
+public final class PeekInputStream extends InputStream
+{
+ // Change to looking at slices of a ByteBuffer and rework TokenizerBytes
+
+ private final InputStreamBuffered source ;
+
+ private static final int PUSHBACK_SIZE = 10 ;
+ static final byte BYTE0 = (byte)0 ;
+
+ private byte[] pushbackBytes ;
+ private int idxPushback ; // Index into pushbackBytes: points to next pushBack. -1 => none.
+
+ private int currByte = UNSET ; // Next byte to return when reading forwards.
+ private long posn ;
+
+ public static final int INIT_LINE = 1 ;
+ public static final int INIT_COL = 1 ;
+
+ private long colNum ;
+ private long lineNum ;
+
+ // ---- static construction methods.
+
+ public static PeekInputStream make(InputStream inputStream)
+ {
+ return make(inputStream, InputStreamBuffered.DFT_BUFSIZE) ;
+ }
+
+ public static PeekInputStream make(InputStream inputStream, int bufferSize)
+ {
+ if ( inputStream instanceof PeekInputStream )
+ return (PeekInputStream)inputStream ;
+
+ if ( inputStream instanceof InputStreamBuffered )
+ return new PeekInputStream((InputStreamBuffered)inputStream) ;
+ InputStreamBuffered in = new InputStreamBuffered(inputStream, bufferSize) ;
+ return new PeekInputStream(in) ;
+ }
+
+ public static PeekInputStream open(String filename)
+ {
+ try {
+ InputStream in = new FileInputStream(filename) ;
+ return make(in) ;
+ } catch (FileNotFoundException ex){ throw new AtlasException("File not found: "+filename) ; }
+ }
+
+ private PeekInputStream(InputStreamBuffered input)
+ {
+ this.source = input ;
+ this.pushbackBytes = new byte[PUSHBACK_SIZE] ;
+ this.idxPushback = -1 ;
+
+ this.colNum = INIT_COL ;
+ this.lineNum = INIT_LINE ;
+ this.posn = 0 ;
+
+ // We start at byte "-1", i.e. just before the file starts.
+ // Advance always so that the peek byte is valid (is byte 0)
+ // Returns the byte before the file starts (i.e. UNSET).
+ }
+
+ public final InputStreamBuffered getInput() { return source ; }
+
+ public long getLineNum() { return lineNum; }
+
+ public long getColNum() { return colNum; }
+
+ public long getPosition() { return posn; }
+
+ //---- Do not access currByte except with peekByte/setCurrByte.
+ public final int peekByte()
+ {
+ if ( idxPushback >= 0 )
+ return pushbackBytes[idxPushback] ;
+
+ // If not started ... delayed initialization.
+ if ( currByte == UNSET )
+ init() ;
+ return currByte ;
+ }
+
+ // And the correct way to read the currByte is to call peekByte
+ private final void setCurrByte(int b)
+ {
+ currByte = b ;
+ }
+
+ public final int readByte() { return nextByte() ; }
+
+ /** push back a byte : does not alter underlying position, line or column counts*/
+ public final void pushbackByte(int b) { unreadByte(b) ; }
+
+ @Override
+ public final void close() throws IOException
+ {
+ source.close() ;
+ }
+
+ @Override
+ public final int read() throws IOException
+ {
+ if ( eof() )
+ return EOF ;
+ int x = readByte() ;
+ return x ;
+ }
+
+ @Override
+ public final int read(byte[] buf, int off, int len) throws IOException
+ {
+ if ( eof() )
+ return EOF ;
+ for ( int i = 0 ; i < len ; i++ )
+ {
+ int ch = readByte() ;
+ if ( ch == EOF )
+ return (i==0)? EOF : i ;
+ buf[i+off] = (byte)ch ;
+ }
+ return len ;
+ }
+
+ public final boolean eof() { return peekByte() == EOF ; }
+
+ // ----------------
+ // The methods below are the only ones to manipulate the byte buffers.
+ // Other methods may read the state of variables.
+
+ private final void unreadByte(int b)
+ {
+ // The push back buffer is in the order where [0] is the oldest.
+ // Does not alter the line number, column number or position count.
+
+ if ( idxPushback >= pushbackBytes.length )
+ {
+ // Enlarge pushback buffer.
+ byte[] pushbackBytes2 = new byte[pushbackBytes.length*2] ;
+ System.arraycopy(pushbackBytes, 0, pushbackBytes2, 0, pushbackBytes.length) ;
+ pushbackBytes = pushbackBytes2 ;
+ //throw new JenaException("Pushback buffer overflow") ;
+ }
+ if ( b == EOF || b == UNSET )
+ throw new RuntimeIOException("Illegal byte to push back: "+b) ;
+
+ idxPushback++ ;
+ pushbackBytes[idxPushback] = (byte)b ;
+ }
+
+ private final void init()
+ {
+ advanceAndSet() ;
+ if ( currByte == UNSET )
+ setCurrByte(EOF) ;
+ }
+
+ private final void advanceAndSet()
+ {
+ try {
+ int ch = source.read() ;
+ setCurrByte(ch) ;
+ } catch (IOException ex) { IO.exception(ex) ; }
+ }
+
+
+ // Invariants.
+ // currByte is either bytes[idx-1] or pushbackBytes[idxPushback]
+
+ /** Return the next byte, moving on one place and resetting the peek byte */
+ private final int nextByte()
+ {
+ int b = peekByte() ;
+
+ if ( b == EOF )
+ return EOF ;
+
+ if ( idxPushback >= 0 )
+ {
+ byte b2 = pushbackBytes[idxPushback] ;
+ idxPushback-- ;
+ return b2 ;
+ }
+
+ posn++ ;
+
+ if (b == '\n')
+ {
+ lineNum++;
+ colNum = INIT_COL ;
+ }
+ else
+ colNum++;
+
+ advanceAndSet() ;
+ return b ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/PeekReader.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/PeekReader.java b/jena-base/src/main/java/org/apache/jena/atlas/io/PeekReader.java
new file mode 100644
index 0000000..31076b0
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/PeekReader.java
@@ -0,0 +1,270 @@
+/*
+ * 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 static org.apache.jena.atlas.io.IO.UNSET ;
+
+import java.io.* ;
+
+import org.apache.jena.atlas.AtlasException ;
+import org.apache.jena.atlas.RuntimeIOException ;
+import org.apache.jena.atlas.lib.Chars ;
+
+/**
+ * Parsing-centric reader. This class is not thread safe.
+ * @see PeekInputStream
+ */
+
+public final class PeekReader extends Reader {
+ // Remember to apply fixes to PeekInputStream as well.
+
+ // Buffering is done by a CharStream - does it make adifference?
+ // Yes. A lot (Java6).
+
+ // Using a Reader here seems to have zero cost or benefit but CharStream
+ // allows fast String handling.
+ private final CharStream source ;
+
+ private static final int PUSHBACK_SIZE = 10 ;
+ static final byte CHAR0 = (char)0 ;
+
+ private char[] pushbackChars ;
+ // Index into pushbackChars: points to next pushBack.
+ // -1 => none.
+ private int idxPushback ;
+
+ // Next character to return when reading forwards.
+ private int currChar = UNSET ;
+ private long posn ;
+
+ public static final int INIT_LINE = 1 ;
+ public static final int INIT_COL = 1 ;
+
+ private long colNum ;
+ private long lineNum ;
+
+ // ---- static construction methods.
+
+ public static PeekReader make(Reader r) {
+ if ( r instanceof PeekReader )
+ return (PeekReader)r ;
+ return make(r, CharStreamBuffered.CB_SIZE) ;
+ }
+
+ public static PeekReader make(Reader r, int bufferSize) {
+ // It is worth our own buffering even if a BufferedReader
+ // because of the synchronized on one char reads in BufferedReader.
+ return new PeekReader(new CharStreamBuffered(r, bufferSize)) ;
+ }
+
+ /** Make PeekReader where the input is UTF8 : BOM is removed */
+ public static PeekReader makeUTF8(InputStream in) {
+ // This is the best route to make a PeekReader because it avoids
+ // chances of wrong charset for a Reader say.
+ PeekReader pr ;
+ if ( true ) {
+ Reader r = IO.asUTF8(in) ;
+ // This adds reader-level buffering
+ pr = make(r) ;
+ } else {
+ // This is a bit slower - reason unknown.
+ InputStreamBuffered in2 = new InputStreamBuffered(in) ;
+ CharStream r = new InStreamUTF8(in2) ;
+ pr = new PeekReader(r) ;
+ }
+ // Skip BOM.
+ int ch = pr.peekChar() ;
+ if ( ch == Chars.BOM )
+ // Skip BOM
+ pr.readChar() ;
+ return pr ;
+ }
+
+ /** Make PeekReader where the input is ASCII */
+ public static PeekReader makeASCII(InputStream in) {
+ Reader r = IO.asASCII(in) ;
+ return make(r) ;
+ }
+
+ public static PeekReader make(CharStream r) {
+ return new PeekReader(r) ;
+ }
+
+ public static PeekReader readString(String string) {
+ return new PeekReader(new CharStreamSequence(string)) ;
+ }
+
+ public static PeekReader open(String filename) {
+ try {
+ InputStream in = new FileInputStream(filename) ;
+ return makeUTF8(in) ;
+ } catch (FileNotFoundException ex) {
+ throw new AtlasException("File not found: " + filename) ;
+ }
+ }
+
+ private PeekReader(CharStream stream) {
+ this.source = stream ;
+ this.pushbackChars = new char[PUSHBACK_SIZE] ;
+ this.idxPushback = -1 ;
+
+ this.colNum = INIT_COL ;
+ this.lineNum = INIT_LINE ;
+ this.posn = 0 ;
+ }
+
+ public long getLineNum() {
+ return lineNum ;
+ }
+
+ public long getColNum() {
+ return colNum ;
+ }
+
+ public long getPosition() {
+ return posn ;
+ }
+
+ // ---- Do not access currChar except with peekChar/setCurrChar.
+ public final int peekChar() {
+ if ( idxPushback >= 0 )
+ return pushbackChars[idxPushback] ;
+
+ // If not started ... delayed initialization.
+ if ( currChar == UNSET )
+ init() ;
+ return currChar ;
+ }
+
+ // And the correct way to read the currChar is to call peekChar.
+ private final void setCurrChar(int ch) {
+ currChar = ch ;
+ }
+
+ public final int readChar() {
+ return nextChar() ;
+ }
+
+ /**
+ * push back a character : does not alter underlying position, line or
+ * column counts
+ */
+ public final void pushbackChar(int ch) {
+ unreadChar(ch) ;
+ }
+
+ // Reader operations
+ @Override
+ public final void close() throws IOException {
+ source.closeStream() ;
+ }
+
+ @Override
+ public final int read() throws IOException {
+ if ( eof() )
+ return EOF ;
+ int x = readChar() ;
+ return x ;
+ }
+
+ @Override
+ public final int read(char[] cbuf, int off, int len) throws IOException {
+ if ( eof() )
+ return EOF ;
+ // Note - we need to preserve line count
+ // Single char ops are reasonably efficient.
+ for (int i = 0; i < len; i++) {
+ int ch = readChar() ;
+ if ( ch == EOF )
+ return (i == 0) ? EOF : i ;
+ cbuf[i + off] = (char)ch ;
+ }
+ return len ;
+ }
+
+ public final boolean eof() {
+ return peekChar() == EOF ;
+ }
+
+ // ----------------
+ // The methods below are the only ones to manipulate the character buffers.
+ // Other methods may read the state of variables.
+
+ private final void unreadChar(int ch) {
+ // The push back buffer is in the order where [0] is the oldest.
+ // Does not alter the line number, column number or position count
+ // not does reading a pushback charcater.
+
+ if ( idxPushback >= pushbackChars.length ) {
+ // Enlarge pushback buffer.
+ char[] pushbackChars2 = new char[pushbackChars.length * 2] ;
+ System.arraycopy(pushbackChars, 0, pushbackChars2, 0, pushbackChars.length) ;
+ pushbackChars = pushbackChars2 ;
+ // throw new JenaException("Pushback buffer overflow") ;
+ }
+ if ( ch == EOF || ch == UNSET )
+ throw new RuntimeIOException("Illegal character to push back: " + ch) ;
+
+ idxPushback++ ;
+ pushbackChars[idxPushback] = (char)ch ;
+ }
+
+ private final void init() {
+ advanceAndSet() ;
+ if ( currChar == UNSET )
+ setCurrChar(EOF) ;
+ }
+
+ private final void advanceAndSet() {
+ int ch = source.advance() ;
+ setCurrChar(ch) ;
+ }
+
+ // Invariants.
+ // currChar is either chars[idx-1] or pushbackChars[idxPushback]
+
+ /**
+ * Return the next character, moving on one place and resetting the peek
+ * character
+ */
+ private final int nextChar() {
+ int ch = peekChar() ;
+
+ if ( ch == EOF )
+ return EOF ;
+
+ if ( idxPushback >= 0 ) {
+ char ch2 = pushbackChars[idxPushback] ;
+ idxPushback-- ;
+ return ch2 ;
+ }
+
+ posn++ ;
+
+ if ( ch == '\n' ) {
+ lineNum++ ;
+ colNum = INIT_COL ;
+ } else
+ colNum++ ;
+
+ advanceAndSet() ;
+ return ch ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/PrintUtils.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/PrintUtils.java b/jena-base/src/main/java/org/apache/jena/atlas/io/PrintUtils.java
new file mode 100644
index 0000000..14e4ed1
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/PrintUtils.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;
+
+
+public class PrintUtils
+{
+ // ---- Printable
+ public static String toString(Printable f)
+ {
+ IndentedLineBuffer buff = new IndentedLineBuffer() ;
+ f.output(buff) ;
+ return buff.toString() ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/Printable.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/Printable.java b/jena-base/src/main/java/org/apache/jena/atlas/io/Printable.java
new file mode 100644
index 0000000..69bd211
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/Printable.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.io;
+
+public interface Printable
+{
+ public void output(IndentedWriter out) ;
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/PrintableBase.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/PrintableBase.java b/jena-base/src/main/java/org/apache/jena/atlas/io/PrintableBase.java
new file mode 100644
index 0000000..89fbb24
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/PrintableBase.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+// Hmm - hardly worth it!
+public abstract class PrintableBase implements Printable
+{
+ @Override
+ public String toString() { return PrintUtils.toString(this) ; }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/StringWriterI.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/StringWriterI.java b/jena-base/src/main/java/org/apache/jena/atlas/io/StringWriterI.java
new file mode 100644
index 0000000..5fea4d3
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/StringWriterI.java
@@ -0,0 +1,30 @@
+/**
+ * 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 ;
+
+public class StringWriterI extends Writer2
+{
+ public StringWriterI()
+ {
+ super(new StringWriter()) ;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/io/Writer2.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/io/Writer2.java b/jena-base/src/main/java/org/apache/jena/atlas/io/Writer2.java
new file mode 100644
index 0000000..100ec6f
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/io/Writer2.java
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.atlas.io;
+
+import java.io.BufferedWriter ;
+import java.io.IOException ;
+import java.io.Writer ;
+
+import org.apache.jena.atlas.lib.Closeable ;
+
+/** A Writer, without the checked exceptions. */
+
+public class Writer2 extends AWriterBase implements AWriter, Closeable
+{
+ protected final Writer writer ;
+
+ public static Writer2 wrap(Writer writer)
+ {
+ if ( writer instanceof BufferedWriter )
+ return new Writer2(writer) ;
+ if ( writer instanceof BufferingWriter )
+ return new Writer2(writer) ;
+
+ writer = new BufferingWriter(writer) ;
+ return new Writer2(writer) ;
+ }
+
+ protected Writer2(Writer writer) { this.writer = writer ; }
+
+ @Override
+ public void print(char ch)
+ {
+ try { writer.write(ch) ; } catch (IOException ex) { IO.exception(ex) ; }
+ }
+
+ @Override
+ public void print(String string)
+ {
+ try { writer.write(string) ; } catch (IOException ex) { IO.exception(ex) ; }
+ }
+
+ @Override
+ public void print(char[] cbuf)
+ {
+ try { writer.write(cbuf) ; } catch (IOException ex) { IO.exception(ex) ; }
+ }
+
+ @Override
+ public void flush()
+ {
+ try { writer.flush() ; } catch (IOException ex) { IO.exception(ex) ; }
+ }
+
+ @Override
+ public void close()
+ {
+ try { writer.close() ; } catch (IOException ex) { IO.exception(ex) ; }
+ }
+
+ @Override
+ public void printf(String fmt, Object... args)
+ {
+ print(String.format(fmt, args)) ;
+ }
+
+ @Override
+ public void println(String obj)
+ {
+ print(obj) ; print("\n") ;
+ }
+
+ @Override
+ public void println()
+ {
+ print("\n") ;
+ }
+
+ @Override
+ public String toString() { return writer.toString() ; }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/AccString.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/AccString.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/AccString.java
new file mode 100644
index 0000000..7df3486
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/AccString.java
@@ -0,0 +1,66 @@
+/*
+ * 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.iterator;
+
+public class AccString<T> implements Accumulate<T, String>
+{
+ StringBuilder buffer = null ;
+ private String sep ;
+ private boolean first = true ;
+
+ // Fresh StringBuilder
+ public AccString(String sep) { this.sep = sep ; }
+ public AccString() { this(" ") ; }
+
+ @Override
+ public void accumulate(T item)
+ {
+ if ( ! first )
+ buffer.append(sep) ;
+ if ( item != null )
+ buffer.append(toString(item)) ;
+ else
+ buffer.append("<null>") ;
+ first = false ;
+ }
+
+ /** Make into a string */
+ protected String toString(T item)
+ {
+ return item.toString() ;
+ }
+
+ @Override
+ public String get()
+ {
+ return buffer.toString() ;
+ }
+
+ @Override
+ public void start()
+ {
+ // Resets on each use.
+ buffer = new StringBuilder() ;
+ first = true ;
+ }
+
+ @Override
+ public void finish() {}
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/Accumulate.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Accumulate.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Accumulate.java
new file mode 100644
index 0000000..2eef4fa
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Accumulate.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.iterator;
+
+public interface Accumulate <T, R>
+{
+ public void start() ;
+ public void accumulate(T item) ;
+ public void finish();
+ public R get() ;
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/Action.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Action.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Action.java
new file mode 100644
index 0000000..de2630e
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Action.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.iterator;
+
+public interface Action<T>
+{
+ public void apply(T item) ;
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/ActionCount.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/ActionCount.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/ActionCount.java
new file mode 100644
index 0000000..20f837c
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/ActionCount.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.iterator;
+
+
+public class ActionCount<T> implements Action<T>
+{
+ private long count = 0 ;
+
+ @Override
+ public void apply(T item)
+ { count++ ; }
+
+ public long getCount() { return count ; }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/ActionNothing.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/ActionNothing.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/ActionNothing.java
new file mode 100644
index 0000000..d05b279
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/ActionNothing.java
@@ -0,0 +1,26 @@
+/*
+ * 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.iterator;
+
+
+public class ActionNothing<T> implements Action<T>
+{
+ @Override
+ public void apply(T item) { }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/Filter.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Filter.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Filter.java
new file mode 100644
index 0000000..5d427c5
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Filter.java
@@ -0,0 +1,22 @@
+/*
+ * 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.iterator;
+
+@FunctionalInterface
+public interface Filter <T> { boolean accept(T item) ; }
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterDistinctAdjacent.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterDistinctAdjacent.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterDistinctAdjacent.java
new file mode 100644
index 0000000..3beb832
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterDistinctAdjacent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.iterator;
+
+import org.apache.jena.atlas.lib.Lib ;
+
+public class FilterDistinctAdjacent<T> implements Filter<T>
+{
+ private boolean isSet = false ;
+ private T last = null ;
+
+ public FilterDistinctAdjacent() { }
+
+ @Override
+ public boolean accept(T item)
+ {
+ if ( isSet && Lib.equal(last, item) )
+ return false ;
+ last = item ;
+ isSet = true ;
+ return true ;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterOutNulls.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterOutNulls.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterOutNulls.java
new file mode 100644
index 0000000..21cd471
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterOutNulls.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.atlas.iterator;
+
+
+public class FilterOutNulls<T> implements Filter<T>
+{
+ public FilterOutNulls() { }
+
+ @Override
+ public boolean accept(T item)
+ {
+ return item != null ;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterStack.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterStack.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterStack.java
new file mode 100644
index 0000000..479d93f
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterStack.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.iterator;
+
+/**
+ * Add a filter to a chain - the original filter is called after this new sub-filter.
+ */
+public abstract class FilterStack<T> implements Filter<T>
+{
+ private final Filter<T> other ;
+ private final boolean subFilterLast ;
+
+ public FilterStack(Filter<T> other) { this(other, false) ; }
+
+ public FilterStack(Filter<T> other, boolean callOldFilterFirst)
+ {
+ this.other = other ;
+ this.subFilterLast = callOldFilterFirst ;
+ }
+
+ @Override
+ public final boolean accept(T item)
+ {
+ if ( subFilterLast )
+ return acceptAdditionaOther(item) ;
+ else
+ return acceptOtherAdditional(item) ;
+ }
+
+ private boolean acceptAdditionaOther(T item)
+ {
+ if ( ! acceptAdditional(item) )
+ return false ;
+
+ if ( other != null && ! other.accept(item) )
+ return false ;
+
+ return true ;
+ }
+
+ private boolean acceptOtherAdditional(T item)
+ {
+ if ( other != null && ! other.accept(item) )
+ return false ;
+ return acceptAdditional(item) ;
+ }
+
+
+ /** Additional filter condition to apply */
+ public abstract boolean acceptAdditional(T item) ;
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterUnique.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterUnique.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterUnique.java
new file mode 100644
index 0000000..2b2a957
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/FilterUnique.java
@@ -0,0 +1,39 @@
+/*
+ * 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.iterator;
+
+import java.util.HashSet ;
+import java.util.Set ;
+
+public class FilterUnique<T> implements Filter<T>
+{
+ private Set<T> seen = new HashSet<>() ;
+
+ public FilterUnique() { }
+
+ @Override
+ public boolean accept(T item)
+ {
+ if ( seen.contains(item) )
+ return false ;
+ seen.add(item) ;
+ return true ;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/1320f8db/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
new file mode 100644
index 0000000..8aec2ea
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/iterator/Iter.java
@@ -0,0 +1,971 @@
+/*
+ * 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.iterator ;
+
+import java.io.PrintStream ;
+import java.util.* ;
+import java.util.stream.Stream ;
+import java.util.stream.StreamSupport ;
+
+import org.apache.jena.atlas.lib.ActionKeyValue ;
+import org.apache.jena.atlas.lib.Closeable ;
+import org.apache.jena.atlas.lib.Sink ;
+
+public class Iter<T> implements Iterator<T> {
+
+ // Most Iterable<T> operations have been removed - use streams instad.
+
+ public static <T> Stream<T> asStream(Iterator<T> iterator) {
+ // Why isn't there a JDK operation for iterator -> (sequential) stream?
+ return asStream(iterator, false);
+ }
+
+ public static <T> Stream<T> asStream(Iterator<T> iterator, boolean parallel) {
+ // Why isn't there a JDK operation for iterator -> (sequential) stream?
+ Iterable<T> iterable = () -> iterator;
+ return StreamSupport.stream(iterable.spliterator(), parallel);
+ }
+
+ // First part : the static function library.
+ // Often with both Iterator<? extends T> and Iterable<? extends T>
+
+ public static <T> Iterator<T> singleton(T item) {
+ return new SingletonIterator<>(item) ;
+ }
+
+ @SuppressWarnings("rawtypes")
+ private static final Iterator iter0 = new NullIterator() ;
+ @SuppressWarnings({"unchecked", "cast"})
+ public static <T> Iterator<T> nullIterator() { return (NullIterator<T>)iter0 ; }
+
+// public static <T> Iterator<T> nullIterator() {
+// return new NullIterator<T>() ;
+// }
+
+// public static <T> Set<T> toSet(Iterable<? extends T> stream) {
+// return toSet(stream.iterator()) ;
+// }
+
+ public static <T> Set<T> toSet(Iterator<? extends T> stream) {
+ Accumulate<T, Set<T>> action = new Accumulate<T, Set<T>>() {
+ private Set<T> acc = null ;
+
+ @Override
+ public void accumulate(T item) {
+ acc.add(item) ;
+ }
+
+ @Override
+ public Set<T> get() {
+ return acc ;
+ }
+
+ @Override
+ public void start() {
+ acc = new HashSet<>() ;
+ }
+
+ @Override
+ public void finish() {}
+ } ;
+ return reduce(stream, action) ;
+ }
+
+// public static <T> List<T> toList(Iterable<? extends T> stream) {
+// return toList(stream.iterator()) ;
+// }
+
+ public static <T> List<T> toList(Iterator<? extends T> stream) {
+ Accumulate<T, List<T>> action = new Accumulate<T, List<T>>() {
+ private List<T> acc = null ;
+
+ @Override
+ public void accumulate(T item) {
+ acc.add(item) ;
+ }
+
+ @Override
+ public List<T> get() {
+ return acc ;
+ }
+
+ @Override
+ public void start() {
+ acc = new ArrayList<>() ;
+ }
+
+ @Override
+ public void finish() {}
+ } ;
+ return reduce(stream, action) ;
+ }
+
+ /**
+ * Create another iterator without risk of concurrent modification
+ * exceptions. This materializes the input iterator.
+ */
+ public static <T> Iterator<T> iterator(Iterator<? extends T> iterator) {
+ List<T> x = Iter.toList(iterator) ;
+ return x.iterator() ;
+ }
+
+ public interface Folder<X, Y> {
+ Y eval(Y acc, X arg) ;
+ }
+
+ public static <T, R> R foldLeft(Iterable<? extends T> stream, Folder<T, R> function, R value) {
+ return foldLeft(stream.iterator(), function, value) ;
+ }
+
+ public static <T, R> R foldLeft(Iterator<? extends T> stream, Folder<T, R> function, R value) {
+ // Tail recursion, unwound
+ for (; stream.hasNext();) {
+ T item = stream.next() ;
+ value = function.eval(value, item) ;
+ }
+ return value ;
+ }
+
+ public static <T, R> R foldRight(Iterable<? extends T> stream, Folder<T, R> function, R value) {
+ return foldRight(stream.iterator(), function, value) ;
+ }
+
+ public static <T, R> R foldRight(Iterator<? extends T> stream, Folder<T, R> function, R value) {
+ // Recursive.
+ if ( !stream.hasNext() )
+ return value ;
+ T item = stream.next() ;
+ return function.eval(foldRight(stream, function, value), item) ;
+ }
+
+ // Note fold-left and fold-right
+ // http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29
+
+ // This reduce is fold-left (take first element, apply to rest of list)
+ // which copes with infinite lists.
+ // Fold-left starts by combining the first element, then moves on.
+
+ public static <T, R> R reduce(Iterable<? extends T> stream, Accumulate<T, R> aggregator) {
+ return reduce(stream.iterator(), aggregator) ;
+ }
+
+ public static <T, R> R reduce(Iterator<? extends T> stream, Accumulate<T, R> aggregator) {
+ aggregator.start() ;
+ for (; stream.hasNext();) {
+ T item = stream.next() ;
+ aggregator.accumulate(item) ;
+ }
+ aggregator.finish() ;
+ return aggregator.get() ;
+ }
+
+ // map without the results - do immediately.
+ // Also, apply with call in between?
+
+// public static <T> void apply(Iterable<? extends T> stream, Action<T> action) {
+// apply(stream.iterator(), action) ;
+// }
+
+ public static <T> void apply(Iterator<? extends T> stream, Action<T> action) {
+ for (; stream.hasNext();) {
+ T item = stream.next() ;
+ action.apply(item) ;
+ }
+ }
+
+ // -- Map specific apply. No results - do immediately.
+
+ public static <K, V> void apply(Map<K, V> map, ActionKeyValue<K, V> action) {
+ for (Map.Entry<K, V> entry : map.entrySet())
+ action.apply(entry.getKey(), entry.getValue()) ;
+ }
+
+ // ---- Filter
+
+// public static <T> Iterator<T> filter(Iterable<? extends T> stream, Filter<T> filter) {
+// return filter(stream.iterator(), filter) ;
+// }
+
+ public static <T> Iterator<T> filter(final Iterator<? extends T> stream, final Filter<T> filter) {
+ final Iterator<T> iter = new Iterator<T>() {
+
+ boolean finished = false ;
+ boolean slotOccupied = false ;
+ T slot ;
+
+ @Override
+ public boolean hasNext() {
+ if ( finished )
+ return false ;
+ while (!slotOccupied) {
+ if ( !stream.hasNext() ) {
+ finished = true ;
+ break ;
+ }
+ T nextItem = stream.next() ;
+ if ( filter.accept(nextItem) ) {
+ slot = nextItem ;
+ slotOccupied = true ;
+ break ;
+ }
+ }
+ return slotOccupied ;
+ }
+
+ @Override
+ public T next() {
+ if ( hasNext() ) {
+ slotOccupied = false ;
+ return slot ;
+ }
+ throw new NoSuchElementException("filter.next") ;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("filter.remove") ;
+ }
+ } ;
+
+ return iter ;
+ }
+
+ private static class InvertedFilter<T> implements Filter<T> {
+ public static <T> Filter<T> invert(Filter<T> filter) {
+ return new InvertedFilter<>(filter) ;
+ }
+ private Filter<T> baseFilter ;
+
+ private InvertedFilter(Filter<T> baseFilter) {
+ this.baseFilter = baseFilter ;
+ }
+
+ @Override
+ public boolean accept(T item) {
+ return !baseFilter.accept(item) ;
+ }
+ }
+
+// public static <T> Iterator<T> notFilter(Iterable<? extends T> stream, Filter<T> filter) {
+// return notFilter(stream.iterator(), filter) ;
+// }
+
+ public static <T> Iterator<T> notFilter(final Iterator<? extends T> stream, final Filter<T> filter) {
+ Filter<T> flippedFilter = InvertedFilter.invert(filter) ;
+ return filter(stream, flippedFilter) ;
+ }
+
+ // Filter-related
+
+// /**
+// * Return true if every element of stream passes the filter (reads the
+// * stream)
+// */
+// public static <T> boolean every(Iterable<? extends T> stream, Filter<T> filter) {
+// for (T item : stream)
+// if ( !filter.accept(item) )
+// return false ;
+// return true ;
+// }
+
+ /**
+ * Return true if every element of stream passes the filter (reads the
+ * stream until the first element not passing the filter)
+ */
+ public static <T> boolean every(Iterator<? extends T> stream, Filter<T> filter) {
+ for (; stream.hasNext();) {
+ T item = stream.next() ;
+ if ( !filter.accept(item) )
+ return false ;
+ }
+ return true ;
+ }
+
+// /**
+// * Return true if every element of stream passes the filter (reads the
+// * stream until the first element passing the filter)
+// */
+// public static <T> boolean some(Iterable<? extends T> stream, Filter<T> filter) {
+// for (T item : stream)
+// if ( filter.accept(item) )
+// return true ;
+// return false ;
+// }
+
+ /**
+ * Return true if one or more elements of stream passes the filter (reads
+ * the stream to first element passing the filter)
+ */
+ public static <T> boolean some(Iterator<? extends T> stream, Filter<T> filter) {
+ for (; stream.hasNext();) {
+ T item = stream.next() ;
+ if ( filter.accept(item) )
+ return true ;
+ }
+ return false ;
+ }
+
+ // ---- Map
+
+// public static <T, R> Iterator<R> map(Iterable<? extends T> stream, Transform<T, R> converter) {
+// return map(stream.iterator(), converter) ;
+// }
+
+ public static <T, R> Iterator<R> map(final Iterator<? extends T> stream, final Transform<T, R> converter) {
+ final Iterator<R> iter = new Iterator<R>() {
+ @Override
+ public boolean hasNext() {
+ return stream.hasNext() ;
+ }
+
+ @Override
+ public R next() {
+ return converter.convert(stream.next()) ;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("map.remove") ;
+ }
+ } ;
+ return iter ;
+ }
+
+ public static <T, R> List<R> map(List<? extends T> list, Transform<T, R> converter) {
+ return toList(map(list.iterator(), converter)) ;
+ }
+
+ /**
+ * Projects each element of a sequence to an Iterator<R> and flattens
+ * the resulting sequences into one sequence.
+ */
+ public static <T, R> Iterator<R> mapMany(final Iterator<? extends T> stream,
+ final Transform<? super T, Iterator<R>> converter) {
+ final Iterator<R> iter = new Iterator<R>() {
+
+ private Iterator<? extends R> it = null ; // Iterator for the
+ // current element of
+ // stream.
+
+ @Override
+ public boolean hasNext() {
+ if ( it != null && it.hasNext() )
+ // Element of the current iterator.
+ return true ;
+ // Start or current iterator has ended.
+ it = null ;
+
+ // Need to move to next non-empty iterator of the stream.
+ while (stream.hasNext()) {
+ it = converter.convert(stream.next()) ;
+ if ( it.hasNext() )
+ // There is something.
+ return true ;
+ }
+ it = null ;
+ // Stream ran out.
+ return false ;
+ }
+
+ @Override
+ public R next() {
+ if ( !hasNext() )
+ throw new NoSuchElementException() ;
+ // "it" is always left with something to yield if hashNext is
+ // true.
+ return it.next() ;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("mapMany.remove") ;
+ }
+ } ;
+
+ return iter ;
+ }
+
+// public static <T, R> Iterator<R> mapMany(Iterable<? extends T> stream, Transform<T, Iterator<R>> converter) {
+// return mapMany(stream.iterator(), converter) ;
+// }
+
+ public static <T, R> List<R> mapMany(List<? extends T> list, Transform<T, Iterator<R>> converter) {
+ return toList(mapMany(list.iterator(), converter)) ;
+ }
+
+// /**
+// * Apply an action to everything in stream, yielding a stream of the same
+// * items
+// */
+// public static <T> Iterator<T> operate(Iterable<? extends T> stream, Action<T> converter) {
+// return operate(stream.iterator(), converter) ;
+// }
+
+ /**
+ * Apply an action to everything in stream, yielding a stream of the same
+ * items
+ */
+ public static <T> Iterator<T> operate(final Iterator<? extends T> stream, final Action<T> action) {
+ final Iterator<T> iter = new Iterator<T>() {
+ @Override
+ public boolean hasNext() {
+ return stream.hasNext() ;
+ }
+
+ @Override
+ public T next() {
+ T t = stream.next() ;
+ action.apply(t) ;
+ return t ;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("operate.remove") ;
+ }
+ } ;
+ return iter ;
+ }
+
+ /** Print an iterator as it gets used - this adds a printing wrapper */
+ public static <T> Iterator<T> printWrapper(final Iterator<? extends T> stream) {
+ return Iter.printWrapper(System.out, stream) ;
+ }
+
+ /** Print an iterator as it gets used - this adds a printing wrapper */
+ public static <T> Iterator<T> printWrapper(final PrintStream out, final Iterator<? extends T> stream) {
+ Action<T> action = new Action<T>() {
+ @Override
+ public void apply(T item) {
+ out.println(item) ;
+ }
+ } ;
+ return Iter.operate(stream, action) ;
+ }
+
+// /** Join two iteratables
+// * If there, potentially, going to be many iterators, it is better to
+// * create an {@link IteratorConcat} explicitly and add each iterator.
+// */
+// public static <T> Iterator<T> append(Iterable<? extends T> iter1, Iterable<? extends T> iter2) {
+// return IteratorCons.create(iterator(iter1), iterator(iter2)) ;
+// }
+
+ /** Join two iterator
+ * If there, potentially, going to be many iterators, it is better to
+ * create an {@link IteratorConcat} explicitly and add each iterator.
+ */
+ public static <T> Iterator<T> append(Iterator<? extends T> iter1, Iterator<? extends T> iter2) {
+ return IteratorCons.create(iter1, iter2) ;
+ }
+
+// private static <T> Iterator<T> iterator(Iterable<T> iter) {
+// return (iter == null) ? null : iter.iterator() ;
+// }
+
+// public static <T> Iterator<T> distinct(Iterable<T> iter) {
+// return distinct(iter.iterator()) ;
+// }
+
+ public static <T> Iterator<T> distinct(Iterator<T> iter) {
+ return filter(iter, new FilterUnique<T>()) ;
+ }
+
+// /** Remove adjacent duplicates */
+// public static <T> Iterator<T> distinctAdjacent(Iterable<T> iter) {
+// return distinctAdjacent(iter.iterator()) ;
+// }
+
+ /** Remove adjacent duplicates */
+ public static <T> Iterator<T> distinctAdjacent(Iterator<T> iter) {
+ return filter(iter, new FilterDistinctAdjacent<T>()) ;
+ }
+
+// public static <T> Iterator<T> removeNulls(Iterable<T> iter) {
+// return filter(iter, new FilterOutNulls<T>()) ;
+// }
+
+ public static <T> Iterator<T> removeNulls(Iterator<T> iter) {
+ return filter(iter, new FilterOutNulls<T>()) ;
+ }
+
+ /** Take the first N elements of an iterator - stop early if too few */
+ public static <T> List<T> take(Iterator<T> iter, int N) {
+ iter = new IteratorN<>(iter, N) ;
+ List<T> x = new ArrayList<>(N) ;
+ for (; iter.hasNext();)
+ x.add(iter.next()) ;
+ return x ;
+ }
+
+ /** Iterator that only returns upto N items */
+ static class IteratorN<T> implements Iterator<T> {
+ private final Iterator<T> iter ;
+ private final int N ;
+ private int count ;
+
+ IteratorN(Iterator<T> iter, int N) {
+ this.iter = iter ;
+ this.N = N ;
+ this.count = 0 ;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if ( count >= N )
+ return false ;
+ return iter.hasNext() ;
+ }
+
+ @Override
+ public T next() {
+ if ( count >= N )
+ throw new NoSuchElementException() ;
+ T x = iter.next() ;
+ count++ ;
+ return x ;
+ }
+
+ @Override
+ public void remove() {
+ // But leave the count as-is.
+ iter.remove() ;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> Iterator<T> convert(Iterator<? > iterator) {
+ return (Iterator<T>)iterator ;
+ }
+
+// /**
+// * Count the iterable - many iterable objects have a .size() operation which
+// * should be used in preference to this explicit counting operation
+// */
+// public static <T> long count(Iterable<T> iterable) {
+// return count(iterable.iterator()) ;
+// }
+
+ /** Count the iterator (this is destructive on the iterator) */
+ public static <T> long count(Iterator<T> iterator) {
+ long x = 0 ;
+ while (iterator.hasNext()) {
+ iterator.next() ;
+ x++ ;
+ }
+ return x ;
+ // ActionCount<T> action = new ActionCount<T>() ;
+ // Iter.apply(iterator, action) ;
+ // return action.getCount() ;
+ }
+
+// // --- Consume the iterator.
+// /** Consume the iterable */
+// public static <T> void consume(Iterable<T> iterator) {
+// count(iterator) ;
+// }
+
+ /** Consume the iterator */
+ public static <T> void consume(Iterator<T> iterator) {
+ count(iterator) ;
+ }
+
+ // ---- String related helpers
+
+ public static <T> String asString(Iterable<T> stream) {
+ return asString(stream, new AccString<T>()) ;
+ }
+
+ public static <T> String asString(Iterator<T> stream) {
+ return asString(stream, new AccString<T>()) ;
+ }
+
+// public static <T> String asString(Iter<T> stream) {
+// return asString(stream, new AccString<T>()) ;
+// }
+//
+
+ public static <T> String asString(Iterable<T> stream, String sep) {
+ return asString(stream, new AccString<T>(sep)) ;
+ }
+
+ public static <T> String asString(Iterator<T> stream, String sep) {
+ return asString(stream, new AccString<T>(sep)) ;
+ }
+
+// public static <T> String asString(Iter<T> stream, String sep) {
+// return asString(stream.iterator(), new AccString<T>(sep)) ;
+// }
+//
+ public static <T> String asString(Iterable<T> stream, AccString<T> formatter) {
+ return asString(stream.iterator(), formatter) ;
+ }
+
+ public static <T> String asString(Iterator<T> stream, AccString<T> formatter) {
+ return reduce(stream, formatter) ;
+ }
+
+// public static <T> String asString(Iter<T> stream, AccString<T> formatter) {
+// return reduce(stream.iterator(), formatter) ;
+// }
+
+ // ----
+
+ public static <T> void close(Iterator<T> iter) {
+ if ( iter instanceof Closeable )
+ ((Closeable)iter).close() ;
+ }
+
+
+ /**
+ * Print an iterator to stdout, return a copy of the iterator. Printing
+ * occurs now. See {@link #debug} for an operation to print as the
+ * iterator is used.
+ */
+ public static <T> Iterator<T> log(Iterator<T> stream) {
+ return log(System.out, stream) ;
+ }
+
+ /**
+ * Print an iterator to stdout, return a copy of the iterator. Printing
+ * occurs when the returned iterator is used
+ */
+ public static <T> Iterator<T> log(final PrintStream out, Iterator<T> stream) {
+ Iterator<T> iter = debug(out, stream) ;
+ // And force it to run.
+ return Iter.toList(iter).iterator();
+ }
+
+ /**
+ * Print an iterator to stdout, return a copy of the iterator. Printing
+ * occurs when the iterator is used. See {@link #log} for
+ * an operation to print now.
+ */
+ public static <T> Iterator<T> debug(Iterator<T> stream) {
+ return debug(System.out, stream) ;
+ }
+
+ /**
+ * Print an iterator to stdout, return a copy of the iterator. Printing
+ * occurs when the returned iterator is used
+ */
+ public static <T> Iterator<T> debug(final PrintStream out, Iterator<T> stream) {
+ try {
+ Transform<T, T> x = new Transform<T, T>() {
+ @Override
+ public T convert(T item) {
+ out.println(item) ;
+ return item ;
+ }
+ } ;
+ return map(stream, x) ;
+ } finally { out.flush() ; }
+ }
+
+ /** Print an iterator (destructive) */
+ public static <T> void print(Iterator<T> stream) {
+ print(System.out, stream) ;
+ }
+
+ /** Print an iterator (destructive) */
+ public static <T> void print(final PrintStream out, Iterator<T> stream) {
+ Action<T> x = new Action<T>() {
+ @Override
+ public void apply(T item) {
+ out.println(item) ;
+ }
+
+ } ;
+ apply(stream, x) ;
+ }
+
+// /** Print an iterable */
+// public static <T> void print(PrintStream out, Iterable<T> iterable) {
+// print(out, iterable.iterator()) ;
+// }
+//
+// /** Print an iterable */
+// public static <T> void print(Iterable<T> iterable) {
+// print(iterable.iterator()) ;
+// }
+
+ /** Send the elements of the iterator to a sink - consumes the iterator */
+ public static <T> void sendToSink(Iterator<T> iter, Sink<T> sink) {
+ for (; iter.hasNext();) {
+ T thing = iter.next() ;
+ sink.send(thing) ;
+ }
+ sink.close() ;
+ }
+
+ /** Send the elements of the iterable to a sink */
+ public static <T> void sendToSink(Iterable<T> stream, Sink<T> sink) {
+ sendToSink(stream.iterator(), sink) ;
+ }
+
+ // ----
+ // Iter class part : factories
+
+ public static <T> Iter<T> iter(Iter<T> iter) {
+ return iter ;
+ }
+
+ // May not do what you expect. iter(int[]) is iter of one object (an int[])
+ // public static <T> Iter<T> iter(T...objects)
+ // { return Iter.iter(Arrays.asList(objects)) ; }
+
+ public static <T> Iter<T> iterSingleton(T x) {
+ return Iter.iter(SingletonIterator.create(x)) ;
+ }
+
+ public static <T> Iter<T> iter(Collection<T> collection) {
+ return Iter.iter(collection.iterator()) ;
+ }
+
+ public static <T> Iter<T> iter(Iterator<T> iterator) {
+ if ( iterator instanceof Iter<? > )
+ return (Iter<T>)iterator ;
+ return new Iter<>(iterator) ;
+ }
+
+// public static <T> Iter<T> iter(Iterable<T> iterable) {
+// if ( iterable instanceof Iter<? > )
+// return (Iter<T>)iterable ;
+// return new Iter<>(iterable.iterator()) ;
+// }
+
+ public static <T> Iter<T> singletonIter(T item) {
+ return iter(new SingletonIterator<>(item)) ;
+ }
+
+ public static <T> Iter<T> nullIter() {
+ return iter(new NullIterator<T>()) ;
+ }
+
+ /**
+ * Materialize an iterator, that is, force it to run now - useful in
+ * debugging
+ */
+ public static <T> Iterator<T> materialize(Iterator<T> iter) {
+ return Iter.toList(iter).iterator() ;
+ }
+
+ public static <T> Iter<T> concat(Iter<T> iter1, Iter<T> iter2) {
+ if ( iter1 == null )
+ return iter2 ;
+ if ( iter2 == null )
+ return iter1 ;
+ return iter1.append(iter2) ;
+ }
+
+ public static <T> Iterator<T> concat(Iterator<T> iter1, Iterator<T> iter2) {
+ if ( iter1 == null )
+ return iter2 ;
+ if ( iter2 == null )
+ return iter1 ;
+ return Iter.iter(iter1).append(Iter.iter(iter2)) ;
+ }
+
+ public static <T> T first(Iterator<T> iter, Filter<T> filter) {
+ for (int idx = 0; iter.hasNext(); idx++) {
+ T t = iter.next() ;
+ if ( filter.accept(t) )
+ return t ;
+ // return idx ;
+ }
+ return null ;
+ }
+
+ public static <T> T first(Collection<T> collection, Filter<T> filter) {
+ return first(collection.iterator(), filter) ;
+ }
+
+ public static <T> int firstIndex(Iterator<T> iter, Filter<T> filter) {
+ for (int idx = 0; iter.hasNext(); idx++) {
+ T t = iter.next() ;
+ if ( filter.accept(t) )
+ return idx ;
+ }
+ return -1 ;
+ }
+
+ public static <T> int firstIndex(Collection<T> collection, Filter<T> filter) {
+ return firstIndex(collection.iterator(), filter) ;
+ }
+
+ public static <T> T last(Iterator<T> iter, Filter<T> filter) {
+ T thing = null ;
+ for (int idx = 0; iter.hasNext(); idx++) {
+ T t = iter.next() ;
+ if ( filter.accept(t) )
+ thing = t ;
+ }
+ return thing ;
+ }
+
+ public static <T> T last(Collection<T> collection, Filter<T> filter) {
+ return last(collection.iterator(), filter) ;
+ }
+
+ public static <T> int lastIndex(Iterator<T> iter, Filter<T> filter) {
+ int location = -1 ;
+ for (int idx = 0; iter.hasNext(); idx++) {
+ T t = iter.next() ;
+ if ( filter.accept(t) )
+ location = idx ;
+ }
+ return location ;
+ }
+
+ public static <T> int lastIndex(Collection<T> collection, Filter<T> filter) {
+ return lastIndex(collection.iterator(), filter) ;
+ }
+
+ // ------------------------------------------------------
+ // The class.
+
+ private Iterator<T> iterator ;
+
+ private Iter(Iterator<T> iterator) {
+ this.iterator = iterator ;
+ }
+
+ public Set<T> toSet() {
+ return toSet(iterator) ;
+ }
+
+ public List<T> toList() {
+ return toList(iterator) ;
+ }
+
+ public void sendToSink(Sink<T> sink) {
+ sendToSink(iterator, sink) ;
+ }
+
+ public T first(Filter<T> filter) {
+ return first(iterator, filter) ;
+ }
+
+ public int firstIndex(Filter<T> filter) {
+ return firstIndex(iterator, filter) ;
+ }
+
+ public T last(Filter<T> filter) {
+ return last(iterator, filter) ;
+ }
+
+ public int lastIndex(Filter<T> filter) {
+ return lastIndex(iterator, filter) ;
+ }
+
+ public Iter<T> filter(Filter<T> filter) {
+ return iter(filter(iterator, filter)) ;
+ }
+
+ public boolean every(Filter<T> filter) {
+ return every(iterator, filter) ;
+ }
+
+ public boolean some(Filter<T> filter) {
+ return some(iterator, filter) ;
+ }
+
+ public Iter<T> removeNulls() {
+ return filter(new FilterOutNulls<T>()) ;
+ }
+
+ public <R> Iter<R> map(Transform<T, R> converter) {
+ return iter(map(iterator, converter)) ;
+ }
+
+ /**
+ * Apply an action to everything in the stream, yielding a stream of the
+ * same items
+ */
+ public Iter<T> operate(Action<T> action) {
+ return iter(operate(iterator, action)) ;
+ }
+
+ public <R> R reduce(Accumulate<T, R> aggregator) {
+ return reduce(iterator, aggregator) ;
+ }
+
+ public void apply(Action<T> action) {
+ apply(iterator, action) ;
+ }
+
+ /** Join on an iterator.
+ * If there are going to be many iterators, uit is better to create an {@link IteratorConcat}
+ * and <tt>.add</tt> each iterator. The overheads are much lower.
+ */
+ public Iter<T> append(Iterator<T> iter) {
+ return iter(IteratorCons.create(iterator, iter)) ;
+ }
+
+ /** Return an Iter that yields at most the first N items */
+ public Iter<T> take(int N) {
+ return Iter.iter(take(iterator, N)) ;
+ }
+
+ /** Count the iterator (this is destructive on the iterator) */
+ public long count() {
+ ActionCount<T> action = new ActionCount<>() ;
+ apply(action) ;
+ return action.getCount() ;
+ }
+
+ public String asString() {
+ return asString(iterator) ;
+ }
+
+ public String asString(String sep) {
+ return asString(iterator, sep) ;
+ }
+
+ public Iter<T> distinct() {
+ return iter((distinct(iterator()))) ;
+ }
+
+ public Iter<T> distinctAdjacent() {
+ return iter(distinctAdjacent(iterator())) ;
+ }
+
+ // ---- Iterable
+// @Override
+ public Iterator<T> iterator() {
+ return iterator ;
+ }
+
+ // ---- Iterator
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext() ;
+ }
+
+ @Override
+ public T next() {
+ return iterator.next() ;
+ }
+
+ @Override
+ public void remove() {
+ iterator.remove() ;
+ }
+}