You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/14 21:56:28 UTC
[03/57] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/IOGroovyMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/IOGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/IOGroovyMethods.java
new file mode 100644
index 0000000..f33d377
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/IOGroovyMethods.java
@@ -0,0 +1,1698 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import groovy.io.GroovyPrintWriter;
+import groovy.lang.Closure;
+import groovy.lang.StringWriterIOException;
+import groovy.lang.Writable;
+import groovy.transform.stc.ClosureParams;
+import groovy.transform.stc.FirstParam;
+import groovy.transform.stc.FromString;
+import groovy.transform.stc.PickFirstResolver;
+import groovy.transform.stc.SimpleType;
+import org.codehaus.groovy.runtime.callsite.BooleanClosureWrapper;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Formatter;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+import static org.codehaus.groovy.ast.tools.ClosureUtils.hasSingleStringArg;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.callClosureForLine;
+
+/**
+ * This class defines new groovy methods for Files, URLs, URIs which appear
+ * on normal JDK classes inside the Groovy environment.
+ * Static methods are used with the first parameter being the destination class,
+ * i.e. <code>public static long size(File self)</code>
+ * provides a <code>size()</code> method for <code>File</code>.
+ * <p>
+ * NOTE: While this class contains many 'public' static methods, it is
+ * primarily regarded as an internal class (its internal package name
+ * suggests this also). We value backwards compatibility of these
+ * methods when used within Groovy but value less backwards compatibility
+ * at the Java method call level. I.e. future versions of Groovy may
+ * remove or move a method call in this file but would normally
+ * aim to keep the method available from within Groovy.
+ */
+public class IOGroovyMethods extends DefaultGroovyMethodsSupport {
+
+ private static final Logger LOG = Logger.getLogger(IOGroovyMethods.class.getName());
+
+ /**
+ * Overloads the leftShift operator for Writer to allow an object to be written
+ * using Groovy's default representation for the object.
+ *
+ * @param self a Writer
+ * @param value an Object whose default representation will be written to the Writer
+ * @return the writer on which this operation was invoked
+ * @throws IOException if an I/O error occurs.
+ * @since 1.0
+ */
+ public static Writer leftShift(Writer self, Object value) throws IOException {
+ InvokerHelper.write(self, value);
+ return self;
+ }
+
+ /**
+ * Overloads the leftShift operator for Appendable to allow an object to be appended
+ * using Groovy's default representation for the object.
+ *
+ * @param self an Appendable
+ * @param value an Object whose default representation will be appended to the Appendable
+ * @return the Appendable on which this operation was invoked
+ * @throws IOException if an I/O error occurs.
+ * @since 2.1.0
+ */
+ public static Appendable leftShift(Appendable self, Object value) throws IOException {
+ InvokerHelper.append(self, value);
+ return self;
+ }
+
+ /**
+ * Invokes a Closure that uses a Formatter taking care of resource handling.
+ * A Formatter is created and passed to the Closure as its argument.
+ * After the Closure executes, the Formatter is flushed and closed releasing any
+ * associated resources.
+ *
+ * @param self an Appendable
+ * @param closure a 1-arg Closure which will be called with a Formatter as its argument
+ * @return the Appendable on which this operation was invoked
+ * @since 2.1.0
+ */
+ public static Appendable withFormatter(Appendable self, @ClosureParams(value=SimpleType.class, options="java.util.Formatter") Closure closure) {
+ Formatter formatter = new Formatter(self);
+ callWithFormatter(closure, formatter);
+ return self;
+ }
+
+ /**
+ * Invokes a Closure that uses a Formatter taking care of resource handling.
+ * A Formatter is created using the given Locale and passed to the Closure as its argument.
+ * After the Closure executes, the Formatter is flushed and closed releasing any
+ * associated resources.
+ *
+ * @param self an Appendable
+ * @param locale a Locale used when creating the Formatter
+ * @param closure a 1-arg Closure which will be called with a Formatter as its argument
+ * @return the Appendable on which this operation was invoked
+ * @since 2.1.0
+ */
+ public static Appendable withFormatter(Appendable self, Locale locale, @ClosureParams(value=SimpleType.class, options="java.util.Formatter") Closure closure) {
+ Formatter formatter = new Formatter(self, locale);
+ callWithFormatter(closure, formatter);
+ return self;
+ }
+
+ private static void callWithFormatter(Closure closure, Formatter formatter) {
+ try {
+ closure.call(formatter);
+ } finally {
+ formatter.flush();
+ formatter.close();
+ }
+ }
+
+ /**
+ * A helper method so that dynamic dispatch of the writer.write(object) method
+ * will always use the more efficient Writable.writeTo(writer) mechanism if the
+ * object implements the Writable interface.
+ *
+ * @param self a Writer
+ * @param writable an object implementing the Writable interface
+ * @throws IOException if an I/O error occurs.
+ * @since 1.0
+ */
+ public static void write(Writer self, Writable writable) throws IOException {
+ writable.writeTo(self);
+ }
+
+ /**
+ * Overloads the leftShift operator to provide an append mechanism to add values to a stream.
+ *
+ * @param self an OutputStream
+ * @param value a value to append
+ * @return a Writer
+ * @throws java.io.IOException if an I/O error occurs.
+ * @since 1.0
+ */
+
+ public static Writer leftShift(OutputStream self, Object value) throws IOException {
+ OutputStreamWriter writer = new FlushingStreamWriter(self);
+ leftShift(writer, value);
+ return writer;
+ }
+
+ /**
+ * Overloads the leftShift operator to add objects to an ObjectOutputStream.
+ *
+ * @param self an ObjectOutputStream
+ * @param value an object to write to the stream
+ * @throws IOException if an I/O error occurs.
+ * @since 1.5.0
+ */
+ public static void leftShift(ObjectOutputStream self, Object value) throws IOException {
+ self.writeObject(value);
+ }
+
+ /**
+ * Pipe an InputStream into an OutputStream for efficient stream copying.
+ *
+ * @param self stream on which to write
+ * @param in stream to read from
+ * @return the outputstream itself
+ * @throws IOException if an I/O error occurs.
+ * @since 1.0
+ */
+ public static OutputStream leftShift(OutputStream self, InputStream in) throws IOException {
+ byte[] buf = new byte[1024];
+ while (true) {
+ int count = in.read(buf, 0, buf.length);
+ if (count == -1) break;
+ if (count == 0) {
+ Thread.yield();
+ continue;
+ }
+ self.write(buf, 0, count);
+ }
+ self.flush();
+ return self;
+ }
+
+ /**
+ * Overloads the leftShift operator to provide an append mechanism to add bytes to a stream.
+ *
+ * @param self an OutputStream
+ * @param value a value to append
+ * @return an OutputStream
+ * @throws IOException if an I/O error occurs.
+ * @since 1.0
+ */
+ public static OutputStream leftShift(OutputStream self, byte[] value) throws IOException {
+ self.write(value);
+ self.flush();
+ return self;
+ }
+
+ /**
+ * Create an object output stream for this output stream.
+ *
+ * @param outputStream an output stream
+ * @return an object output stream
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.0
+ */
+ public static ObjectOutputStream newObjectOutputStream(OutputStream outputStream) throws IOException {
+ return new ObjectOutputStream(outputStream);
+ }
+
+ /**
+ * Create a new ObjectOutputStream for this output stream and then pass it to the
+ * closure. This method ensures the stream is closed after the closure
+ * returns.
+ *
+ * @param outputStream am output stream
+ * @param closure a closure
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #withStream(java.io.OutputStream, groovy.lang.Closure)
+ * @since 1.5.0
+ */
+ public static <T> T withObjectOutputStream(OutputStream outputStream, @ClosureParams(value=SimpleType.class, options="java.io.ObjectOutputStream") Closure<T> closure) throws IOException {
+ return withStream(newObjectOutputStream(outputStream), closure);
+ }
+
+ /**
+ * Create an object input stream for this input stream.
+ *
+ * @param inputStream an input stream
+ * @return an object input stream
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.0
+ */
+ public static ObjectInputStream newObjectInputStream(InputStream inputStream) throws IOException {
+ return new ObjectInputStream(inputStream);
+ }
+
+ /**
+ * Create an object input stream for this input stream using the given class loader.
+ *
+ * @param inputStream an input stream
+ * @param classLoader the class loader to use when loading the class
+ * @return an object input stream
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.0
+ */
+ public static ObjectInputStream newObjectInputStream(InputStream inputStream, final ClassLoader classLoader) throws IOException {
+ return new ObjectInputStream(inputStream) {
+ protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+ return Class.forName(desc.getName(), true, classLoader);
+
+ }
+ };
+ }
+
+ /**
+ * Iterates through the given object stream object by object. The
+ * ObjectInputStream is closed afterwards.
+ *
+ * @param ois an ObjectInputStream, closed after the operation
+ * @param closure a closure
+ * @throws IOException if an IOException occurs.
+ * @throws ClassNotFoundException if the class is not found.
+ * @since 1.0
+ */
+ public static void eachObject(ObjectInputStream ois, Closure closure) throws IOException, ClassNotFoundException {
+ try {
+ while (true) {
+ try {
+ Object obj = ois.readObject();
+ // we allow null objects in the object stream
+ closure.call(obj);
+ } catch (EOFException e) {
+ break;
+ }
+ }
+ InputStream temp = ois;
+ ois = null;
+ temp.close();
+ } finally {
+ closeWithWarning(ois);
+ }
+ }
+
+ /**
+ * Create a new ObjectInputStream for this file and pass it to the closure.
+ * This method ensures the stream is closed after the closure returns.
+ *
+ * @param inputStream an input stream
+ * @param closure a closure
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #withStream(java.io.InputStream, groovy.lang.Closure)
+ * @since 1.5.0
+ */
+ public static <T> T withObjectInputStream(InputStream inputStream, @ClosureParams(value=SimpleType.class, options="java.io.ObjectInputStream") Closure<T> closure) throws IOException {
+ return withStream(newObjectInputStream(inputStream), closure);
+ }
+
+ /**
+ * Create a new ObjectInputStream for this file and pass it to the closure.
+ * This method ensures the stream is closed after the closure returns.
+ *
+ * @param inputStream an input stream
+ * @param classLoader the class loader to use when loading the class
+ * @param closure a closure
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #withStream(java.io.InputStream, groovy.lang.Closure)
+ * @since 1.5.0
+ */
+ public static <T> T withObjectInputStream(InputStream inputStream, ClassLoader classLoader, @ClosureParams(value=SimpleType.class, options="java.io.ObjectInputStream") Closure<T> closure) throws IOException {
+ return withStream(newObjectInputStream(inputStream, classLoader), closure);
+ }
+
+ /**
+ * Iterates through this stream reading with the provided charset, passing each line to the
+ * given 1 or 2 arg closure. The stream is closed before this method returns.
+ *
+ * @param stream a stream
+ * @param charset opens the stream with a specified charset
+ * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1)
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #eachLine(java.io.InputStream, java.lang.String, int, groovy.lang.Closure)
+ * @since 1.5.5
+ */
+ public static <T> T eachLine(InputStream stream, String charset, @ClosureParams(value=FromString.class,options={"String","String,Integer"}) Closure<T> closure) throws IOException {
+ return eachLine(stream, charset, 1, closure);
+ }
+
+ /**
+ * Iterates through this stream reading with the provided charset, passing each line to
+ * the given 1 or 2 arg closure. The stream is closed after this method returns.
+ *
+ * @param stream a stream
+ * @param charset opens the stream with a specified charset
+ * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
+ * @param closure a closure (arg 1 is line, optional arg 2 is line number)
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
+ * @since 1.5.7
+ */
+ public static <T> T eachLine(InputStream stream, String charset, int firstLine, @ClosureParams(value=FromString.class,options={"String","String,Integer"}) Closure<T> closure) throws IOException {
+ return eachLine(new InputStreamReader(stream, charset), firstLine, closure);
+ }
+
+ /**
+ * Iterates through this stream, passing each line to the given 1 or 2 arg closure.
+ * The stream is closed before this method returns.
+ *
+ * @param stream a stream
+ * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1)
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #eachLine(java.io.InputStream, int, groovy.lang.Closure)
+ * @since 1.5.6
+ */
+ public static <T> T eachLine(InputStream stream, @ClosureParams(value=FromString.class,options={"String","String,Integer"}) Closure<T> closure) throws IOException {
+ return eachLine(stream, 1, closure);
+ }
+
+ /**
+ * Iterates through this stream, passing each line to the given 1 or 2 arg closure.
+ * The stream is closed before this method returns.
+ *
+ * @param stream a stream
+ * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
+ * @param closure a closure (arg 1 is line, optional arg 2 is line number)
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
+ * @since 1.5.7
+ */
+ public static <T> T eachLine(InputStream stream, int firstLine, @ClosureParams(value=FromString.class,options={"String","String,Integer"}) Closure<T> closure) throws IOException {
+ return eachLine(new InputStreamReader(stream), firstLine, closure);
+ }
+
+ /**
+ * Iterates through the given reader line by line. Each line is passed to the
+ * given 1 or 2 arg closure. If the closure has two arguments, the line count is passed
+ * as the second argument. The Reader is closed before this method returns.
+ *
+ * @param self a Reader, closed after the method returns
+ * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1)
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
+ * @since 1.5.6
+ */
+ public static <T> T eachLine(Reader self, @ClosureParams(value=FromString.class,options={"String","String,Integer"}) Closure<T> closure) throws IOException {
+ return eachLine(self, 1, closure);
+ }
+
+ /**
+ * Iterates through the given reader line by line. Each line is passed to the
+ * given 1 or 2 arg closure. If the closure has two arguments, the line count is passed
+ * as the second argument. The Reader is closed before this method returns.
+ *
+ * @param self a Reader, closed after the method returns
+ * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
+ * @param closure a closure which will be passed each line (or for 2 arg closures the line and line count)
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.7
+ */
+ public static <T> T eachLine(Reader self, int firstLine, @ClosureParams(value=FromString.class,options={"String","String,Integer"}) Closure<T> closure) throws IOException {
+ BufferedReader br;
+ int count = firstLine;
+ T result = null;
+
+ if (self instanceof BufferedReader)
+ br = (BufferedReader) self;
+ else
+ br = new BufferedReader(self);
+
+ try {
+ while (true) {
+ String line = br.readLine();
+ if (line == null) {
+ break;
+ } else {
+ result = callClosureForLine(closure, line, count);
+ count++;
+ }
+ }
+ Reader temp = self;
+ self = null;
+ temp.close();
+ return result;
+ } finally {
+ closeWithWarning(self);
+ closeWithWarning(br);
+ }
+ }
+
+ /**
+ * Iterates through the given reader line by line, splitting each line using
+ * the given regex separator. For each line, the given closure is called with
+ * a single parameter being the list of strings computed by splitting the line
+ * around matches of the given regular expression. The Reader is closed afterwards.
+ * <p>
+ * Here is an example:
+ * <pre>
+ * def s = 'The 3 quick\nbrown 4 fox'
+ * def result = ''
+ * new StringReader(s).splitEachLine(/\d/){ parts ->
+ * result += "${parts[0]}_${parts[1]}|"
+ * }
+ * assert result == 'The _ quick|brown _ fox|'
+ * </pre>
+ *
+ * @param self a Reader, closed after the method returns
+ * @param regex the delimiting regular expression
+ * @param closure a closure
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @throws java.util.regex.PatternSyntaxException
+ * if the regular expression's syntax is invalid
+ * @see java.lang.String#split(java.lang.String)
+ * @since 1.5.5
+ */
+ public static <T> T splitEachLine(Reader self, String regex, @ClosureParams(value=FromString.class,options={"List<String>","String[]"},conflictResolutionStrategy=PickFirstResolver.class) Closure<T> closure) throws IOException {
+ return splitEachLine(self, Pattern.compile(regex), closure);
+ }
+
+ /**
+ * Iterates through the given reader line by line, splitting each line using
+ * the given regex separator Pattern. For each line, the given closure is called with
+ * a single parameter being the list of strings computed by splitting the line
+ * around matches of the given regular expression. The Reader is closed afterwards.
+ * <p>
+ * Here is an example:
+ * <pre>
+ * def s = 'The 3 quick\nbrown 4 fox'
+ * def result = ''
+ * new StringReader(s).splitEachLine(~/\d/){ parts ->
+ * result += "${parts[0]}_${parts[1]}|"
+ * }
+ * assert result == 'The _ quick|brown _ fox|'
+ * </pre>
+ *
+ * @param self a Reader, closed after the method returns
+ * @param pattern the regular expression Pattern for the delimiter
+ * @param closure a closure
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @throws java.util.regex.PatternSyntaxException
+ * if the regular expression's syntax is invalid
+ * @see java.lang.String#split(java.lang.String)
+ * @since 1.6.8
+ */
+ public static <T> T splitEachLine(Reader self, Pattern pattern, @ClosureParams(value=FromString.class,options={"List<String>","String[]"},conflictResolutionStrategy=PickFirstResolver.class) Closure<T> closure) throws IOException {
+ BufferedReader br;
+ T result = null;
+
+ if (self instanceof BufferedReader)
+ br = (BufferedReader) self;
+ else
+ br = new BufferedReader(self);
+
+ try {
+ while (true) {
+ String line = br.readLine();
+ if (line == null) {
+ break;
+ } else {
+ List vals = Arrays.asList(pattern.split(line));
+ result = closure.call(hasSingleStringArg(closure) ? vals.get(0) : vals);
+ }
+ }
+ Reader temp = self;
+ self = null;
+ temp.close();
+ return result;
+ } finally {
+ closeWithWarning(self);
+ closeWithWarning(br);
+ }
+ }
+
+ /**
+ * Iterates through the given InputStream line by line using the specified
+ * encoding, splitting each line using the given separator. The list of tokens
+ * for each line is then passed to the given closure. Finally, the stream
+ * is closed.
+ *
+ * @param stream an InputStream
+ * @param regex the delimiting regular expression
+ * @param charset opens the stream with a specified charset
+ * @param closure a closure
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @throws java.util.regex.PatternSyntaxException
+ * if the regular expression's syntax is invalid
+ * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure)
+ * @since 1.5.5
+ */
+ public static <T> T splitEachLine(InputStream stream, String regex, String charset, @ClosureParams(value=FromString.class,options={"List<String>","String[]"},conflictResolutionStrategy=PickFirstResolver.class) Closure<T> closure) throws IOException {
+ return splitEachLine(new BufferedReader(new InputStreamReader(stream, charset)), regex, closure);
+ }
+
+ /**
+ * Iterates through the given InputStream line by line using the specified
+ * encoding, splitting each line using the given separator Pattern. The list of tokens
+ * for each line is then passed to the given closure. Finally, the stream
+ * is closed.
+ *
+ * @param stream an InputStream
+ * @param pattern the regular expression Pattern for the delimiter
+ * @param charset opens the stream with a specified charset
+ * @param closure a closure
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure)
+ * @since 1.6.8
+ */
+ public static <T> T splitEachLine(InputStream stream, Pattern pattern, String charset, @ClosureParams(value=FromString.class,options={"List<String>","String[]"},conflictResolutionStrategy=PickFirstResolver.class) Closure<T> closure) throws IOException {
+ return splitEachLine(new BufferedReader(new InputStreamReader(stream, charset)), pattern, closure);
+ }
+
+ /**
+ * Iterates through the given InputStream line by line, splitting each line using
+ * the given separator. The list of tokens for each line is then passed to
+ * the given closure. The stream is closed before the method returns.
+ *
+ * @param stream an InputStream
+ * @param regex the delimiting regular expression
+ * @param closure a closure
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @throws java.util.regex.PatternSyntaxException
+ * if the regular expression's syntax is invalid
+ * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure)
+ * @since 1.5.6
+ */
+ public static <T> T splitEachLine(InputStream stream, String regex, @ClosureParams(value=FromString.class,options={"List<String>","String[]"},conflictResolutionStrategy=PickFirstResolver.class) Closure<T> closure) throws IOException {
+ return splitEachLine(new BufferedReader(new InputStreamReader(stream)), regex, closure);
+ }
+
+ /**
+ * Iterates through the given InputStream line by line, splitting each line using
+ * the given separator Pattern. The list of tokens for each line is then passed to
+ * the given closure. The stream is closed before the method returns.
+ *
+ * @param stream an InputStream
+ * @param pattern the regular expression Pattern for the delimiter
+ * @param closure a closure
+ * @return the last value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure)
+ * @since 1.6.8
+ */
+ public static <T> T splitEachLine(InputStream stream, Pattern pattern, @ClosureParams(value=FromString.class,options={"List<String>","String[]"},conflictResolutionStrategy=PickFirstResolver.class) Closure<T> closure) throws IOException {
+ return splitEachLine(new BufferedReader(new InputStreamReader(stream)), pattern, closure);
+ }
+
+ /**
+ * Read a single, whole line from the given Reader.
+ *
+ * @param self a Reader
+ * @return a line
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static String readLine(Reader self) throws IOException {
+ if (self instanceof BufferedReader) {
+ BufferedReader br = (BufferedReader) self;
+ return br.readLine();
+ }
+ if (self.markSupported()) {
+ return readLineFromReaderWithMark(self);
+ }
+ return readLineFromReaderWithoutMark(self);
+ }
+
+ private static int charBufferSize = 4096; // half the default stream buffer size
+ private static int expectedLineLength = 160; // double the default line length
+ private static int EOF = -1; // End Of File
+
+ /*
+ * This method tries to read subsequent buffers from the reader using a mark
+ */
+ private static String readLineFromReaderWithMark(final Reader input)
+ throws IOException {
+ char[] cbuf = new char[charBufferSize];
+ try {
+ input.mark(charBufferSize);
+ } catch (IOException e) {
+ // this should never happen
+ LOG.warning("Caught exception setting mark on supporting reader: " + e);
+ // fallback
+ return readLineFromReaderWithoutMark(input);
+ }
+
+ // could be changed into do..while, but then
+ // we might create an additional StringBuilder
+ // instance at the end of the stream
+ int count = input.read(cbuf);
+ if (count == EOF) // we are at the end of the input data
+ return null;
+
+ StringBuilder line = new StringBuilder(expectedLineLength);
+ // now work on the buffer(s)
+ int ls = lineSeparatorIndex(cbuf, count);
+ while (ls == -1) {
+ line.append(cbuf, 0, count);
+ count = input.read(cbuf);
+ if (count == EOF) {
+ // we are at the end of the input data
+ return line.toString();
+ }
+ ls = lineSeparatorIndex(cbuf, count);
+ }
+ line.append(cbuf, 0, ls);
+
+ // correct ls if we have \r\n
+ int skipLS = 1;
+ if (ls + 1 < count) {
+ // we are not at the end of the buffer
+ if (cbuf[ls] == '\r' && cbuf[ls + 1] == '\n') {
+ skipLS++;
+ }
+ } else {
+ if (cbuf[ls] == '\r' && input.read() == '\n') {
+ skipLS++;
+ }
+ }
+
+ //reset() and skip over last linesep
+ input.reset();
+ input.skip(line.length() + skipLS);
+ return line.toString();
+ }
+
+ /*
+ * This method reads without a buffer.
+ * It returns too many empty lines if \r\n combinations
+ * are used. Nothing can be done because we can't push
+ * back the character we have just read.
+ */
+ private static String readLineFromReaderWithoutMark(Reader input)
+ throws IOException {
+
+ int c = input.read();
+ if (c == -1)
+ return null;
+ StringBuilder line = new StringBuilder(expectedLineLength);
+
+ while (c != EOF && c != '\n' && c != '\r') {
+ char ch = (char) c;
+ line.append(ch);
+ c = input.read();
+ }
+ return line.toString();
+ }
+
+ /*
+ * searches for \n or \r
+ * Returns -1 if not found.
+ */
+ private static int lineSeparatorIndex(char[] array, int length) {
+ for (int k = 0; k < length; k++) {
+ if (isLineSeparator(array[k])) {
+ return k;
+ }
+ }
+ return -1;
+ }
+
+ /*
+ * true if either \n or \r
+ */
+ private static boolean isLineSeparator(char c) {
+ return c == '\n' || c == '\r';
+ }
+
+ /**
+ * Reads the stream into a list, with one element for each line.
+ *
+ * @param stream a stream
+ * @return a List of lines
+ * @throws IOException if an IOException occurs.
+ * @see #readLines(java.io.Reader)
+ * @since 1.0
+ */
+ public static List<String> readLines(InputStream stream) throws IOException {
+ return readLines(newReader(stream));
+ }
+
+ /**
+ * Reads the stream into a list, with one element for each line.
+ *
+ * @param stream a stream
+ * @param charset opens the stream with a specified charset
+ * @return a List of lines
+ * @throws IOException if an IOException occurs.
+ * @see #readLines(java.io.Reader)
+ * @since 1.6.8
+ */
+ public static List<String> readLines(InputStream stream, String charset) throws IOException {
+ return readLines(newReader(stream, charset));
+ }
+
+ /**
+ * Reads the reader into a list of Strings, with one entry for each line.
+ * The reader is closed before this method returns.
+ *
+ * @param reader a Reader
+ * @return a List of lines
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static List<String> readLines(Reader reader) throws IOException {
+ IteratorClosureAdapter<String> closure = new IteratorClosureAdapter<String>(reader);
+ eachLine(reader, closure);
+ return closure.asList();
+ }
+
+ /**
+ * Read the content of this InputStream and return it as a String.
+ * The stream is closed before this method returns.
+ *
+ * @param is an input stream
+ * @return the text from that URL
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static String getText(InputStream is) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ return getText(reader);
+ }
+
+ /**
+ * Read the content of this InputStream using specified charset and return
+ * it as a String. The stream is closed before this method returns.
+ *
+ * @param is an input stream
+ * @param charset opens the stream with a specified charset
+ * @return the text from that URL
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static String getText(InputStream is, String charset) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
+ return getText(reader);
+ }
+
+ /**
+ * Read the content of the Reader and return it as a String. The reader
+ * is closed before this method returns.
+ *
+ * @param reader a Reader whose content we want to read
+ * @return a String containing the content of the buffered reader
+ * @throws IOException if an IOException occurs.
+ * @see #getText(java.io.BufferedReader)
+ * @since 1.0
+ */
+ public static String getText(Reader reader) throws IOException {
+ BufferedReader bufferedReader = new BufferedReader(reader);
+ return getText(bufferedReader);
+ }
+
+ /**
+ * Read the content of the BufferedReader and return it as a String.
+ * The BufferedReader is closed afterwards.
+ *
+ * @param reader a BufferedReader whose content we want to read
+ * @return a String containing the content of the buffered reader
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static String getText(BufferedReader reader) throws IOException {
+ StringBuilder answer = new StringBuilder();
+ // reading the content of the file within a char buffer
+ // allow to keep the correct line endings
+ char[] charBuffer = new char[8192];
+ int nbCharRead /* = 0*/;
+ try {
+ while ((nbCharRead = reader.read(charBuffer)) != -1) {
+ // appends buffer
+ answer.append(charBuffer, 0, nbCharRead);
+ }
+ Reader temp = reader;
+ reader = null;
+ temp.close();
+ } finally {
+ closeWithWarning(reader);
+ }
+ return answer.toString();
+ }
+
+ /**
+ * Read the content of this InputStream and return it as a byte[].
+ * The stream is closed before this method returns.
+ *
+ * @param is an input stream
+ * @return the byte[] from that InputStream
+ * @throws IOException if an IOException occurs.
+ * @since 1.7.1
+ */
+ public static byte[] getBytes(InputStream is) throws IOException {
+ ByteArrayOutputStream answer = new ByteArrayOutputStream();
+ // reading the content of the file within a byte buffer
+ byte[] byteBuffer = new byte[8192];
+ int nbByteRead /* = 0*/;
+ try {
+ while ((nbByteRead = is.read(byteBuffer)) != -1) {
+ // appends buffer
+ answer.write(byteBuffer, 0, nbByteRead);
+ }
+ } finally {
+ closeWithWarning(is);
+ }
+ return answer.toByteArray();
+ }
+
+ /**
+ * Write the byte[] to the output stream.
+ * The stream is closed before this method returns.
+ *
+ * @param os an output stream
+ * @param bytes the byte[] to write to the output stream
+ * @throws IOException if an IOException occurs.
+ * @since 1.7.1
+ */
+ public static void setBytes(OutputStream os, byte[] bytes) throws IOException {
+ try {
+ os.write(bytes);
+ } finally {
+ closeWithWarning(os);
+ }
+ }
+
+ /**
+ * Write the text and append a newline (using the platform's line-ending).
+ *
+ * @param writer a BufferedWriter
+ * @param line the line to write
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static void writeLine(BufferedWriter writer, String line) throws IOException {
+ writer.write(line);
+ writer.newLine();
+ }
+
+ /**
+ * Creates an iterator which will traverse through the reader a line at a time.
+ *
+ * @param self a Reader object
+ * @return an Iterator for the Reader
+ * @see java.io.BufferedReader#readLine()
+ * @since 1.5.0
+ */
+ public static Iterator<String> iterator(Reader self) {
+ final BufferedReader bufferedReader;
+ if (self instanceof BufferedReader)
+ bufferedReader = (BufferedReader) self;
+ else
+ bufferedReader = new BufferedReader(self);
+ return new Iterator<String>() {
+ String nextVal /* = null */;
+ boolean nextMustRead = true;
+ boolean hasNext = true;
+
+ public boolean hasNext() {
+ if (nextMustRead && hasNext) {
+ try {
+ nextVal = readNext();
+ nextMustRead = false;
+ } catch (IOException e) {
+ hasNext = false;
+ }
+ }
+ return hasNext;
+ }
+
+ public String next() {
+ String retval = null;
+ if (nextMustRead) {
+ try {
+ retval = readNext();
+ } catch (IOException e) {
+ hasNext = false;
+ }
+ } else
+ retval = nextVal;
+ nextMustRead = true;
+ return retval;
+ }
+
+ private String readNext() throws IOException {
+ String nv = bufferedReader.readLine();
+ if (nv == null)
+ hasNext = false;
+ return nv;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Cannot remove() from a Reader Iterator");
+ }
+ };
+ }
+
+ /**
+ * Standard iterator for a input stream which iterates through the stream
+ * content in a byte-based fashion.
+ *
+ * @param self an InputStream object
+ * @return an Iterator for the InputStream
+ * @since 1.5.0
+ */
+ public static Iterator<Byte> iterator(InputStream self) {
+ return iterator(new DataInputStream(self));
+ }
+
+ /**
+ * Standard iterator for a data input stream which iterates through the
+ * stream content a Byte at a time.
+ *
+ * @param self a DataInputStream object
+ * @return an Iterator for the DataInputStream
+ * @since 1.5.0
+ */
+ public static Iterator<Byte> iterator(final DataInputStream self) {
+ return new Iterator<Byte>() {
+ Byte nextVal;
+ boolean nextMustRead = true;
+ boolean hasNext = true;
+
+ public boolean hasNext() {
+ if (nextMustRead && hasNext) {
+ try {
+ nextVal = self.readByte();
+ nextMustRead = false;
+ } catch (IOException e) {
+ hasNext = false;
+ }
+ }
+ return hasNext;
+ }
+
+ public Byte next() {
+ Byte retval = null;
+ if (nextMustRead) {
+ try {
+ retval = self.readByte();
+ } catch (IOException e) {
+ hasNext = false;
+ }
+ } else
+ retval = nextVal;
+ nextMustRead = true;
+ return retval;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Cannot remove() from a DataInputStream Iterator");
+ }
+ };
+ }
+
+ /**
+ * Creates a reader for this input stream.
+ *
+ * @param self an input stream
+ * @return a reader
+ * @since 1.0
+ */
+ public static BufferedReader newReader(InputStream self) {
+ return new BufferedReader(new InputStreamReader(self));
+ }
+
+ /**
+ * Creates a reader for this input stream, using the specified
+ * charset as the encoding.
+ *
+ * @param self an input stream
+ * @param charset the charset for this input stream
+ * @return a reader
+ * @throws UnsupportedEncodingException if the encoding specified is not supported
+ * @since 1.6.0
+ */
+ public static BufferedReader newReader(InputStream self, String charset) throws UnsupportedEncodingException {
+ return new BufferedReader(new InputStreamReader(self, charset));
+ }
+
+ /**
+ * Create a new PrintWriter for this Writer.
+ *
+ * @param writer a Writer
+ * @return a PrintWriter
+ * @since 1.6.0
+ */
+ public static PrintWriter newPrintWriter(Writer writer) {
+ return new GroovyPrintWriter(writer);
+ }
+
+ /**
+ * Create a new PrintWriter for this OutputStream.
+ *
+ * @param stream an OutputStream
+ * @return a PrintWriter
+ * @since 2.2.0
+ */
+ public static PrintWriter newPrintWriter(OutputStream stream) {
+ return new GroovyPrintWriter(stream);
+ }
+
+ /**
+ * Create a new PrintWriter for this Writer. The writer is passed to the
+ * closure, and will be closed before this method returns.
+ *
+ * @param writer a writer
+ * @param closure the closure to invoke with the PrintWriter
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @since 1.6.0
+ */
+ public static <T> T withPrintWriter(Writer writer, @ClosureParams(value=SimpleType.class, options="java.io.PrintWriter") Closure<T> closure) throws IOException {
+ return withWriter(newPrintWriter(writer), closure);
+ }
+
+ /**
+ * Create a new PrintWriter for this OutputStream. The writer is passed to the
+ * closure, and will be closed before this method returns.
+ *
+ * @param stream an OutputStream
+ * @param closure the closure to invoke with the PrintWriter
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @since 2.2.0
+ */
+ public static <T> T withPrintWriter(OutputStream stream, @ClosureParams(value=SimpleType.class, options="java.io.PrintWriter") Closure<T> closure) throws IOException {
+ return withWriter(newPrintWriter(stream), closure);
+ }
+
+ /**
+ * Allows this writer to be used within the closure, ensuring that it
+ * is flushed and closed before this method returns.
+ *
+ * @param writer the writer which is used and then closed
+ * @param closure the closure that the writer is passed into
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.2
+ */
+ public static <T> T withWriter(Writer writer, @ClosureParams(FirstParam.class) Closure<T> closure) throws IOException {
+ try {
+ T result = closure.call(writer);
+
+ try {
+ writer.flush();
+ } catch (IOException e) {
+ // try to continue even in case of error
+ }
+ Writer temp = writer;
+ writer = null;
+ temp.close();
+ return result;
+ } finally {
+ closeWithWarning(writer);
+ }
+ }
+
+ /**
+ * Allows this reader to be used within the closure, ensuring that it
+ * is closed before this method returns.
+ *
+ * @param reader the reader which is used and then closed
+ * @param closure the closure that the writer is passed into
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.2
+ */
+ public static <T> T withReader(Reader reader, @ClosureParams(FirstParam.class) Closure<T> closure) throws IOException {
+ try {
+ T result = closure.call(reader);
+
+ Reader temp = reader;
+ reader = null;
+ temp.close();
+
+ return result;
+ } finally {
+ closeWithWarning(reader);
+ }
+ }
+
+ /**
+ * Allows this input stream to be used within the closure, ensuring that it
+ * is flushed and closed before this method returns.
+ *
+ * @param stream the stream which is used and then closed
+ * @param closure the closure that the stream is passed into
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.2
+ */
+ public static <T, U extends InputStream> T withStream(U stream, @ClosureParams(value=FirstParam.class) Closure<T> closure) throws IOException {
+ try {
+ T result = closure.call(stream);
+
+ InputStream temp = stream;
+ stream = null;
+ temp.close();
+
+ return result;
+ } finally {
+ closeWithWarning(stream);
+ }
+ }
+
+ /**
+ * Helper method to create a new Reader for a stream and then
+ * passes it into the closure. The reader (and this stream) is closed after
+ * the closure returns.
+ *
+ * @param in a stream
+ * @param closure the closure to invoke with the InputStream
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see java.io.InputStreamReader
+ * @since 1.5.2
+ */
+ public static <T> T withReader(InputStream in, @ClosureParams(value=SimpleType.class, options="java.io.Reader") Closure<T> closure) throws IOException {
+ return withReader(new InputStreamReader(in), closure);
+ }
+
+ /**
+ * Helper method to create a new Reader for a stream and then
+ * passes it into the closure. The reader (and this stream) is closed after
+ * the closure returns.
+ *
+ * @param in a stream
+ * @param charset the charset used to decode the stream
+ * @param closure the closure to invoke with the reader
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see java.io.InputStreamReader
+ * @since 1.5.6
+ */
+ public static <T> T withReader(InputStream in, String charset, @ClosureParams(value=SimpleType.class, options="java.io.Reader") Closure<T> closure) throws IOException {
+ return withReader(new InputStreamReader(in, charset), closure);
+ }
+
+ /**
+ * Creates a writer from this stream, passing it to the given closure.
+ * This method ensures the stream is closed after the closure returns.
+ *
+ * @param stream the stream which is used and then closed
+ * @param closure the closure that the writer is passed into
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #withWriter(java.io.Writer, groovy.lang.Closure)
+ * @since 1.5.2
+ */
+ public static <T> T withWriter(OutputStream stream, @ClosureParams(value=SimpleType.class, options="java.io.Writer") Closure<T> closure) throws IOException {
+ return withWriter(new OutputStreamWriter(stream), closure);
+ }
+
+ /**
+ * Creates a writer for this stream.
+ *
+ * @param stream the stream which is used and then closed
+ * @return the newly created Writer
+ * @since 2.2.0
+ */
+ public static Writer newWriter(OutputStream stream) {
+ return new OutputStreamWriter(stream);
+ }
+
+ /**
+ * Creates a writer from this stream, passing it to the given closure.
+ * This method ensures the stream is closed after the closure returns.
+ *
+ * @param stream the stream which is used and then closed
+ * @param charset the charset used
+ * @param closure the closure that the writer is passed into
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @see #withWriter(java.io.Writer, groovy.lang.Closure)
+ * @since 1.5.2
+ */
+ public static <T> T withWriter(OutputStream stream, String charset, @ClosureParams(value=SimpleType.class, options="java.io.Writer") Closure<T> closure) throws IOException {
+ return withWriter(new OutputStreamWriter(stream, charset), closure);
+ }
+
+ /**
+ * Creates a writer for this stream using the given charset.
+ *
+ * @param stream the stream which is used and then closed
+ * @param charset the charset used
+ * @return the newly created Writer
+ * @throws UnsupportedEncodingException if an encoding exception occurs.
+ * @since 2.2.0
+ */
+ public static Writer newWriter(OutputStream stream, String charset) throws UnsupportedEncodingException {
+ return new OutputStreamWriter(stream, charset);
+ }
+
+ /**
+ * Passes this OutputStream to the closure, ensuring that the stream
+ * is closed after the closure returns, regardless of errors.
+ *
+ * @param os the stream which is used and then closed
+ * @param closure the closure that the stream is passed into
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.2
+ */
+ public static <T, U extends OutputStream> T withStream(U os, @ClosureParams(value=FirstParam.class) Closure<T> closure) throws IOException {
+ try {
+ T result = closure.call(os);
+ os.flush();
+
+ OutputStream temp = os;
+ os = null;
+ temp.close();
+
+ return result;
+ } finally {
+ closeWithWarning(os);
+ }
+ }
+
+ /**
+ * Traverse through each byte of the specified stream. The
+ * stream is closed after the closure returns.
+ *
+ * @param is stream to iterate over, closed after the method call
+ * @param closure closure to apply to each byte
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static void eachByte(InputStream is, @ClosureParams(value=SimpleType.class, options="byte") Closure closure) throws IOException {
+ try {
+ while (true) {
+ int b = is.read();
+ if (b == -1) {
+ break;
+ } else {
+ closure.call((byte) b);
+ }
+ }
+
+ InputStream temp = is;
+ is = null;
+ temp.close();
+ } finally {
+ closeWithWarning(is);
+ }
+ }
+
+ /**
+ * Traverse through each the specified stream reading bytes into a buffer
+ * and calling the 2 parameter closure with this buffer and the number of bytes.
+ *
+ * @param is stream to iterate over, closed after the method call.
+ * @param bufferLen the length of the buffer to use.
+ * @param closure a 2 parameter closure which is passed the byte[] and a number of bytes successfully read.
+ * @throws IOException if an IOException occurs.
+ * @since 1.8
+ */
+ public static void eachByte(InputStream is, int bufferLen, @ClosureParams(value=FromString.class, options="byte[],Integer") Closure closure) throws IOException {
+ byte[] buffer = new byte[bufferLen];
+ int bytesRead;
+ try {
+ while ((bytesRead = is.read(buffer, 0, bufferLen)) > 0) {
+ closure.call(buffer, bytesRead);
+ }
+
+ InputStream temp = is;
+ is = null;
+ temp.close();
+ } finally {
+ closeWithWarning(is);
+ }
+ }
+
+ /**
+ * Transforms each character from this reader by passing it to the given
+ * closure. The Closure should return each transformed character, which
+ * will be passed to the Writer. The reader and writer will both be
+ * closed before this method returns.
+ *
+ * @param self a Reader object
+ * @param writer a Writer to receive the transformed characters
+ * @param closure a closure that performs the required transformation
+ * @throws IOException if an IOException occurs.
+ * @since 1.5.0
+ */
+ public static void transformChar(Reader self, Writer writer, @ClosureParams(value=SimpleType.class, options="java.lang.String") Closure closure) throws IOException {
+ int c;
+ try {
+ char[] chars = new char[1];
+ while ((c = self.read()) != -1) {
+ chars[0] = (char) c;
+ Object o = closure.call(new String(chars));
+ if (o != null) {
+ writer.write(o.toString());
+ }
+ }
+ writer.flush();
+
+ Writer temp2 = writer;
+ writer = null;
+ temp2.close();
+ Reader temp1 = self;
+ self = null;
+ temp1.close();
+ } finally {
+ closeWithWarning(self);
+ closeWithWarning(writer);
+ }
+ }
+
+ /**
+ * Transforms the lines from a reader with a Closure and
+ * write them to a writer. Both Reader and Writer are
+ * closed after the operation.
+ *
+ * @param reader Lines of text to be transformed. Reader is closed afterwards.
+ * @param writer Where transformed lines are written. Writer is closed afterwards.
+ * @param closure Single parameter closure that is called to transform each line of
+ * text from the reader, before writing it to the writer.
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static void transformLine(Reader reader, Writer writer, @ClosureParams(value=SimpleType.class, options="java.lang.String") Closure closure) throws IOException {
+ BufferedReader br = new BufferedReader(reader);
+ BufferedWriter bw = new BufferedWriter(writer);
+ String line;
+ try {
+ while ((line = br.readLine()) != null) {
+ Object o = closure.call(line);
+ if (o != null) {
+ bw.write(o.toString());
+ bw.newLine();
+ }
+ }
+ bw.flush();
+
+ Writer temp2 = writer;
+ writer = null;
+ temp2.close();
+ Reader temp1 = reader;
+ reader = null;
+ temp1.close();
+ } finally {
+ closeWithWarning(br);
+ closeWithWarning(reader);
+ closeWithWarning(bw);
+ closeWithWarning(writer);
+ }
+ }
+
+ /**
+ * Filter the lines from a reader and write them on the writer,
+ * according to a closure which returns true if the line should be included.
+ * Both Reader and Writer are closed after the operation.
+ *
+ * @param reader a reader, closed after the call
+ * @param writer a writer, closed after the call
+ * @param closure the closure which returns booleans
+ * @throws IOException if an IOException occurs.
+ * @since 1.0
+ */
+ public static void filterLine(Reader reader, Writer writer, @ClosureParams(value=SimpleType.class, options="java.lang.String") Closure closure) throws IOException {
+ BufferedReader br = new BufferedReader(reader);
+ BufferedWriter bw = new BufferedWriter(writer);
+ String line;
+ try {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ while ((line = br.readLine()) != null) {
+ if (bcw.call(line)) {
+ bw.write(line);
+ bw.newLine();
+ }
+ }
+ bw.flush();
+
+ Writer temp2 = writer;
+ writer = null;
+ temp2.close();
+ Reader temp1 = reader;
+ reader = null;
+ temp1.close();
+ } finally {
+ closeWithWarning(br);
+ closeWithWarning(reader);
+ closeWithWarning(bw);
+ closeWithWarning(writer);
+ }
+
+ }
+
+ /**
+ * Filter the lines from this Reader, and return a Writable which can be
+ * used to stream the filtered lines to a destination. The closure should
+ * return <code>true</code> if the line should be passed to the writer.
+ *
+ * @param reader this reader
+ * @param closure a closure used for filtering
+ * @return a Writable which will use the closure to filter each line
+ * from the reader when the Writable#writeTo(Writer) is called.
+ * @since 1.0
+ */
+ public static Writable filterLine(Reader reader, @ClosureParams(value=SimpleType.class, options="java.lang.String") final Closure closure) {
+ final BufferedReader br = new BufferedReader(reader);
+ return new Writable() {
+ public Writer writeTo(Writer out) throws IOException {
+ BufferedWriter bw = new BufferedWriter(out);
+ String line;
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ while ((line = br.readLine()) != null) {
+ if (bcw.call(line)) {
+ bw.write(line);
+ bw.newLine();
+ }
+ }
+ bw.flush();
+ return out;
+ }
+
+ public String toString() {
+ StringWriter buffer = new StringWriter();
+ try {
+ writeTo(buffer);
+ } catch (IOException e) {
+ throw new StringWriterIOException(e);
+ }
+ return buffer.toString();
+ }
+ };
+ }
+
+ /**
+ * Filter lines from an input stream using a closure predicate. The closure
+ * will be passed each line as a String, and it should return
+ * <code>true</code> if the line should be passed to the writer.
+ *
+ * @param self an input stream
+ * @param predicate a closure which returns boolean and takes a line
+ * @return a writable which writes out the filtered lines
+ * @see #filterLine(java.io.Reader, groovy.lang.Closure)
+ * @since 1.0
+ */
+ public static Writable filterLine(InputStream self, @ClosureParams(value=SimpleType.class, options="java.lang.String") Closure predicate) {
+ return filterLine(newReader(self), predicate);
+ }
+
+ /**
+ * Filter lines from an input stream using a closure predicate. The closure
+ * will be passed each line as a String, and it should return
+ * <code>true</code> if the line should be passed to the writer.
+ *
+ * @param self an input stream
+ * @param charset opens the stream with a specified charset
+ * @param predicate a closure which returns boolean and takes a line
+ * @return a writable which writes out the filtered lines
+ * @throws UnsupportedEncodingException if the encoding specified is not supported
+ * @see #filterLine(java.io.Reader, groovy.lang.Closure)
+ * @since 1.6.8
+ */
+ public static Writable filterLine(InputStream self, String charset, @ClosureParams(value=SimpleType.class, options="java.lang.String") Closure predicate)
+ throws UnsupportedEncodingException {
+ return filterLine(newReader(self, charset), predicate);
+ }
+
+ /**
+ * Uses a closure to filter lines from this InputStream and pass them to
+ * the given writer. The closure will be passed each line as a String, and
+ * it should return <code>true</code> if the line should be passed to the
+ * writer.
+ *
+ * @param self the InputStream
+ * @param writer a writer to write output to
+ * @param predicate a closure which returns true if a line should be accepted
+ * @throws IOException if an IOException occurs.
+ * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure)
+ * @since 1.0
+ */
+ public static void filterLine(InputStream self, Writer writer, @ClosureParams(value=SimpleType.class, options="java.lang.String") Closure predicate)
+ throws IOException {
+ filterLine(newReader(self), writer, predicate);
+ }
+
+ /**
+ * Uses a closure to filter lines from this InputStream and pass them to
+ * the given writer. The closure will be passed each line as a String, and
+ * it should return <code>true</code> if the line should be passed to the
+ * writer.
+ *
+ * @param self the InputStream
+ * @param writer a writer to write output to
+ * @param charset opens the stream with a specified charset
+ * @param predicate a closure which returns true if a line should be accepted
+ * @throws IOException if an IOException occurs.
+ * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure)
+ * @since 1.6.8
+ */
+ public static void filterLine(InputStream self, Writer writer, String charset, @ClosureParams(value=SimpleType.class, options="java.lang.String") Closure predicate)
+ throws IOException {
+ filterLine(newReader(self, charset), writer, predicate);
+ }
+
+ /**
+ * Allows this closeable to be used within the closure, ensuring that it
+ * is closed once the closure has been executed and before this method returns.
+ * <p>
+ * As with the try-with-resources statement, if multiple exceptions are thrown
+ * the exception from the closure will be returned and the exception from closing
+ * will be added as a suppressed exception.
+ *
+ * @param self the Closeable
+ * @param action the closure taking the Closeable as parameter
+ * @return the value returned by the closure
+ * @throws IOException if an IOException occurs.
+ * @since 2.4.0
+ */
+ public static <T, U extends Closeable> T withCloseable(U self, @ClosureParams(value=FirstParam.class) Closure<T> action) throws IOException {
+ Throwable thrown = null;
+ try {
+ return action.call(self);
+ } catch (Throwable e) {
+ thrown = e;
+ throw e;
+ } finally {
+ if (thrown != null) {
+ Throwable suppressed = tryClose(self, true);
+ if (suppressed != null) {
+ thrown.addSuppressed(suppressed);
+ }
+ } else {
+ self.close();
+ }
+ }
+ }
+
+ /**
+ * Allows this AutoCloseable to be used within the closure, ensuring that it
+ * is closed once the closure has been executed and before this method returns.
+ * <p>
+ * As with the try-with-resources statement, if multiple exceptions are thrown
+ * the exception from the closure will be returned and the exception from closing
+ * will be added as a suppressed exception.
+ *
+ * @param self the AutoCloseable
+ * @param action the closure taking the AutoCloseable as parameter
+ * @return the value returned by the closure
+ * @throws Exception if an Exception occurs.
+ * @since 2.5.0
+ */
+ public static <T, U extends AutoCloseable> T withCloseable(U self, @ClosureParams(value=FirstParam.class) Closure<T> action) throws Exception {
+ Throwable thrown = null;
+ try {
+ return action.call(self);
+ } catch (Throwable e) {
+ thrown = e;
+ throw e;
+ } finally {
+ if (thrown != null) {
+ Throwable suppressed = tryClose(self, true);
+ if (suppressed != null) {
+ thrown.addSuppressed(suppressed);
+ }
+ } else {
+ self.close();
+ }
+ }
+ }
+
+ static void writeUTF16BomIfRequired(final Writer writer, final String charset) throws IOException {
+ writeUTF16BomIfRequired(writer, Charset.forName(charset));
+ }
+
+ static void writeUTF16BomIfRequired(final Writer writer, final Charset charset) throws IOException {
+ if ("UTF-16BE".equals(charset.name())) {
+ writeUtf16Bom(writer, true);
+ } else if ("UTF-16LE".equals(charset.name())) {
+ writeUtf16Bom(writer, false);
+ }
+ }
+
+ static void writeUTF16BomIfRequired(final OutputStream stream, final String charset) throws IOException {
+ writeUTF16BomIfRequired(stream, Charset.forName(charset));
+ }
+
+ static void writeUTF16BomIfRequired(final OutputStream stream, final Charset charset) throws IOException {
+ if ("UTF-16BE".equals(charset.name())) {
+ writeUtf16Bom(stream, true);
+ } else if ("UTF-16LE".equals(charset.name())) {
+ writeUtf16Bom(stream, false);
+ }
+ }
+
+ private static void writeUtf16Bom(OutputStream stream, boolean bigEndian) throws IOException {
+ if (bigEndian) {
+ stream.write(-2); // FE
+ stream.write(-1); // FF
+ } else {
+ stream.write(-1); // FF
+ stream.write(-2); // FE
+ }
+ }
+
+ private static void writeUtf16Bom(Writer writer, boolean bigEndian) throws IOException {
+ if (bigEndian) {
+ writer.write(-2); // FE
+ writer.write(-1); // FF
+ } else {
+ writer.write(-1); // FF
+ writer.write(-2); // FE
+ }
+ }
+}