1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.io;
19
20import java.util.Arrays;
21
22/**
23 * A specialized {@link Writer} that writes characters to a {@code StringBuffer}
24 * in a sequential manner, appending them in the process. The result can later
25 * be queried using the {@link #StringWriter(int)} or {@link #toString()}
26 * methods.
27 *
28 * @see StringReader
29 */
30public class StringWriter extends Writer {
31
32    private StringBuffer buf;
33
34    /**
35     * Constructs a new {@code StringWriter} which has a {@link StringBuffer}
36     * allocated with the default size of 16 characters. The {@code
37     * StringBuffer} is also the {@code lock} used to synchronize access to this
38     * writer.
39     */
40    public StringWriter() {
41        buf = new StringBuffer(16);
42        lock = buf;
43    }
44
45    /**
46     * Constructs a new {@code StringWriter} which has a {@link StringBuffer}
47     * allocated with a size of {@code initialSize} characters. The {@code
48     * StringBuffer} is also the {@code lock} used to synchronize access to this
49     * writer.
50     *
51     * @param initialSize
52     *            the initial size of the target string buffer.
53     */
54    public StringWriter(int initialSize) {
55        if (initialSize < 0) {
56            throw new IllegalArgumentException("initialSize < 0: " + initialSize);
57        }
58        buf = new StringBuffer(initialSize);
59        lock = buf;
60    }
61
62    /**
63     * Calling this method has no effect. In contrast to most {@code Writer} subclasses,
64     * the other methods in {@code StringWriter} do not throw an {@code IOException} if
65     * {@code close()} has been called.
66     *
67     * @throws IOException
68     *             if an error occurs while closing this writer.
69     */
70    @Override
71    public void close() throws IOException {
72        /* empty */
73    }
74
75    /**
76     * Calling this method has no effect.
77     */
78    @Override
79    public void flush() {
80        /* empty */
81    }
82
83    /**
84     * Gets a reference to this writer's internal {@link StringBuffer}. Any
85     * changes made to the returned buffer are reflected in this writer.
86     *
87     * @return a reference to this writer's internal {@code StringBuffer}.
88     */
89    public StringBuffer getBuffer() {
90        return buf;
91    }
92
93    /**
94     * Gets a copy of the contents of this writer as a string.
95     *
96     * @return this writer's contents as a string.
97     */
98    @Override
99    public String toString() {
100        return buf.toString();
101    }
102
103    /**
104     * Writes {@code count} characters starting at {@code offset} in {@code buf}
105     * to this writer's {@code StringBuffer}.
106     *
107     * @param chars
108     *            the non-null character array to write.
109     * @param offset
110     *            the index of the first character in {@code chars} to write.
111     * @param count
112     *            the maximum number of characters to write.
113     * @throws IndexOutOfBoundsException
114     *             if {@code offset < 0} or {@code count < 0}, or if {@code
115     *             offset + count} is greater than the size of {@code buf}.
116     */
117    @Override
118    public void write(char[] chars, int offset, int count) {
119        Arrays.checkOffsetAndCount(chars.length, offset, count);
120        if (count == 0) {
121            return;
122        }
123        buf.append(chars, offset, count);
124    }
125
126    /**
127     * Writes one character to this writer's {@code StringBuffer}. Only the two
128     * least significant bytes of the integer {@code oneChar} are written.
129     *
130     * @param oneChar
131     *            the character to write to this writer's {@code StringBuffer}.
132     */
133    @Override
134    public void write(int oneChar) {
135        buf.append((char) oneChar);
136    }
137
138    /**
139     * Writes the characters from the specified string to this writer's {@code
140     * StringBuffer}.
141     *
142     * @param str
143     *            the non-null string containing the characters to write.
144     */
145    @Override
146    public void write(String str) {
147        buf.append(str);
148    }
149
150    /**
151     * Writes {@code count} characters from {@code str} starting at {@code
152     * offset} to this writer's {@code StringBuffer}.
153     *
154     * @param str
155     *            the non-null string containing the characters to write.
156     * @param offset
157     *            the index of the first character in {@code str} to write.
158     * @param count
159     *            the number of characters from {@code str} to write.
160     * @throws StringIndexOutOfBoundsException
161     *             if {@code offset < 0} or {@code count < 0}, or if {@code
162     *             offset + count} is greater than the length of {@code str}.
163     */
164    @Override
165    public void write(String str, int offset, int count) {
166        String sub = str.substring(offset, offset + count);
167        buf.append(sub);
168    }
169
170    /**
171     * Appends the character {@code c} to this writer's {@code StringBuffer}.
172     * This method works the same way as {@link #write(int)}.
173     *
174     * @param c
175     *            the character to append to the target stream.
176     * @return this writer.
177     */
178    @Override
179    public StringWriter append(char c) {
180        write(c);
181        return this;
182    }
183
184    /**
185     * Appends the character sequence {@code csq} to this writer's {@code
186     * StringBuffer}. This method works the same way as {@code
187     * StringWriter.write(csq.toString())}. If {@code csq} is {@code null}, then
188     * the string "null" is written to the target stream.
189     *
190     * @param csq
191     *            the character sequence appended to the target.
192     * @return this writer.
193     */
194    @Override
195    public StringWriter append(CharSequence csq) {
196        if (csq == null) {
197            csq = "null";
198        }
199        write(csq.toString());
200        return this;
201    }
202
203    /**
204     * Appends a subsequence of the character sequence {@code csq} to this
205     * writer's {@code StringBuffer}. This method works the same way as {@code
206     * StringWriter.writer(csq.subsequence(start, end).toString())}. If {@code
207     * csq} is {@code null}, then the specified subsequence of the string "null"
208     * will be written to the target.
209     *
210     * @param csq
211     *            the character sequence appended to the target.
212     * @param start
213     *            the index of the first char in the character sequence appended
214     *            to the target.
215     * @param end
216     *            the index of the character following the last character of the
217     *            subsequence appended to the target.
218     * @return this writer.
219     * @throws IndexOutOfBoundsException
220     *             if {@code start > end}, {@code start < 0}, {@code end < 0} or
221     *             either {@code start} or {@code end} are greater or equal than
222     *             the length of {@code csq}.
223     */
224    @Override
225    public StringWriter append(CharSequence csq, int start, int end) {
226        if (csq == null) {
227            csq = "null";
228        }
229        String output = csq.subSequence(start, end).toString();
230        write(output, 0, output.length());
231        return this;
232    }
233}
234