forked from stleary/JSON-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBufferedAppendable.java
More file actions
348 lines (324 loc) · 11.7 KB
/
BufferedAppendable.java
File metadata and controls
348 lines (324 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
package org.json.util;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import java.io.IOException;
import java.io.Writer;
import java.nio.CharBuffer;
/**
* A simple buffering {@code Appendable}, without the synchronization of
* {@code java.io.BufferedWriter}. This means that all operations on a
* {@code BufferedAppendable} object must be performed synchronously.
* <p>
* In addition, the {@code Appendable} to be buffered is supplied using the
* {@link #with(Appendable)} method, rather than at construction time. This
* allows the buffer to be reused for several different operations requiring
* buffering.</p>
* <p>
* Uses {@code java.nio.CharBuffer.allocate()} to create the backing buffer.</p>
* <p>
* Does <em>not</em> propagate the {@code flush()} or {@code close()} methods
* to the wrapped {@code Appendable}.</p>
*
* @author JSON.org
* @version 2016-07-08
*/
public final class BufferedAppendable extends Writer {
/** The default buffer size of 1024 characters, if none is specified. */
public static final int DEFAULT_BUFFER_SIZE = 1024;
private static final String NULL_SEQ = "null";
private final CharBuffer buffer;
private Appendable appendable;
/**
* Buffer a given {@code Appendable} with the default buffer size.
*/
public BufferedAppendable() {
this.buffer = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
}
/**
* Buffer a given {@code Appendable} with the given buffer size.
* The size must be at least 16.
*
* @param buffSize the buffer size, must be >= 16
* @throws NullPointerException the supplied Appendable is null
*/
public BufferedAppendable(int buffSize) {
if (buffSize < 16) {
throw new IllegalArgumentException("Buffsize should be at least 16");
}
this.buffer = CharBuffer.allocate(buffSize);
}
/**
* Reset this buffered appendable, setting it to buffer the given
* appendable. Any existing buffered output is flushed to the old
* {@code Appendable} first.
* <p>
* This resets the state of the buffered reader to the open state
* if the new appendable is non-{@code null}, otherwise resets the state to
* closed.</p>
*
* @param newAppendable the new {@code Appendable} to buffer
* @return this {@code BufferedAppendable}
*/
public BufferedAppendable with(Appendable newAppendable) {
try {
flushBuffer();
} catch (IOException e) {
// don't care, just set the new appender
} catch (RuntimeException e) {
// don't care, just set the new appender
}
this.appendable = newAppendable;
return this;
}
/**
* Appends the specified character sequence to this
* {@code BufferedAppendable}.
*
* @param csq the character sequence to be appended
* @return this {@code BufferedAppendable}
* @throws IOException there was a problem appending to the underlying
* appendable
*/
@Override
public BufferedAppendable append(CharSequence csq) throws IOException {
if (csq == null) {
csq = NULL_SEQ;
}
if (csq.length() > 0) {
assertOpen();
if (csq.length() < buffer.remaining()) {
buffer.append(csq);
} else {
flushBuffer();
if (csq.length() < buffer.length()) {
buffer.append(csq);
} else {
appendable.append(csq);
}
}
}
return this;
}
/**
* Appends a subsequence of the specified character sequence to this
* {@code BufferedAppendable}.
*
* @param csq The character sequence from which a subsequence will be
* appended.
* @param start The index of the first character in the subsequence
* @param end The index of the character following the last character in the
* subsequence
* @return this {@code BufferedAppendable}
* @throws IOException there was a problem appending to the underlying
* {@code Appendable}
*/
@Override
public BufferedAppendable append(CharSequence csq, int start, int end) throws IOException {
final int len = end - start;
if ((start < 0) || (len < 0)) {
throw new IllegalArgumentException("start or end out of bounds");
}
if (csq == null) {
csq = NULL_SEQ;
}
if (end > csq.length()) {
throw new IllegalArgumentException("end index out of bounds");
}
if (len > 0) {
assertOpen();
if (len < buffer.remaining()) {
buffer.append(csq, start, end);
} else {
flushBuffer();
if (len < buffer.length()) {
buffer.append(csq, start, end);
} else {
appendable.append(csq, start, end);
}
}
}
return this;
}
/**
* Appends the specified character to this {@code BufferedAppendable}.
*
* @param c The character to append
* @return this {@code BufferedAppendable}
* @throws IOException there was a problem appending to the underlying
* {@code Appendable}
*/
@Override
public BufferedAppendable append(char c) throws IOException {
if (appendable == null) {
throw new IOException("Buffered appendable is not open");
}
if (! buffer.hasRemaining()) {
flushBuffer();
}
buffer.append(c);
return this;
}
/**
* Check that this buffer is currently open.
*
* @throws IOException the buffered appendable is in a closed state
*/
private void assertOpen() throws IOException {
if (appendable == null) {
throw new IOException("Buffered appendable is not open");
}
}
/**
* Flushes and clears the internal buffer. An {@code assertOpen()} is
* called if the buffer needs to be written to the current
* {@code Appendable}.
*
* @throws IOException there was a problem flushing the buffer to the
* current {@code Appendable}
*/
private void flushBuffer() throws IOException {
final int pos = buffer.position();
if(pos > 0) {
try {
assertOpen();
buffer.flip();
appendable.append(buffer, 0, pos);
} finally {
buffer.clear();
}
}
}
// -- Fulfill the Writer contract in terms of Appendable, above --
/**
* Writes a single character. The character to be written is contained in
* the 16 low-order bits of the given integer value; the 16 high-order bits
* are ignored.
*
* @param c int specifying a character to be written
* @throws IOException If an I/O error occurs
*/
@Override
public void write(int c) throws IOException {
// inline for single char append
if (appendable == null) {
throw new IOException("Buffered appendable is not open");
}
if (! buffer.hasRemaining()) {
flushBuffer();
}
buffer.append((char)c);
}
/**
* Writes an array of characters.
*
* @param cbuf Array of characters to be written
*
* @throws IOException If an I/O error occurs
* @throws NullPointerException cbuf is {@code null}
*/
@Override
public void write(char[] cbuf) throws IOException {
if(cbuf.length > 0) {
append(CharBuffer.wrap(cbuf));
}
}
/**
* Writes a portion of an array of characters.
*
* @param cbuf Array of characters
* @param off Offset from which to start writing characters
* @param len Number of characters to write
* @throws IOException If an I/O error occurs
* @throws NullPointerException cbuf is {@code null}
* @throws IndexOutOfBoundsException off or len are out of bounds
*/
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
append(CharBuffer.wrap(cbuf, off, len));
}
/**
* Writes a string.
*
* @param str String to be written
* @throws IOException If an I/O error occurs
* @throws NullPointerException str is {@code null}
*/
@Override
public void write(String str) throws IOException {
if(str.length() > 0) {
append(str);
}
}
/**
* Writes a portion of a string.
*
* @param str A String
* @param off Offset from which to start writing characters
* @param len Number of characters to write
* @throws IndexOutOfBoundsException
* If {@code off} is negative, or {@code len} is negative,
* or {@code off+len} is negative or greater than the length
* of the given string
* @throws IOException If an I/O error occurs
* @throws NullPointerException str is {@code null}
*/
@Override
public void write(String str, int off, int len) throws IOException {
if ((off < 0) || (off > str.length()) || (len < 0) ||
((off + len) > str.length()) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
append(str, off, off + len);
}
/**
* Flush the buffer of this {@code BufferedAppendable}.
*
* @throws IOException there was a problem appending to the underlying
* {@code Appendable}
*/
@Override
public void flush() throws IOException {
flushBuffer();
}
/**
* Flush and close the buffer of this {@code BufferedAppendable}.
*
* @throws IOException there was a problem appending to the underlying
* {@code Appendable}
*/
@Override
public void close() throws IOException {
if (appendable != null) {
try {
flushBuffer();
} finally {
appendable = null;
}
}
}
}