You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by ke...@apache.org on 2015/12/08 05:06:04 UTC

groovy git commit: GROOVY-7465: ResourceGroovyMethods/NioGroovyMethods BOM behavior is inconsistent (closes #176)

Repository: groovy
Updated Branches:
  refs/heads/master c8325af0d -> 93fe2f2dc


GROOVY-7465: ResourceGroovyMethods/NioGroovyMethods BOM behavior is inconsistent (closes #176)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/93fe2f2d
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/93fe2f2d
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/93fe2f2d

Branch: refs/heads/master
Commit: 93fe2f2dc05a9e9d163ff79a36d98e7261539429
Parents: c8325af
Author: Keegan Witt <ke...@gmail.com>
Authored: Wed Jul 22 09:08:46 2015 -0400
Committer: Keegan Witt <ke...@gmail.com>
Committed: Mon Dec 7 23:02:45 2015 -0500

----------------------------------------------------------------------
 .../groovy/runtime/IOGroovyMethods.java         |  44 +++
 .../groovy/runtime/ResourceGroovyMethods.java   | 296 +++++++++++------
 .../runtime/ResourceGroovyMethodsTest.groovy    | 126 ++++++--
 .../groovy/tools/groovydoc/FileOutputTool.java  |   2 +-
 .../groovy/runtime/NioGroovyMethods.java        | 316 +++++++++++++++----
 .../groovy/runtime/NioGroovyMethodsTest.groovy  | 182 ++++++++++-
 6 files changed, 787 insertions(+), 179 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/93fe2f2d/src/main/org/codehaus/groovy/runtime/IOGroovyMethods.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/runtime/IOGroovyMethods.java b/src/main/org/codehaus/groovy/runtime/IOGroovyMethods.java
index b7b1587..54f1f9e 100644
--- a/src/main/org/codehaus/groovy/runtime/IOGroovyMethods.java
+++ b/src/main/org/codehaus/groovy/runtime/IOGroovyMethods.java
@@ -48,6 +48,7 @@ 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;
@@ -1606,4 +1607,47 @@ public class IOGroovyMethods extends DefaultGroovyMethodsSupport {
         }
     }
 
+    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
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/93fe2f2d/src/main/org/codehaus/groovy/runtime/ResourceGroovyMethods.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/runtime/ResourceGroovyMethods.java b/src/main/org/codehaus/groovy/runtime/ResourceGroovyMethods.java
index ba92e06..b41b072 100644
--- a/src/main/org/codehaus/groovy/runtime/ResourceGroovyMethods.java
+++ b/src/main/org/codehaus/groovy/runtime/ResourceGroovyMethods.java
@@ -731,7 +731,7 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Write the text to the File.
+     * Write the text to the File without writing a BOM.
      *
      * @param file a File
      * @param text the text to write to the File
@@ -739,18 +739,23 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 1.0
      */
     public static void write(File file, String text) throws IOException {
-        Writer writer = null;
-        try {
-            writer = new FileWriter(file);
-            writer.write(text);
-            writer.flush();
+        write(file, text, false);
+    }
 
-            Writer temp = writer;
-            writer = null;
-            temp.close();
-        } finally {
-            closeWithWarning(writer);
-        }
+    /**
+     * Write the text to the File.  If the default charset is
+     * "UTF-16BE" or "UTF-16LE" (or an equivalent alias) and
+     * <code>writeBom</code> is <code>true</code>, the requisite byte order
+     * mark is written to the file before the text.
+     *
+     * @param file     a File
+     * @param text     the text to write to the File
+     * @param writeBom whether to write a BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void write(File file, String text, boolean writeBom) throws IOException {
+        write(file, text, Charset.defaultCharset().name(), writeBom);
     }
 
     /**
@@ -831,9 +836,8 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Write the text to the File, using the specified encoding.  If the given
-     * charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias), the
-     * requisite byte order mark is written to the file before the text.
+     * Write the text to the File without writing a BOM,
+     * using the specified encoding.
      *
      * @param file    a File
      * @param text    the text to write to the File
@@ -842,10 +846,29 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 1.0
      */
     public static void write(File file, String text, String charset) throws IOException {
+        write(file, text, charset, false);
+    }
+
+    /**
+     * Write the text to the File, using the specified encoding.  If the given
+     * charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias) and
+     * <code>writeBom</code> is <code>true</code>, the requisite byte order
+     * mark is written to the file before the text.
+     *
+     * @param file     a File
+     * @param text     the text to write to the File
+     * @param charset  the charset used
+     * @param writeBom whether to write a BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void write(File file, String text, String charset, boolean writeBom) throws IOException {
         Writer writer = null;
         try {
             FileOutputStream out = new FileOutputStream(file);
-            writeUTF16BomIfRequired(charset, out);
+            if (writeBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(out, charset);
+            }
             writer = new OutputStreamWriter(out, charset);
             writer.write(text);
             writer.flush();
@@ -859,7 +882,7 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Append the text at the end of the File.
+     * Append the text at the end of the File without writing a BOM.
      *
      * @param file a File
      * @param text the text to append at the end of the File
@@ -867,22 +890,27 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 1.0
      */
     public static void append(File file, Object text) throws IOException {
-        Writer writer = null;
-        try {
-            writer = new FileWriter(file, true);
-            InvokerHelper.write(writer, text);
-            writer.flush();
+        append(file, text, false);
+    }
 
-            Writer temp = writer;
-            writer = null;
-            temp.close();
-        } finally {
-            closeWithWarning(writer);
-        }
+    /**
+     * Append the text at the end of the File.  If the default
+     * charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias) and
+     * <code>writeBom</code> is <code>true</code>, the requisite byte order
+     * mark is written to the file before the text.
+     *
+     * @param file     a File
+     * @param text     the text to append at the end of the File
+     * @param writeBom whether to write a BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(File file, Object text, boolean writeBom) throws IOException {
+        append(file, text, Charset.defaultCharset().name(), writeBom);
     }
-    
+
     /**
-     * Append the text supplied by the Writer at the end of the File.
+     * Append the text supplied by the Writer at the end of the File without writing a BOM.
      *
      * @param file a File
      * @param reader the Reader supplying the text to append at the end of the File
@@ -890,11 +918,11 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3
      */
     public static void append(File file, Reader reader) throws IOException {
-        appendBuffered(file, reader);
+        append(file, reader, false);
     }
-    
+
     /**
-     * Append the text supplied by the Writer at the end of the File.
+     * Append the text supplied by the Writer at the end of the File without writing a BOM.
      *
      * @param file a File
      * @param writer the Writer supplying the text to append at the end of the File
@@ -902,13 +930,33 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3
      */
     public static void append(File file, Writer writer) throws IOException {
-         appendBuffered(file, writer);
+         append(file, writer, false);
     }
-    
-    private static void appendBuffered(File file, Object text) throws IOException {
+
+    /**
+     * Append the text supplied by the Writer at the end of the File.  If the default
+     * charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias) and
+     * <code>writeBom</code> is <code>true</code>, the requisite byte order
+     * mark is written to the file before the text.
+     *
+     * @param file     a File
+     * @param writer   the Writer supplying the text to append at the end of the File
+     * @param writeBom whether to write a BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(File file, Writer writer, boolean writeBom) throws IOException {
+        appendBuffered(file, writer, writeBom);
+    }
+
+    private static void appendBuffered(File file, Object text, boolean writeBom) throws IOException {
         BufferedWriter writer = null;
         try {
+            boolean shouldWriteBom = writeBom && !file.exists();
             writer = newWriter(file, true);
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(writer, Charset.defaultCharset().name());
+            }
             InvokerHelper.write(writer, text);
             writer.flush();
 
@@ -921,7 +969,8 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Append bytes to the end of a File.
+     * Append bytes to the end of a File.  It <strong>will not</strong> be
+     * interpreted as text.
      *
      * @param file  a File
      * @param bytes the byte array to append to the end of the File
@@ -962,10 +1011,8 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Append the text at the end of the File, using a specified encoding.  If
-     * the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias)
-     * and the file doesn't already exist, the requisite byte order mark is
-     * written to the file before the text is appended.
+     * Append the text at the end of the File without writing a BOM,
+     * using a specified encoding.
      *
      * @param file    a File
      * @param text    the text to append at the end of the File
@@ -974,11 +1021,30 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 1.0
      */
     public static void append(File file, Object text, String charset) throws IOException {
+        append(file, text, charset, false);
+    }
+
+    /**
+     * Append the text at the end of the File, using a specified encoding.  If
+     * the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
+     *
+     * @param file     a File
+     * @param text     the text to append at the end of the File
+     * @param charset  the charset used
+     * @param writeBom whether to write a BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(File file, Object text, String charset, boolean writeBom) throws IOException {
         Writer writer = null;
         try {
+            boolean shouldWriteBom = writeBom && !file.exists();
             FileOutputStream out = new FileOutputStream(file, true);
-            if (!file.exists()) {
-                writeUTF16BomIfRequired(charset, out);
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(out, charset);
             }
             writer = new OutputStreamWriter(out, charset);
             InvokerHelper.write(writer, text);
@@ -991,37 +1057,96 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
             closeWithWarning(writer);
         }
     }
-    
+
     /**
-     * Append the text supplied by the Writer at the end of the File, using a specified encoding.
+     * Append the text supplied by the Writer at the end of the File
+     * without writing a BOM, using a specified encoding.
      *
-     * @param file a File
-     * @param writer the Writer supplying the text to append at the end of the File
+     * @param file    a File
+     * @param writer  the Writer supplying the text to append at the end of the File
      * @param charset the charset used
      * @throws IOException if an IOException occurs.
      * @since 2.3
      */
     public static void append(File file, Writer writer, String charset) throws IOException {
-        appendBuffered(file, writer, charset);
+        append(file, writer, charset, false);
     }
-    
+
+    /**
+     * Append the text supplied by the Writer at the end of the File, using a specified encoding.
+     * If the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
+     *
+     * @param file    a File
+     * @param writer  the Writer supplying the text to append at the end of the File
+     * @param charset the charset used
+     * @param writeBom whether to write a BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(File file, Writer writer, String charset, boolean writeBom) throws IOException {
+        appendBuffered(file, writer, charset, writeBom);
+    }
+
     /**
      * Append the text supplied by the Reader at the end of the File, using a specified encoding.
+     * If the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
      *
-     * @param file a File
-     * @param reader the Reader supplying the text to append at the end of the File
+     * @param file    a File
+     * @param reader  the Reader supplying the text to append at the end of the File
+     * @param writeBom whether to write a BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(File file, Reader reader, boolean writeBom) throws IOException {
+        append(file, reader, Charset.defaultCharset().name(), writeBom);
+    }
+
+    /**
+     * Append the text supplied by the Reader at the end of the File
+     * without writing a BOM, using a specified encoding.
+     *
+     * @param file    a File
+     * @param reader  the Reader supplying the text to append at the end of the File
      * @param charset the charset used
      * @throws IOException if an IOException occurs.
      * @since 2.3
      */
     public static void append(File file, Reader reader, String charset) throws IOException {
-        appendBuffered(file, reader, charset);
+        append(file, reader, charset, false);
+    }
+
+    /**
+     * Append the text supplied by the Reader at the end of the File, using a specified encoding.
+     * If the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
+     *
+     * @param file    a File
+     * @param reader  the Reader supplying the text to append at the end of the File
+     * @param charset the charset used
+     * @param writeBom whether to write a BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(File file, Reader reader, String charset, boolean writeBom) throws IOException {
+        appendBuffered(file, reader, charset, writeBom);
     }
-    
-    private static void appendBuffered(File file, Object text, String charset) throws IOException {
+
+    private static void appendBuffered(File file, Object text, String charset, boolean writeBom) throws IOException {
         BufferedWriter writer = null;
         try {
+            boolean shouldWriteBom = writeBom && !file.exists();
             writer = newWriter(file, charset, true);
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(writer, charset);
+            }
             InvokerHelper.write(writer, text);
             writer.flush();
 
@@ -1800,6 +1925,20 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
+     * Helper method to create a buffered writer for a file without writing a BOM.
+     *
+     * @param file    a File
+     * @param charset the name of the encoding used to write in this file
+     * @param append  true if in append mode
+     * @return a BufferedWriter
+     * @throws IOException if an IOException occurs.
+     * @since 1.0
+     */
+    public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException {
+        return newWriter(file, charset, append, false);
+    }
+
+    /**
      * Helper method to create a buffered writer for a file.  If the given
      * charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias), the
      * requisite byte order mark is written to the stream before the writer
@@ -1808,34 +1947,31 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
      * @param file    a File
      * @param charset the name of the encoding used to write in this file
      * @param append  true if in append mode
+     * @param writeBom whether to write a BOM
      * @return a BufferedWriter
      * @throws IOException if an IOException occurs.
-     * @since 1.0
+     * @since 2.5.0
      */
-    public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException {
+    public static BufferedWriter newWriter(File file, String charset, boolean append, boolean writeBom) throws IOException {
+        boolean shouldWriteBom = writeBom && !file.exists();
         if (append) {
-            return new EncodingAwareBufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset));
+            FileOutputStream stream = new FileOutputStream(file, append);
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(stream, charset);
+            }
+            return new EncodingAwareBufferedWriter(new OutputStreamWriter(stream, charset));
         } else {
-            // first write the Byte Order Mark for Unicode encodings
             FileOutputStream stream = new FileOutputStream(file);
-            writeUTF16BomIfRequired(charset, stream);
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(stream, charset);
+            }
             return new EncodingAwareBufferedWriter(new OutputStreamWriter(stream, charset));
         }
     }
 
