This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push: new bae3ef5 Added AppendableWriter. (#87) bae3ef5 is described below commit bae3ef5c23f46f8458da105ee3864b356a2007e3 Author: Rob Spoor <robti...@users.noreply.github.com> AuthorDate: Fri Aug 9 16:41:53 2019 +0200 Added AppendableWriter. (#87) * Added AppendableWriter. * Added IOUtils.writer method. --- src/main/java/org/apache/commons/io/IOUtils.java | 22 +++ .../apache/commons/io/output/AppendableWriter.java | 162 +++++++++++++++++++++ .../org/apache/commons/io/IOUtilsTestCase.java | 29 ++++ .../commons/io/output/AppendableWriterTest.java | 88 +++++++++++ 4 files changed, 301 insertions(+) diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java index 12a44dc..64240f2 100644 --- a/src/main/java/org/apache/commons/io/IOUtils.java +++ b/src/main/java/org/apache/commons/io/IOUtils.java @@ -47,7 +47,9 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; +import org.apache.commons.io.output.AppendableWriter; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.io.output.StringBuilderWriter; @@ -296,6 +298,26 @@ public class IOUtils { } /** + * Returns the given Appendable if it is already a {@link Writer}, otherwise creates a Writer wrapper around the + * given Appendable. + * + * @param appendable the Appendable to wrap or return (not null) + * @return the given Appendable or a Writer wrapper around the given Appendable + * @throws NullPointerException if the input parameter is null + * @since 2.7 + */ + public static Writer writer(final Appendable appendable) { + Objects.requireNonNull(appendable, "appendable"); + if (appendable instanceof Writer) { + return (Writer) appendable; + } + if (appendable instanceof StringBuilder) { + return new StringBuilderWriter((StringBuilder) appendable); + } + return new AppendableWriter<>(appendable); + } + + /** * Closes a URLConnection. * * @param conn the connection to close. diff --git a/src/main/java/org/apache/commons/io/output/AppendableWriter.java b/src/main/java/org/apache/commons/io/output/AppendableWriter.java new file mode 100644 index 0000000..93b2d4f --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/AppendableWriter.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.io.output; + +import java.io.IOException; +import java.io.Writer; +import java.util.Objects; + +/** + * Writer implementation that writes the data to an {@link Appendable} + * Object. + * <p> + * For example, can be used with a {@link java.lang.StringBuilder} + * or {@link java.lang.StringBuffer}. + * + * @since 2.7 + * @see java.lang.Appendable + * + * @param <T> The type of the {@link Appendable} wrapped by this AppendableWriter. + */ +public class AppendableWriter <T extends Appendable> extends Writer { + + private final T appendable; + + /** + * Construct a new instance with the specified appendable. + * + * @param appendable the appendable to write to + */ + public AppendableWriter(final T appendable) { + this.appendable = appendable; + } + + /** + * Write a character to the underlying appendable. + * + * @param c the character to write + * @throws IOException upon error + */ + @Override + public void write(final int c) throws IOException { + appendable.append((char)c); + } + + /** + * Writes a portion of an array of characters to the underlying appendable. + * + * @param cbuf an array with the characters to write + * @param off offset from which to start writing characters + * @param len number of characters to write + * @throws IOException upon error + */ + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + Objects.requireNonNull(cbuf, "Character array is missing"); + if (len < 0 || (off + len) > cbuf.length) { + throw new IndexOutOfBoundsException("Array Size=" + cbuf.length + + ", offset=" + off + ", length=" + len); + } + for (int i = 0; i < len; i++) { + appendable.append(cbuf[off + i]); + } + } + + /** + * Writes a portion of a String to the underlying appendable. + * + * @param str a string + * @param off offset from which to start writing characters + * @param len number of characters to write + * @throws IOException upon error + */ + @Override + public void write(final String str, final int off, final int len) throws IOException { + // appendable.append will add "null" for a null String; add an explicit null check + Objects.requireNonNull(str, "String is missing"); + appendable.append(str, off, off + len); + } + + /** + * Appends the specified character sequence to the underlying appendable. + * + * @param csq the character sequence to append + * @return this writer + * @throws IOException upon error + */ + @Override + public Writer append(final CharSequence csq) throws IOException { + appendable.append(csq); + return this; + } + + /** + * Appends a subsequence of the specified character sequence to the underlying appendable. + * + * @param csq the character sequence from which a subsequence will be appended + * @param start the index of the first character in the subsequence + * @param end the index of the character following the last character in the subsequence + * @return this writer + * @throws IOException upon error + */ + @Override + public Writer append(final CharSequence csq, final int start, final int end) throws IOException { + appendable.append(csq, start, end); + return this; + } + + /** + * Appends the specified character to the underlying appendable. + * + * @param c the character to append + * @return this writer + * @throws IOException upon error + */ + @Override + public Writer append(final char c) throws IOException { + appendable.append(c); + return this; + } + + /** + * Flushes the stream. This implementation does nothing. + * + * @throws IOException upon error + */ + @Override + public void flush() throws IOException { + } + + /** + * Closes the stream. This implementation does nothing. + * + * @throws IOException upon error + */ + @Override + public void close() throws IOException { + } + + /** + * Return the target appendable. + * + * @return the target appendable + */ + public T getAppendable() { + return appendable; + } + +} diff --git a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java index 0058133..f5b5d9d 100644 --- a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java +++ b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java @@ -58,6 +58,8 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; +import org.apache.commons.io.output.AppendableWriter; +import org.apache.commons.io.output.StringBuilderWriter; import org.apache.commons.io.testtools.TestUtils; import org.junit.Assert; import org.junit.Before; @@ -1514,6 +1516,33 @@ public class IOUtilsTestCase { assertSame(bw, IOUtils.buffer(bw, 1024)); } + @Test + public void testAsWriterNull() { + try { + IOUtils.writer(null); + fail("Expected NullPointerException"); + } catch (final NullPointerException npe) { + // expected + } + } + + @Test + public void testAsWriterStringBuilder() { + final Appendable a = new StringBuilder(); + final Writer w = IOUtils.writer(a); + assertNotSame(w, a); + assertEquals(StringBuilderWriter.class, w.getClass()); + assertSame(w, IOUtils.writer(w)); + } + + @Test + public void testAsWriterAppendable() { + final Appendable a = new StringBuffer(); + final Writer w = IOUtils.writer(a); + assertNotSame(w, a); + assertEquals(AppendableWriter.class, w.getClass()); + assertSame(w, IOUtils.writer(w)); + } @Test public void testCopyLarge_SkipWithInvalidOffset() throws IOException { diff --git a/src/test/java/org/apache/commons/io/output/AppendableWriterTest.java b/src/test/java/org/apache/commons/io/output/AppendableWriterTest.java new file mode 100644 index 0000000..f99f8c9 --- /dev/null +++ b/src/test/java/org/apache/commons/io/output/AppendableWriterTest.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.io.output; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests for {@link AppendableWriter}. + * + */ +public class AppendableWriterTest { + + private AppendableWriter<StringBuilder> out; + + @Before + public void setUp() throws Exception { + out = new AppendableWriter<>(new StringBuilder()); + } + + @Test + public void testWriteInt() throws Exception { + out.write('F'); + + assertEquals("F", out.getAppendable().toString()); + } + + @Test + public void testWriteChars() throws Exception { + final String testData = "ABCD"; + + out.write(testData.toCharArray()); + + assertEquals(testData, out.getAppendable().toString()); + } + + @Test + public void testWriteString() throws Exception { + final String testData = "ABCD"; + + out.write(testData); + + assertEquals(testData, out.getAppendable().toString()); + } + + @Test + public void testAppendCharSequence() throws Exception { + final String testData = "ABCD"; + + out.append(testData); + out.append(null); + + assertEquals(testData + "null", out.getAppendable().toString()); + } + + @Test + public void testAppendSubSequence() throws Exception { + final String testData = "ABCD"; + + out.append(testData, 1, 3); + out.append(null, 1, 3); + + assertEquals(testData.substring(1, 3) + "ul", out.getAppendable().toString()); + } + + @Test + public void testAppendChar() throws Exception { + out.append('F'); + + assertEquals("F", out.getAppendable().toString()); + } +}