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