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} for class for writing content to an (internal)
24 * char array. As bytes are written to this writer, the char array may be
25 * expanded to hold more characters. When the writing is considered to be
26 * finished, a copy of the char array can be requested from the class.
27 *
28 * @see CharArrayReader
29 */
30public class CharArrayWriter extends Writer {
31
32    /**
33     * The buffer for characters.
34     */
35    protected char[] buf;
36
37    /**
38     * The ending index of the buffer.
39     */
40    protected int count;
41
42    /**
43     * Constructs a new {@code CharArrayWriter} which has a buffer allocated
44     * with the default size of 32 characters. This buffer is also used as the
45     * {@code lock} to synchronize access to this writer.
46     */
47    public CharArrayWriter() {
48        buf = new char[32];
49        lock = buf;
50    }
51
52    /**
53     * Constructs a new {@code CharArrayWriter} which has a buffer allocated
54     * with the size of {@code initialSize} characters. The buffer is also used
55     * as the {@code lock} to synchronize access to this writer.
56     *
57     * @param initialSize
58     *            the initial size of this CharArrayWriters buffer.
59     * @throws IllegalArgumentException
60     *             if {@code initialSize < 0}.
61     */
62    public CharArrayWriter(int initialSize) {
63        if (initialSize < 0) {
64            throw new IllegalArgumentException("size < 0");
65        }
66        buf = new char[initialSize];
67        lock = buf;
68    }
69
70    /**
71     * Closes this writer. The implementation in {@code CharArrayWriter} does nothing.
72     */
73    @Override
74    public void close() {
75        /* empty */
76    }
77
78    private void expand(int i) {
79        /* Can the buffer handle @i more chars, if not expand it */
80        if (count + i <= buf.length) {
81            return;
82        }
83
84        int newLen = Math.max(2 * buf.length, count + i);
85        char[] newbuf = new char[newLen];
86        System.arraycopy(buf, 0, newbuf, 0, count);
87        buf = newbuf;
88    }
89
90    /**
91     * Flushes this writer. The implementation in {@code CharArrayWriter} does nothing.
92     */
93    @Override
94    public void flush() {
95        /* empty */
96    }
97
98    /**
99     * Resets this writer. The current write position is reset to the beginning
100     * of the buffer. All written characters are lost and the size of this
101     * writer is set to 0.
102     */
103    public void reset() {
104        synchronized (lock) {
105            count = 0;
106        }
107    }
108
109    /**
110     * Returns the size of this writer, that is the number of characters it
111     * stores. This number changes if this writer is reset or when more
112     * characters are written to it.
113     *
114     * @return this CharArrayWriter's current size in characters.
115     */
116    public int size() {
117        synchronized (lock) {
118            return count;
119        }
120    }
121
122    /**
123     * Returns the contents of the receiver as a char array. The array returned
124     * is a copy and any modifications made to this writer after calling this
125     * method are not reflected in the result.
126     *
127     * @return this CharArrayWriter's contents as a new char array.
128     */
129    public char[] toCharArray() {
130        synchronized (lock) {
131            char[] result = new char[count];
132            System.arraycopy(buf, 0, result, 0, count);
133            return result;
134        }
135    }
136
137    /**
138     * Returns the contents of this {@code CharArrayWriter} as a string. The
139     * string returned is a copy and any modifications made to this writer after
140     * calling this method are not reflected in the result.
141     *
142     * @return this CharArrayWriters contents as a new string.
143     */
144    @Override
145    public String toString() {
146        synchronized (lock) {
147            return new String(buf, 0, count);
148        }
149    }
150
151    /**
152     * Writes {@code count} characters starting at {@code offset} in {@code c}
153     * to this writer.
154     *
155     * @param buffer
156     *            the non-null array containing characters to write.
157     * @param offset
158     *            the index of the first character in {@code buf} to write.
159     * @param len
160     *            maximum number of characters to write.
161     * @throws IndexOutOfBoundsException
162     *             if {@code offset < 0} or {@code len < 0}, or if
163     *             {@code offset + len} is bigger than the size of {@code c}.
164     */
165    @Override
166    public void write(char[] buffer, int offset, int len) {
167        Arrays.checkOffsetAndCount(buffer.length, offset, len);
168        synchronized (lock) {
169            expand(len);
170            System.arraycopy(buffer, offset, this.buf, this.count, len);
171            this.count += len;
172        }
173    }
174
175    /**
176     * Writes the specified character {@code oneChar} to this writer.
177     * This implementation writes the two low order bytes of the integer
178     * {@code oneChar} to the buffer.
179     *
180     * @param oneChar
181     *            the character to write.
182     */
183    @Override
184    public void write(int oneChar) {
185        synchronized (lock) {
186            expand(1);
187            buf[count++] = (char) oneChar;
188        }
189    }
190
191    /**
192     * Writes {@code count} characters starting at {@code offset} from
193     * the string {@code str} to this CharArrayWriter.
194     *
195     * @throws NullPointerException
196     *             if {@code str} is {@code null}.
197     * @throws StringIndexOutOfBoundsException
198     *             if {@code offset < 0} or {@code count < 0}, or if
199     *             {@code offset + count} is bigger than the length of
200     *             {@code str}.
201     */
202    @Override
203    public void write(String str, int offset, int count) {
204        if (str == null) {
205            throw new NullPointerException("str == null");
206        }
207        if ((offset | count) < 0 || offset > str.length() - count) {
208            throw new StringIndexOutOfBoundsException(str, offset, count);
209        }
210        synchronized (lock) {
211            expand(count);
212            str.getChars(offset, offset + count, buf, this.count);
213            this.count += count;
214        }
215    }
216
217    /**
218     * Writes the contents of this {@code CharArrayWriter} to another {@code
219     * Writer}. The output is all the characters that have been written to the
220     * receiver since the last reset or since it was created.
221     *
222     * @param out
223     *            the non-null {@code Writer} on which to write the contents.
224     * @throws NullPointerException
225     *             if {@code out} is {@code null}.
226     * @throws IOException
227     *             if an error occurs attempting to write out the contents.
228     */
229    public void writeTo(Writer out) throws IOException {
230        synchronized (lock) {
231            out.write(buf, 0, count);
232        }
233    }
234
235    /**
236     * Appends a char {@code c} to the {@code CharArrayWriter}. The method works
237     * the same way as {@code write(c)}.
238     *
239     * @param c
240     *            the character appended to the CharArrayWriter.
241     * @return this CharArrayWriter.
242     */
243    @Override
244    public CharArrayWriter append(char c) {
245        write(c);
246        return this;
247    }
248
249    /**
250     * Appends a {@code CharSequence} to the {@code CharArrayWriter}. The method
251     * works the same way as {@code write(csq.toString())}. If {@code csq} is
252     * {@code null}, then it will be substituted with the string {@code "null"}.
253     *
254     * @param csq
255     *            the {@code CharSequence} appended to the {@code
256     *            CharArrayWriter}, may be {@code null}.
257     * @return this CharArrayWriter.
258     */
259    @Override
260    public CharArrayWriter append(CharSequence csq) {
261        if (csq == null) {
262            csq = "null";
263        }
264        append(csq, 0, csq.length());
265        return this;
266    }
267
268    /**
269     * Append a subsequence of a {@code CharSequence} to the {@code
270     * CharArrayWriter}. The first and last characters of the subsequence are
271     * specified by the parameters {@code start} and {@code end}. A call to
272     * {@code CharArrayWriter.append(csq)} works the same way as {@code
273     * CharArrayWriter.write(csq.subSequence(start, end).toString)}. If {@code
274     * csq} is {@code null}, then it will be substituted with the string {@code
275     * "null"}.
276     *
277     * @param csq
278     *            the {@code CharSequence} appended to the {@code
279     *            CharArrayWriter}, may be {@code null}.
280     * @param start
281     *            the index of the first character in the {@code CharSequence}
282     *            appended to the {@code CharArrayWriter}.
283     * @param end
284     *            the index of the character after the last one in the {@code
285     *            CharSequence} appended to the {@code CharArrayWriter}.
286     * @return this CharArrayWriter.
287     * @throws IndexOutOfBoundsException
288     *             if {@code start < 0}, {@code end < 0}, {@code start > end},
289     *             or if {@code end} is greater than the length of {@code csq}.
290     */
291    @Override
292    public CharArrayWriter append(CharSequence csq, int start, int end) {
293        if (csq == null) {
294            csq = "null";
295        }
296        String output = csq.subSequence(start, end).toString();
297        write(output, 0, output.length());
298        return this;
299    }
300}
301