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 
18 package java.io;
19 
20 import 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  */
30 public 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      */
CharArrayWriter()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      */
CharArrayWriter(int initialSize)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
close()74     public void close() {
75         /* empty */
76     }
77 
expand(int i)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
flush()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      */
reset()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      */
size()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      */
toCharArray()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
toString()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
write(char[] buffer, int offset, int len)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
write(int oneChar)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
write(String str, int offset, int count)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      */
writeTo(Writer out)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
append(char c)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
append(CharSequence csq)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
append(CharSequence csq, int start, int end)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