-    private static void writeUTF16BomIfRequired(final String charset, final OutputStream stream) throws IOException {
-        if ("UTF-16BE".equals(Charset.forName(charset).name())) {
-            writeUtf16Bom(stream, true);
-        } else if ("UTF-16LE".equals(Charset.forName(charset).name())) {
-            writeUtf16Bom(stream, false);
-        }
-    }
-
     /**
-     * Creates a buffered writer for this file, writing data using the given
-     * encoding.  If the given charset is "UTF-16BE" or "UTF-16LE" (or an
-     * equivalent alias), the requisite byte order mark is written to the
-     * stream before the writer is returned.
+     * Creates a buffered writer for this file, writing data without writing a
+     * BOM, using a specified encoding.
      *
      * @param file    a File
      * @param charset the name of the encoding used to write in this file
@@ -1848,24 +1984,6 @@ public class ResourceGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Write a Byte Order Mark at the beginning of the file
-     *
-     * @param stream    the FileOutputStream to write the BOM to
-     * @param bigEndian true if UTF 16 Big Endian or false if Low Endian
-     * @throws IOException if an IOException occurs.
-     * @since 1.0
-     */
-    private static void writeUtf16Bom(OutputStream stream, boolean bigEndian) throws IOException {
-        if (bigEndian) {
-            stream.write(-2);
-            stream.write(-1);
-        } else {
-            stream.write(-1);
-            stream.write(-2);
-        }
-    }
-
-    /**
      * Creates a new BufferedWriter for this file, passes it to the closure, and
      * ensures the stream is flushed and closed after the closure returns.
      *

http://git-wip-us.apache.org/repos/asf/groovy/blob/93fe2f2d/src/test/org/codehaus/groovy/runtime/ResourceGroovyMethodsTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/org/codehaus/groovy/runtime/ResourceGroovyMethodsTest.groovy b/src/test/org/codehaus/groovy/runtime/ResourceGroovyMethodsTest.groovy
index 6a1c2af..e63ad33 100644
--- a/src/test/org/codehaus/groovy/runtime/ResourceGroovyMethodsTest.groovy
+++ b/src/test/org/codehaus/groovy/runtime/ResourceGroovyMethodsTest.groovy
@@ -18,20 +18,29 @@
  */
 package org.codehaus.groovy.runtime
 
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import static org.junit.Assert.assertEquals
 
