001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.io.output;
019
020import java.io.IOException;
021import java.io.Writer;
022import java.util.Objects;
023
024import org.apache.commons.io.IOUtils;
025
026/**
027 * Writer implementation that writes the data to an {@link Appendable} Object.
028 * <p>
029 * For example, can be used with a {@link StringBuilder} or {@link StringBuffer}.
030 * </p>
031 *
032 * @since 2.7
033 * @see Appendable
034 * @param <T> The type of the {@link Appendable} wrapped by this AppendableWriter.
035 */
036public class AppendableWriter<T extends Appendable> extends Writer {
037
038    private final T appendable;
039
040    /**
041     * Constructs a new instance with the specified appendable.
042     *
043     * @param appendable the appendable to write to.
044     */
045    public AppendableWriter(final T appendable) {
046        this.appendable = appendable;
047    }
048
049    /**
050     * Appends the specified character to the underlying appendable.
051     *
052     * @param c the character to append.
053     * @return this writer.
054     * @throws IOException If an I/O error occurs.
055     */
056    @Override
057    public Writer append(final char c) throws IOException {
058        appendable.append(c);
059        return this;
060    }
061
062    /**
063     * Appends the specified character sequence to the underlying appendable.
064     *
065     * @param csq the character sequence to append.
066     * @return this writer.
067     * @throws IOException If an I/O error occurs.
068     */
069    @Override
070    public Writer append(final CharSequence csq) throws IOException {
071        appendable.append(csq);
072        return this;
073    }
074
075    /**
076     * Appends a subsequence of the specified character sequence to the underlying appendable.
077     *
078     * @param csq   the character sequence from which a subsequence will be appended
079     * @param start the index of the first character in the subsequence
080     * @param end   the index of the character following the last character in the subsequence
081     * @return this writer
082     * @throws IndexOutOfBoundsException If {@code start} or {@code end} are negative, {@code start} is greater than
083     *                                   {@code end}, or {@code end} is greater than {@code csq.length()}.
084     * @throws IOException If an I/O error occurs.
085     */
086    @Override
087    public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
088        appendable.append(csq, start, end);
089        return this;
090    }
091
092    /**
093     * Closes the stream. This implementation does nothing.
094     *
095     * @throws IOException Thrown by a subclass.
096     */
097    @Override
098    public void close() throws IOException {
099        // noop
100    }
101
102    /**
103     * Flushes the stream. This implementation does nothing.
104     *
105     * @throws IOException Thrown by a subclass.
106     */
107    @Override
108    public void flush() throws IOException {
109        // noop
110    }
111
112    /**
113     * Gets the target appendable.
114     *
115     * @return the target appendable.
116     */
117    public T getAppendable() {
118        return appendable;
119    }
120
121    /**
122     * Writes a portion of an array of characters to the underlying appendable.
123     *
124     * @param cbuf an array with the characters to write.
125     * @param off  offset from which to start writing characters.
126     * @param len  number of characters to write.
127     * @throws NullPointerException if the array is {@code null}.
128     * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code cbuf.length}.
129     * @throws IOException If an I/O error occurs.
130     */
131    @Override
132    public void write(final char[] cbuf, final int off, final int len) throws IOException {
133        IOUtils.checkFromIndexSize(cbuf, off, len);
134        for (int i = 0; i < len; i++) {
135            appendable.append(cbuf[off + i]);
136        }
137    }
138
139    /**
140     * Writes a character to the underlying appendable.
141     *
142     * @param c the character to write.
143     * @throws IOException If an I/O error occurs.
144     */
145    @Override
146    public void write(final int c) throws IOException {
147        appendable.append((char) c);
148    }
149
150    /**
151     * Writes a portion of a String to the underlying appendable.
152     *
153     * @param str a string.
154     * @param off offset from which to start writing characters.
155     * @param len number of characters to write.
156     * @throws NullPointerException if the string is {@code null}.
157     * @throws IndexOutOfBoundsException if {@code off} or {@code len} are negative, or if {@code off + len} is greater than {@code str.length()}.
158     * @throws IOException If an I/O error occurs.
159     */
160    @Override
161    public void write(final String str, final int off, final int len) throws IOException {
162        // appendable.append will add "null" for a null String; add an explicit null check
163        Objects.requireNonNull(str, "str");
164        appendable.append(str, off, off + len);
165    }
166}