    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}