-class ResourceGroovyMethodsTest extends GroovyTestCase {
+class ResourceGroovyMethodsTest {
 
+	@Rule
+	public TemporaryFolder temporaryFolder = new TemporaryFolder()
+
+	@Test
 	void test_Should_write_String_to_File_using_default_encoding() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		String text = 'Hello World'
 		
 		ResourceGroovyMethods.write(file, text)
 		
 		assert file.text == text
 	}
-	
+
+	@Test
 	void test_Should_write_String_to_File_using_specified_encoding() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		String text = "؁"
 		String encoding = 'UTF-8'
 		
@@ -39,9 +48,21 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		
 		assert file.getText(encoding) == text
 	}
-	
+
+	@Test
+	void test_Should_write_String_and_bom_to_File_using_specified_encoding() {
+		File file = temporaryFolder.newFile()
+		String text = "؁"
+		String encoding = 'UTF-16LE'
+
+		ResourceGroovyMethods.write(file, text, encoding, true)
+
+		assert file.getBytes() == [-1, -2, 1, 6] as byte[]
+	}
+
+	@Test
 	void test_Should_append_ByteArray_to_File() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		file.write('Hello')
 		byte[] bytes = ' World'.bytes
 		
@@ -49,18 +70,20 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		
 		assert file.text == 'Hello World'
 	}
-	
+
+	@Test
 	void test_Should_append_String_to_File_using_default_encoding() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		file.write('Hello')
 		
 		ResourceGroovyMethods.append(file, ' World')
 		
 		assert file.text == 'Hello World'
 	}
-	
+
+	@Test
 	void test_Should_append_text_supplied_by_Reader_to_File_using_default_encoding() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		file.write('Hello')
 		Reader reader = new StringReader(' World')
 		
@@ -68,9 +91,10 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		
 		assert file.text == 'Hello World'
 	}
-	
+
+	@Test
 	void test_Should_append_text_supplied_by_Writer_to_File_using_default_encoding() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		file.write('Hello')
 		
 		Writer writer = new StringWriter()
@@ -80,9 +104,10 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		
 		assert file.text == 'Hello World'
 	}
-	
+
+	@Test
 	void test_Should_append_String_to_File_using_specified_encoding() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		String encoding = 'UTF-8'
 		file.write('؁', encoding)
 		
@@ -90,9 +115,10 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		
 		assert file.getText(encoding) == '؁ ؁'
 	}
-	
+
+	@Test
 	void test_Should_append_text_supplied_by_Reader_to_File_using_specified_encoding() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		String encoding = 'UTF-8'
 		file.write('؁', encoding)
 		Reader reader = new CharArrayReader([' ','؁'] as char[])
@@ -101,9 +127,10 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		
 		assert file.getText(encoding) == '؁ ؁'
 	}
-	
+
+	@Test
 	void test_Should_append_text_supplied_by_Writer_to_File_using_specified_encoding() {
-		File file = createDeleteOnExitTempFile()
+		File file = temporaryFolder.newFile()
 		String encoding = 'UTF-8'
 		file.write('؁', encoding)
 		
@@ -116,6 +143,59 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		assert file.getText(encoding) == '؁ ؁'
 	}
 
+	@Test
+	void test_Should_write_String_to_File_using_specified_encoding_with_bom() {
+		File file = temporaryFolder.newFile()
+		String text = "؁"
+		String encoding = 'UTF-16LE'
+
+		ResourceGroovyMethods.write(file, text, encoding, true)
+
+		assert file.getBytes() == [-1, -2, 1, 6] as byte[]
+	}
+
+	@Test
+	void test_Should_append_String_to_File_using_specified_encoding_with_bom() {
+		File file = temporaryFolder.newFile()
+		file.delete()
+		String encoding = 'UTF-16LE'
+		file.append('؁', encoding, true)
+
+		ResourceGroovyMethods.append(file, ' ؁', encoding, true)
+
+		assert file.getBytes() == [-1, -2, 1, 6, 32, 0, 1, 6] as byte[]
+	}
+
+	@Test
+	void test_Should_append_text_supplied_by_Reader_to_File_using_specified_encoding_with_bom() {
+		File file = temporaryFolder.newFile()
+		file.delete()
+		String encoding = 'UTF-16LE'
+		file.append('؁', encoding, true)
+		Reader reader = new CharArrayReader([' ','؁'] as char[])
+
+		ResourceGroovyMethods.append(file, reader, encoding, true)
+
+		assert file.getBytes() == [-1, -2, 1, 6, 32, 0, 1, 6] as byte[]
+	}
+
+	@Test
+	void test_Should_append_text_supplied_by_Writer_to_File_using_specified_encoding_with_bom() {
+		File file = temporaryFolder.newFile()
+		file.delete()
+		String encoding = 'UTF-16LE'
+		file.append('؁', encoding, true)
+
+		Writer writer = new CharArrayWriter()
+		writer.append(' ')
+		writer.append('؁')
+
+		ResourceGroovyMethods.append(file, writer, encoding, true)
+
+		assert file.getBytes() == [-1, -2, 1, 6, 32, 0, 1, 6] as byte[]
+	}
+
+	@Test
 	void testFileDirectorySizeExceptions() {
 		try {
 			ResourceGroovyMethods.directorySize(new File("doesn't exist"))
@@ -134,6 +214,7 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		tempFile.delete()
 	}
 
+	@Test
 	void testDirectorySize() {
 		File tempFile = File.createTempFile("__testDirectorySize__", "")
 		delete(tempFile)
@@ -161,7 +242,8 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 		assertEquals(totalSize, ResourceGroovyMethods.directorySize(testDir))
 		delete(testDir)
 	}
-    
+
+	@Test
     void testRelativePath() {
         assertEquals("b", ResourceGroovyMethods.relativePath(new File("a"), new File("a/b")))
         assertEquals("b/a", ResourceGroovyMethods.relativePath(new File("a"), new File("a/b/a")))
@@ -223,10 +305,4 @@ class ResourceGroovyMethodsTest extends GroovyTestCase {
 			throw new RuntimeException(String.format("[%s] is not deleted", file.getAbsolutePath()))
 		}
 	}
-	
-	File createDeleteOnExitTempFile() {
-		File tempFile = File.createTempFile("tmp", "txt")
-		tempFile.deleteOnExit()
-		return tempFile
-	}
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/93fe2f2d/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/FileOutputTool.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/FileOutputTool.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/FileOutputTool.java
index 0ebc579..93da202 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/FileOutputTool.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/FileOutputTool.java
@@ -31,6 +31,6 @@ public class FileOutputTool implements OutputTool {
     public void writeToOutput(String fileName, String text, String charset) throws Exception {
         File file = new File(fileName);
         file.getParentFile().mkdirs();
-        ResourceGroovyMethods.write(file, text, charset);
+        ResourceGroovyMethods.write(file, text, charset, true);
     }
 }

http://git-wip-us.apache.org/repos/asf/groovy/blob/93fe2f2d/subprojects/groovy-nio/src/main/java/org/codehaus/groovy/runtime/NioGroovyMethods.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-nio/src/main/java/org/codehaus/groovy/runtime/NioGroovyMethods.java b/subprojects/groovy-nio/src/main/java/org/codehaus/groovy/runtime/NioGroovyMethods.java
index 66f8f8a..6ac4837 100644
--- a/subprojects/groovy-nio/src/main/java/org/codehaus/groovy/runtime/NioGroovyMethods.java
+++ b/subprojects/groovy-nio/src/main/java/org/codehaus/groovy/runtime/NioGroovyMethods.java
@@ -452,7 +452,7 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Write the text to the Path.
+     * Write the text to the Path without writing a BOM .
      *
      * @param self a Path
      * @param text the text to write to the Path
@@ -460,18 +460,23 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static void write(Path self, String text) throws IOException {
-        Writer writer = null;
-        try {
-            writer = new OutputStreamWriter(Files.newOutputStream(self), Charset.defaultCharset());
-            writer.write(text);
-            writer.flush();
+        write(self, text, false);
+    }
 
-            Writer temp = writer;
-            writer = null;
-            temp.close();
-        } finally {
-            closeWithWarning(writer);
-        }
+    /**
+     * Write the text to the Path.  If the default charset is
+     * "UTF-16BE" or "UTF-16LE" (or an equivalent alias) and
+     * <code>writeBom</code> is <code>true</code>, the requisite byte order
+     * mark is written to the file before the text.
+     *
+     * @param self     a Path
+     * @param text     the text to write to the Path
+     * @param writeBom whether to write the BOM
+     * @throws java.io.IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void write(Path self, String text, boolean writeBom) throws IOException {
+        write(self, text, Charset.defaultCharset().name(), writeBom);
     }
 
     /**
@@ -552,7 +557,7 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Write the text to the Path, using the specified encoding.
+     * Write the text to the Path without writing a BOM, using the specified encoding.
      *
      * @param self    a Path
      * @param text    the text to write to the Path
@@ -561,9 +566,30 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static void write(Path self, String text, String charset) throws IOException {
+        write(self, text, charset, false);
+    }
+
+    /**
+     * Write the text to the Path, using the specified encoding.  If the given
+     * charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias) and
+     * <code>writeBom</code> is <code>true</code>, the requisite byte order
+     * mark is written to the file before the text.
+     *
+     * @param self     a Path
+     * @param text     the text to write to the Path
+     * @param charset  the charset used
+     * @param writeBom whether to write a BOM
+     * @throws java.io.IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void write(Path self, String text, String charset, boolean writeBom) throws IOException {
         Writer writer = null;
         try {
-            writer = new OutputStreamWriter(Files.newOutputStream(self), Charset.forName(charset));
+            OutputStream out = Files.newOutputStream(self);
+            if (writeBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(out, charset);
+            }
+            writer = new OutputStreamWriter(out, Charset.forName(charset));
             writer.write(text);
             writer.flush();
 
@@ -576,7 +602,7 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Append the text at the end of the Path.
+     * Append the text at the end of the Path without writing a BOM.
      *
      * @param self a Path
      * @param text the text to append at the end of the Path
@@ -584,22 +610,12 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static void append(Path self, Object text) throws IOException {
-        Writer writer = null;
-        try {
-            writer = new OutputStreamWriter(Files.newOutputStream(self, CREATE, APPEND), Charset.defaultCharset());
-            InvokerHelper.write(writer, text);
-            writer.flush();
-
-            Writer temp = writer;
-            writer = null;
-            temp.close();
-        } finally {
-            closeWithWarning(writer);
-        }
+        append(self, text, Charset.defaultCharset().name(), false);
     }
 
     /**
-     * Append the text supplied by the Writer at the end of the File.
+     * Append the text supplied by the Writer at the end of the File without
+     * writing a BOM.
      *
      * @param file a Path
      * @param reader the Reader supplying the text to append at the end of the File
@@ -607,11 +623,11 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static void append(Path file, Reader reader) throws IOException {
-        appendBuffered(file, reader);
+        append(file, reader, Charset.defaultCharset().name());
     }
 
     /**
-     * Append the text supplied by the Writer at the end of the File.
+     * Append the text supplied by the Writer at the end of the File without writing a BOM.
      *
      * @param file a File
      * @param writer the Writer supplying the text to append at the end of the File
@@ -619,26 +635,12 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static void append(Path file, Writer writer) throws IOException {
-        appendBuffered(file, writer);
-    }
-
-    private static void appendBuffered(Path file, Object text) throws IOException {
-        BufferedWriter writer = null;
-        try {
-            writer = newWriter(file, true);
-            InvokerHelper.write(writer, text);
-            writer.flush();
-
-            Writer temp = writer;
-            writer = null;
-            temp.close();
-        } finally {
-            closeWithWarning(writer);
-        }
+        append(file, writer, Charset.defaultCharset().name());
     }
 
     /**
-     * Append bytes to the end of a Path.
+     * Append bytes to the end of a Path.  It <strong>will not</strong> be
+     * interpreted as text.
      *
      * @param self  a Path
      * @param bytes the byte array to append to the end of the Path
@@ -679,7 +681,24 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Append the text at the end of the Path, using a specified encoding.
+     * Append the text at the end of the Path.  If the default charset is
+     * "UTF-16BE" or "UTF-16LE" (or an equivalent alias) and
+     * <code>writeBom</code> is <code>true</code>, the requisite byte order
+     * mark is written to the file before the text.
+     *
+     * @param self    a Path
+     * @param text    the text to append at the end of the Path
+     * @param writeBom whether to write the BOM
+     * @throws java.io.IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(Path self, Object text, boolean writeBom) throws IOException {
+        append(self, text, Charset.defaultCharset().name(), writeBom);
+    }
+
+    /**
+     * Append the text at the end of the Path without writing a BOM, using a specified
+     * encoding.
      *
      * @param self    a Path
      * @param text    the text to append at the end of the Path
@@ -688,10 +707,33 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static void append(Path self, Object text, String charset) throws IOException {
+        append(self, text, charset, false);
+    }
+
+    /**
+     * Append the text at the end of the Path, using a specified encoding.  If
+     * the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
+     *
+     * @param self     a Path
+     * @param text     the text to append at the end of the Path
+     * @param charset  the charset used
+     * @param writeBom whether to write the BOM
+     * @throws java.io.IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(Path self, Object text, String charset, boolean writeBom) throws IOException {
         Writer writer = null;
         try {
+            Charset resolvedCharset = Charset.forName(charset);
+            boolean shouldWriteBom = writeBom && !self.toFile().exists();
             OutputStream out = Files.newOutputStream(self, CREATE, APPEND);
-            writer = new OutputStreamWriter(out, Charset.forName(charset));
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(out, resolvedCharset);
+            }
+            writer = new OutputStreamWriter(out, resolvedCharset);
             InvokerHelper.write(writer, text);
             writer.flush();
 
@@ -705,6 +747,24 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
 
     /**
      * Append the text supplied by the Writer at the end of the File, using a specified encoding.
+     * If the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
+     *
+     * @param file a File
+     * @param writer the Writer supplying the text to append at the end of the File
+     * @param writeBom whether to write the BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(Path file, Writer writer, boolean writeBom) throws IOException {
+        append(file, writer, Charset.defaultCharset().name(), writeBom);
+    }
+
+    /**
+     * Append the text supplied by the Writer at the end of the File without writing a BOM,
+     * using a specified encoding.
      *
      * @param file a File
      * @param writer the Writer supplying the text to append at the end of the File
@@ -713,11 +773,47 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static void append(Path file, Writer writer, String charset) throws IOException {
-        appendBuffered(file, writer, charset);
+        appendBuffered(file, writer, charset, false);
+    }
+
+    /**
+     * Append the text supplied by the Writer at the end of the File, using a specified encoding.
+     * If the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
+     *
+     * @param file a File
+     * @param writer the Writer supplying the text to append at the end of the File
+     * @param charset the charset used
+     * @param writeBom whether to write the BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(Path file, Writer writer, String charset, boolean writeBom) throws IOException {
+        appendBuffered(file, writer, charset, writeBom);
     }
 
     /**
      * Append the text supplied by the Reader at the end of the File, using a specified encoding.
+     * If the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
+     *
+     * @param file a File
+     * @param reader the Reader supplying the text to append at the end of the File
+     * @param writeBom whether to write the BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(Path file, Reader reader, boolean writeBom) throws IOException {
+        appendBuffered(file, reader, Charset.defaultCharset().name(), writeBom);
+    }
+
+    /**
+     * Append the text supplied by the Reader at the end of the File without writing
+     * a BOM, using a specified encoding.
      *
      * @param file a File
      * @param reader the Reader supplying the text to append at the end of the File
@@ -726,13 +822,35 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static void append(Path file, Reader reader, String charset) throws IOException {
-        appendBuffered(file, reader, charset);
+        append(file, reader, charset, false);
     }
 
-    private static void appendBuffered(Path file, Object text, String charset) throws IOException {
+    /**
+     * Append the text supplied by the Reader at the end of the File, using a specified encoding.
+     * If the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already
+     * exist, the requisite byte order mark is written to the file before the
+     * text is appended.
+     *
+     * @param file a File
+     * @param reader the Reader supplying the text to append at the end of the File
+     * @param charset the charset used
+     * @param writeBom whether to write the BOM
+     * @throws IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static void append(Path file, Reader reader, String charset, boolean writeBom) throws IOException {
+        appendBuffered(file, reader, charset, writeBom);
+    }
+
+    private static void appendBuffered(Path file, Object text, String charset, boolean writeBom) throws IOException {
         BufferedWriter writer = null;
         try {
+            boolean shouldWriteBom = writeBom && !file.toFile().exists();
             writer = newWriter(file, charset, true);
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(writer, charset);
+            }
             InvokerHelper.write(writer, text);
             writer.flush();
 
@@ -1344,7 +1462,8 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
     /**
      * Create a new BufferedReader for this file using the specified charset and then
      * passes it into the closure, ensuring the reader is closed after the
-     * closure returns.
+     * closure returns.  The writer will use the given charset encoding,
+     * but will not write a BOM.
      *
      * @param self    a file object
      * @param charset the charset for this input stream
@@ -1471,7 +1590,7 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
     }
 
     /**
-     * Helper method to create a buffered writer for a file.
+     * Helper method to create a buffered writer for a file without writing a BOM.
      *
      * @param self    a Path
      * @param charset the name of the encoding used to write in this file
@@ -1481,14 +1600,42 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static BufferedWriter newWriter(Path self, String charset, boolean append) throws IOException {
+        return newWriter(self, charset, append, false);
+    }
+
+    /**
+     * Helper method to create a buffered writer for a file.  If the given
+     * charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias), the
+     * requisite byte order mark is written to the stream before the writer
+     * is returned.
+     *
+     * @param self     a Path
+     * @param charset  the name of the encoding used to write in this file
+     * @param append   true if in append mode
+     * @param writeBom whether to write a BOM
+     * @return a BufferedWriter
+     * @throws java.io.IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static BufferedWriter newWriter(Path self, String charset, boolean append, boolean writeBom) throws IOException {
+        boolean shouldWriteBom = writeBom && !self.toFile().exists();
         if (append) {
-            return Files.newBufferedWriter(self, Charset.forName(charset), CREATE, APPEND);
+            BufferedWriter writer = Files.newBufferedWriter(self, Charset.forName(charset), CREATE, APPEND);
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(writer, charset);
+            }
+            return writer;
+        } else {
+            OutputStream out = Files.newOutputStream(self);
+            if (shouldWriteBom) {
+                IOGroovyMethods.writeUTF16BomIfRequired(out, charset);
+            }
+            return new BufferedWriter(new OutputStreamWriter(out, Charset.forName(charset)));
         }
-        return Files.newBufferedWriter(self, Charset.forName(charset));
     }
 
     /**
-     * Creates a buffered writer for this file, writing data using the given
+     * Creates a buffered writer for this file without writing a BOM, writing data using the given
      * encoding.
      *
      * @param self    a Path
@@ -1504,6 +1651,7 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
     /**
      * Creates a new BufferedWriter for this file, passes it to the closure, and
      * ensures the stream is flushed and closed after the closure returns.
+     * The writer will not write a BOM.
      *
      * @param self    a Path
      * @param closure a closure
@@ -1512,13 +1660,13 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static <T> T withWriter(Path self, @ClosureParams(value = SimpleType.class, options = "java.io.Writer") Closure<T> closure) throws IOException {
-        return IOGroovyMethods.withWriter(newWriter(self), closure);
+        return withWriter(self, Charset.defaultCharset().name(), closure);
     }
 
     /**
      * Creates a new BufferedWriter for this file, passes it to the closure, and
      * ensures the stream is flushed and closed after the closure returns.
-     * The writer will use the given charset encoding.
+     * The writer will use the given charset encoding, but will not write a BOM.
      *
      * @param self    a Path
      * @param charset the charset used
@@ -1528,13 +1676,34 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static <T> T withWriter(Path self, String charset, @ClosureParams(value = SimpleType.class, options = "java.io.Writer") Closure<T> closure) throws IOException {
-        return IOGroovyMethods.withWriter(newWriter(self, charset), closure);
+        return withWriter(self, charset, false, closure);
+    }
+
+    /**
+     * Creates a new BufferedWriter for this file, passes it to the closure, and
+     * ensures the stream is flushed and closed after the closure returns.
+     * The writer will use the given charset encoding.  If the given charset is
+     * "UTF-16BE" or "UTF-16LE" (or an equivalent alias), <code>writeBom</code>
+     * is <code>true</code>, and the file doesn't already exist, the requisite
+     * byte order mark is written to the stream when the writer is created.
+     *
+     * @param self    a Path
+     * @param charset the charset used
+     * @param writeBom whether to write the BOM
+     * @param closure a closure
+     * @return the value returned by the closure
+     * @throws java.io.IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static <T> T withWriter(Path self, String charset, boolean writeBom, @ClosureParams(value = SimpleType.class, options = "java.io.Writer") Closure<T> closure) throws IOException {
+        return IOGroovyMethods.withWriter(newWriter(self, charset, false, writeBom), closure);
     }
 
     /**
      * Create a new BufferedWriter which will append to this
      * file.  The writer is passed to the closure and will be closed before
-     * this method returns.
+     * this method returns.  The writer will use the given charset encoding,
+     * but will not write a BOM.
      *
      * @param self    a Path
      * @param charset the charset used
@@ -1544,12 +1713,33 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static <T> T withWriterAppend(Path self, String charset, @ClosureParams(value = SimpleType.class, options = "java.io.Writer") Closure<T> closure) throws IOException {
-        return IOGroovyMethods.withWriter(newWriter(self, charset, true), closure);
+        return withWriterAppend(self, charset, false, closure);
+    }
+
+    /**
+     * Create a new BufferedWriter which will append to this
+     * file.  The writer is passed to the closure and will be closed before
+     * this method returns.  The writer will use the given charset encoding.
+     * If the given charset is "UTF-16BE" or "UTF-16LE" (or an equivalent alias),
+     * <code>writeBom</code> is <code>true</code>, and the file doesn't already exist,
+     * the requisite byte order mark is written to the stream when the writer is created.
+     *
+     * @param self    a Path
+     * @param charset the charset used
+     * @param writeBom whether to write the BOM
+     * @param closure a closure
+     * @return the value returned by the closure
+     * @throws java.io.IOException if an IOException occurs.
+     * @since 2.5.0
+     */
+    public static <T> T withWriterAppend(Path self, String charset, boolean writeBom, @ClosureParams(value = SimpleType.class, options = "java.io.Writer") Closure<T> closure) throws IOException {
+        return IOGroovyMethods.withWriter(newWriter(self, charset, true, writeBom), closure);
     }
 
     /**
      * Create a new BufferedWriter for this file in append mode.  The writer
      * is passed to the closure and is closed after the closure returns.
+     * The writer will not write a BOM.
      *
      * @param self    a Path
      * @param closure a closure
@@ -1558,7 +1748,7 @@ public class NioGroovyMethods extends DefaultGroovyMethodsSupport {
      * @since 2.3.0
      */
     public static <T> T withWriterAppend(Path self, @ClosureParams(value = SimpleType.class, options = "java.io.Writer") Closure<T> closure) throws IOException {
-        return IOGroovyMethods.withWriter(newWriter(self, true), closure);
+        return withWriterAppend(self, Charset.defaultCharset().name(), closure);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/groovy/blob/93fe2f2d/subprojects/groovy-nio/src/test/groovy/org/codehaus/groovy/runtime/NioGroovyMethodsTest.groovy
----------------------------------------------------------------------
diff --git a/subprojects/groovy-nio/src/test/groovy/org/codehaus/groovy/runtime/NioGroovyMethodsTest.groovy b/subprojects/groovy-nio/src/test/groovy/org/codehaus/groovy/runtime/NioGroovyMethodsTest.groovy
index 0831163..86bc596 100644
--- a/subprojects/groovy-nio/src/test/groovy/org/codehaus/groovy/runtime/NioGroovyMethodsTest.groovy
+++ b/subprojects/groovy-nio/src/test/groovy/org/codehaus/groovy/runtime/NioGroovyMethodsTest.groovy
@@ -19,7 +19,6 @@
 package org.codehaus.groovy.runtime
 
 import java.nio.file.Files
-import java.nio.file.Path
 import java.nio.file.StandardCopyOption
 
 import groovy.io.FileType
@@ -35,6 +34,139 @@ class NioGroovyMethodsTest extends Specification {
     @Rule
     TemporaryFolder temporaryFolder
 
+    def testShouldAppendByteArrayToFile() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        path.write('Hello')
+        byte[] bytes = ' World'.bytes
+        path.append(bytes)
+
+        then:
+        path.text == 'Hello World'
+    }
+
+    def testShouldAppendStringToFileUsingDefaultEncoding() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        path.write('Hello')
+        path.append(' World')
+
+        then:
+        path.text == 'Hello World'
+    }
+
+    def testShouldAppendTextSuppliedByReaderToFileUsingDefaultEncoding() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        path.write('Hello')
+        Reader reader = new StringReader(' World')
+        path.append(reader)
+
+        then:
+        path.text == 'Hello World'
+    }
+
+    def testShouldAppendTextSuppliedByWriterToFileUsingDefaultEncoding() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        path.write('Hello')
+        Writer writer = new StringWriter()
+        writer.append(' World')
+        path.append(writer)
+
+        then:
+        path.text == 'Hello World'
+    }
+
+    def testShouldAppendStringToFileUsingSpecifiedEncoding() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        String encoding = 'UTF-8'
+        path.write('؁', encoding)
+        path.append(' ؁', encoding)
+
+        then:
+        path.getText(encoding) == '؁ ؁'
+    }
+
+    def testShouldAppendTextSuppliedByReaderToFileUsingSpecifiedEncoding() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        String encoding = 'UTF-8'
+        path.write('؁', encoding)
+        Reader reader = new CharArrayReader([' ','؁'] as char[])
+        path.append(reader, encoding)
+
+        then:
+        path.getText(encoding) == '؁ ؁'
+    }
+
+    def testShouldAppendTextSuppliedByWriterToFileUsingSpecifiedEncoding() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        String encoding = 'UTF-8'
+        path.write('؁', encoding)
+        Writer writer = new CharArrayWriter()
+        writer.append(' ')
+        writer.append('؁')
+        path.append(writer, encoding)
+
+        then:
+        path.getText(encoding) == '؁ ؁'
+    }
+
+    def testShouldAppendStringToFileUsingSpecifiedEncodingWithBom() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        Files.delete(path)
+        String encoding = 'UTF-16LE'
+        path.write('؁', encoding, true)
+        path.append(' ؁', encoding, true)
+
+        then:
+        byte[] bytes = NioGroovyMethods.getBytes(path)
+        bytes[0] == -1 as byte
+        bytes[1] == -2 as byte
+        String string = path.getText(encoding)
+        string.substring(1, string.size()) == '؁ ؁'
+    }
+
+    def testShouldAppendTextSuppliedByReaderToFileUsingSpecifiedEncodingWithBom() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        Files.delete(path)
+        String encoding = 'UTF-16LE'
+        path.write('؁', encoding, true)
+        Reader reader = new CharArrayReader([' ','؁'] as char[])
+        path.append(reader, encoding, true)
+
+        then:
+        byte[] bytes = NioGroovyMethods.getBytes(path)
+        bytes[0] == -1 as byte
+        bytes[1] == -2 as byte
+        String string = path.getText(encoding)
+        string.substring(1, string.size()) == '؁ ؁'
+    }
+
+    def testShouldAppendTextSuppliedByWriterToFileUsingSpecifiedEncodingWithBom() {
+        when:
+        def path = temporaryFolder.newFile().toPath()
+        Files.delete(path)
+        String encoding = 'UTF-16LE'
+        path.write('؁', encoding, true)
+        Writer writer = new CharArrayWriter()
+        writer.append(' ')
+        writer.append('؁')
+        path.append(writer, encoding, true)
+
+        then:
+        byte[] bytes = NioGroovyMethods.getBytes(path)
+        bytes[0] == -1 as byte
+        bytes[1] == -2 as byte
+        String string = path.getText(encoding)
+        string.substring(1, string.size()) == '؁ ؁'
+    }
+
     def testPathSize() {
         when:
         def str = 'Hello world!'
@@ -179,6 +311,16 @@ class NioGroovyMethodsTest extends Specification {
         file.text == str
     }
 
+    def testWriteWithEncodingAndBom() {
+        when:
+        def str = '؁'
+        def file = temporaryFolder.newFile()
+        file.toPath().write(str, 'UTF-16LE', true)
+
+        then:
+        assert file.getBytes() == [-1, -2, 1, 6] as byte[]
+    }
+
     def testAppendObject() {
         setup:
         def file = temporaryFolder.newFile()
@@ -325,7 +467,9 @@ class NioGroovyMethodsTest extends Specification {
     def testAppendUTF16LE() {
         setup:
         def path = temporaryFolder.newFile().toPath()
+        Files.delete(path)
         def file = temporaryFolder.newFile()
+        file.delete()
         def str = 'Hello world!'
 
         // save using a File, thus uses ResourcesGroovyMethods
@@ -351,6 +495,30 @@ class NioGroovyMethodsTest extends Specification {
         path.getText('UTF-16LE') == 'Hello world! - Hola Mundo!'
     }
 
+    def testWriteUTF16LE() {
+        setup:
+        def path = temporaryFolder.newFile().toPath()
+        def str = 'Hello world!'
+
+        when:
+        path.write(str, 'UTF-16LE')
+
+        then:
+        path.getText('UTF-16LE') == str
+    }
+
+    def testWriteUTF16LEWithBom() {
+        setup:
+        def path = temporaryFolder.newFile().toPath()
+        def str = 'Hello world!'
+
+        when:
+        path.write(str, 'UTF-16LE', true)
+
+        then:
+        assert NioGroovyMethods.getBytes(path) == [-1, -2, 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 119, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33, 0] as byte[]
+    }
+
     def testWriteUTF16BE() {
         setup:
         def path = temporaryFolder.newFile().toPath()
@@ -363,6 +531,18 @@ class NioGroovyMethodsTest extends Specification {
         path.getText('UTF-16BE') == str
     }
 
+    def testWriteUTF16BEWithBom() {
+        setup:
+        def path = temporaryFolder.newFile().toPath()
+        def str = 'Hello world!'
+
+        when:
+        path.write(str, 'UTF-16BE', true)
+
+        then:
+        assert NioGroovyMethods.getBytes(path) == [-2, -1, 0, 72, 0, 101, 0, 108, 0, 108, 0, 111, 0, 32, 0, 119, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33] as byte[]
+    }
+
     def testWithCloseable() {
         setup:
         def closeable = new DummyCloseable()