1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
package org.apache.myfaces.shared.util; |
20 | |
|
21 | |
import java.io.EOFException; |
22 | |
import java.io.Externalizable; |
23 | |
import java.io.IOException; |
24 | |
import java.io.ObjectInput; |
25 | |
import java.io.ObjectOutput; |
26 | |
import java.io.Reader; |
27 | |
import java.io.Writer; |
28 | |
import java.lang.ref.SoftReference; |
29 | |
import java.util.ArrayList; |
30 | |
import java.util.HashMap; |
31 | |
import java.util.HashSet; |
32 | |
import java.util.Iterator; |
33 | |
import java.util.List; |
34 | |
import java.util.Map; |
35 | |
import java.util.Set; |
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 | 26 | public class StreamCharBuffer implements CharSequence, |
223 | |
Externalizable |
224 | |
{ |
225 | |
static final long serialVersionUID = 5486972234419632945L; |
226 | |
|
227 | |
|
228 | 1 | private static final int DEFAULT_CHUNK_SIZE = Integer.getInteger( |
229 | |
"oam.streamcharbuffer.chunksize", 512); |
230 | 1 | private static final int DEFAULT_MAX_CHUNK_SIZE = Integer.getInteger( |
231 | |
"oam.streamcharbuffer.maxchunksize", 1024 * 1024); |
232 | 1 | private static final int DEFAULT_CHUNK_SIZE_GROW_PROCENT = Integer |
233 | |
.getInteger("oam.streamcharbuffer.growprocent", 100); |
234 | 1 | private static final int SUB_BUFFERCHUNK_MIN_SIZE = Integer.getInteger( |
235 | |
"oam.streamcharbuffer.subbufferchunkminsize", 512); |
236 | 1 | private static final int SUB_STRINGCHUNK_MIN_SIZE = Integer.getInteger( |
237 | |
"oam.streamcharbuffer.substringchunkminsize", 512); |
238 | 1 | private static final int WRITE_DIRECT_MIN_SIZE = Integer.getInteger( |
239 | |
"oam.streamcharbuffer.writedirectminsize", 1024); |
240 | 1 | private static final int CHUNK_MIN_SIZE = Integer.getInteger( |
241 | |
"oam.streamcharbuffer.chunkminsize", 256); |
242 | |
|
243 | |
private final int firstChunkSize; |
244 | |
private final int growProcent; |
245 | |
private final int maxChunkSize; |
246 | 7 | private int subStringChunkMinSize = SUB_STRINGCHUNK_MIN_SIZE; |
247 | 7 | private int subBufferChunkMinSize = SUB_BUFFERCHUNK_MIN_SIZE; |
248 | 7 | private int writeDirectlyToConnectedMinSize = WRITE_DIRECT_MIN_SIZE; |
249 | 7 | private int chunkMinSize = CHUNK_MIN_SIZE; |
250 | |
|
251 | |
private int chunkSize; |
252 | |
private int totalChunkSize; |
253 | |
|
254 | |
private final StreamCharBufferWriter writer; |
255 | |
private List<ConnectedWriter> connectedWriters; |
256 | |
private Writer connectedWritersWriter; |
257 | |
|
258 | 7 | boolean preferSubChunkWhenWritingToOtherBuffer = false; |
259 | |
|
260 | |
private AllocatedBuffer allocBuffer; |
261 | |
private AbstractChunk firstChunk; |
262 | |
private AbstractChunk lastChunk; |
263 | |
private int totalCharsInList; |
264 | |
private int totalCharsInDynamicChunks; |
265 | |
private int sizeAtLeast; |
266 | 7 | private StreamCharBufferKey bufferKey = new StreamCharBufferKey(); |
267 | |
private Map<StreamCharBufferKey, StreamCharBufferSubChunk> dynamicChunkMap; |
268 | |
|
269 | |
private Set<SoftReference<StreamCharBufferKey>> parentBuffers; |
270 | 7 | int allocatedBufferIdSequence = 0; |
271 | 7 | int readerCount = 0; |
272 | 7 | boolean hasReaders = false; |
273 | |
|
274 | |
public StreamCharBuffer() |
275 | |
{ |
276 | 0 | this(DEFAULT_CHUNK_SIZE, DEFAULT_CHUNK_SIZE_GROW_PROCENT, |
277 | |
DEFAULT_MAX_CHUNK_SIZE); |
278 | 0 | } |
279 | |
|
280 | |
public StreamCharBuffer(int chunkSize) |
281 | |
{ |
282 | 0 | this(chunkSize, DEFAULT_CHUNK_SIZE_GROW_PROCENT, DEFAULT_MAX_CHUNK_SIZE); |
283 | 0 | } |
284 | |
|
285 | |
public StreamCharBuffer(int chunkSize, int growProcent) |
286 | |
{ |
287 | 7 | this(chunkSize, growProcent, DEFAULT_MAX_CHUNK_SIZE); |
288 | 7 | } |
289 | |
|
290 | |
public StreamCharBuffer(int chunkSize, int growProcent, int maxChunkSize) |
291 | 7 | { |
292 | 7 | this.firstChunkSize = chunkSize; |
293 | 7 | this.growProcent = growProcent; |
294 | 7 | this.maxChunkSize = maxChunkSize; |
295 | 7 | writer = new StreamCharBufferWriter(); |
296 | 7 | reset(true); |
297 | 7 | } |
298 | |
|
299 | 14 | private class StreamCharBufferKey |
300 | |
{ |
301 | |
StreamCharBuffer getBuffer() |
302 | |
{ |
303 | 0 | return StreamCharBuffer.this; |
304 | |
} |
305 | |
} |
306 | |
|
307 | |
public boolean isPreferSubChunkWhenWritingToOtherBuffer() |
308 | |
{ |
309 | 0 | return preferSubChunkWhenWritingToOtherBuffer; |
310 | |
} |
311 | |
|
312 | |
public void setPreferSubChunkWhenWritingToOtherBuffer( |
313 | |
boolean preferSubChunkWhenWritingToOtherBuffer) |
314 | |
{ |
315 | 0 | this.preferSubChunkWhenWritingToOtherBuffer = preferSubChunkWhenWritingToOtherBuffer; |
316 | 0 | } |
317 | |
|
318 | |
public final void reset() |
319 | |
{ |
320 | 7 | reset(true); |
321 | 7 | } |
322 | |
|
323 | |
|
324 | |
|
325 | |
|
326 | |
|
327 | |
|
328 | |
public final void reset(boolean resetChunkSize) |
329 | |
{ |
330 | 14 | firstChunk = null; |
331 | 14 | lastChunk = null; |
332 | 14 | totalCharsInList = 0; |
333 | 14 | totalCharsInDynamicChunks = -1; |
334 | 14 | sizeAtLeast = -1; |
335 | 14 | if (resetChunkSize) |
336 | |
{ |
337 | 14 | chunkSize = firstChunkSize; |
338 | 14 | totalChunkSize = 0; |
339 | |
} |
340 | 14 | allocBuffer = new AllocatedBuffer(chunkSize); |
341 | 14 | dynamicChunkMap = new HashMap<StreamCharBufferKey, StreamCharBufferSubChunk>(); |
342 | 14 | } |
343 | |
|
344 | |
|
345 | |
|
346 | |
|
347 | |
|
348 | |
public final void clear() |
349 | |
{ |
350 | 0 | reset(); |
351 | 0 | notifyBufferChange(); |
352 | 0 | } |
353 | |
|
354 | |
|
355 | |
|
356 | |
|
357 | |
|
358 | |
|
359 | |
|
360 | |
|
361 | |
public final void connectTo(Writer w) |
362 | |
{ |
363 | 0 | connectTo(w, true); |
364 | 0 | } |
365 | |
|
366 | |
public final void connectTo(Writer w, boolean autoFlush) |
367 | |
{ |
368 | 0 | initConnected(); |
369 | 0 | connectedWriters.add(new ConnectedWriter(w, autoFlush)); |
370 | 0 | initConnectedWritersWriter(); |
371 | 0 | } |
372 | |
|
373 | |
private void initConnectedWritersWriter() |
374 | |
{ |
375 | 0 | if (connectedWriters.size() > 1) |
376 | |
{ |
377 | 0 | connectedWritersWriter = new MultiOutputWriter(connectedWriters); |
378 | |
} |
379 | |
else |
380 | |
{ |
381 | 0 | connectedWritersWriter = new SingleOutputWriter( |
382 | |
connectedWriters.get(0)); |
383 | |
} |
384 | 0 | } |
385 | |
|
386 | |
public final void connectTo(LazyInitializingWriter w) |
387 | |
{ |
388 | 0 | connectTo(w, true); |
389 | 0 | } |
390 | |
|
391 | |
public final void connectTo(LazyInitializingWriter w, boolean autoFlush) |
392 | |
{ |
393 | 0 | initConnected(); |
394 | 0 | connectedWriters.add(new ConnectedWriter(w, autoFlush)); |
395 | 0 | initConnectedWritersWriter(); |
396 | 0 | } |
397 | |
|
398 | |
public final void removeConnections() |
399 | |
{ |
400 | 0 | if (connectedWriters != null) |
401 | |
{ |
402 | 0 | connectedWriters.clear(); |
403 | 0 | connectedWritersWriter = null; |
404 | |
} |
405 | 0 | } |
406 | |
|
407 | |
private void initConnected() |
408 | |
{ |
409 | 0 | if (connectedWriters == null) |
410 | |
{ |
411 | 0 | connectedWriters = new ArrayList<ConnectedWriter>(2); |
412 | |
} |
413 | 0 | } |
414 | |
|
415 | |
public int getSubStringChunkMinSize() |
416 | |
{ |
417 | 0 | return subStringChunkMinSize; |
418 | |
} |
419 | |
|
420 | |
|
421 | |
|
422 | |
|
423 | |
|
424 | |
|
425 | |
|
426 | |
public void setSubStringChunkMinSize(int stringChunkMinSize) |
427 | |
{ |
428 | 0 | this.subStringChunkMinSize = stringChunkMinSize; |
429 | 0 | } |
430 | |
|
431 | |
public int getSubBufferChunkMinSize() |
432 | |
{ |
433 | 0 | return subBufferChunkMinSize; |
434 | |
} |
435 | |
|
436 | |
public void setSubBufferChunkMinSize(int subBufferChunkMinSize) |
437 | |
{ |
438 | 0 | this.subBufferChunkMinSize = subBufferChunkMinSize; |
439 | 0 | } |
440 | |
|
441 | |
public int getWriteDirectlyToConnectedMinSize() |
442 | |
{ |
443 | 0 | return writeDirectlyToConnectedMinSize; |
444 | |
} |
445 | |
|
446 | |
|
447 | |
|
448 | |
|
449 | |
|
450 | |
|
451 | |
public void setWriteDirectlyToConnectedMinSize( |
452 | |
int writeDirectlyToConnectedMinSize) |
453 | |
{ |
454 | 0 | this.writeDirectlyToConnectedMinSize = writeDirectlyToConnectedMinSize; |
455 | 0 | } |
456 | |
|
457 | |
public int getChunkMinSize() |
458 | |
{ |
459 | 0 | return chunkMinSize; |
460 | |
} |
461 | |
|
462 | |
public void setChunkMinSize(int chunkMinSize) |
463 | |
{ |
464 | 0 | this.chunkMinSize = chunkMinSize; |
465 | 0 | } |
466 | |
|
467 | |
|
468 | |
|
469 | |
|
470 | |
|
471 | |
|
472 | |
public Writer getWriter() |
473 | |
{ |
474 | 14 | return writer; |
475 | |
} |
476 | |
|
477 | |
|
478 | |
|
479 | |
|
480 | |
|
481 | |
|
482 | |
|
483 | |
|
484 | |
public Reader getReader() |
485 | |
{ |
486 | 0 | return getReader(false); |
487 | |
} |
488 | |
|
489 | |
|
490 | |
|
491 | |
|
492 | |
|
493 | |
|
494 | |
|
495 | |
public Reader getReader(boolean removeAfterReading) |
496 | |
{ |
497 | 0 | readerCount++; |
498 | 0 | hasReaders = true; |
499 | 0 | return new StreamCharBufferReader(removeAfterReading); |
500 | |
} |
501 | |
|
502 | |
|
503 | |
|
504 | |
|
505 | |
|
506 | |
|
507 | |
|
508 | |
public Writer writeTo(Writer target) throws IOException |
509 | |
{ |
510 | 7 | writeTo(target, false, false); |
511 | 7 | return getWriter(); |
512 | |
} |
513 | |
|
514 | |
|
515 | |
|
516 | |
|
517 | |
|
518 | |
|
519 | |
|
520 | |
|
521 | |
|
522 | |
public void writeTo(Writer target, boolean flushTarget, boolean emptyAfter) |
523 | |
throws IOException |
524 | |
{ |
525 | |
|
526 | |
|
527 | |
|
528 | 7 | if (target instanceof StreamCharBufferWriter) |
529 | |
{ |
530 | 0 | if (target == writer) |
531 | |
{ |
532 | 0 | throw new IllegalArgumentException( |
533 | |
"Cannot write buffer to itself."); |
534 | |
} |
535 | 0 | ((StreamCharBufferWriter) target).write(this); |
536 | 0 | return; |
537 | |
} |
538 | 7 | writeToImpl(target, flushTarget, emptyAfter); |
539 | 7 | } |
540 | |
|
541 | |
private void writeToImpl(Writer target, boolean flushTarget, |
542 | |
boolean emptyAfter) throws IOException |
543 | |
{ |
544 | 7 | AbstractChunk current = firstChunk; |
545 | 7 | while (current != null) |
546 | |
{ |
547 | 0 | current.writeTo(target); |
548 | 0 | current = current.next; |
549 | |
} |
550 | 7 | if (emptyAfter) |
551 | |
{ |
552 | 0 | firstChunk = null; |
553 | 0 | lastChunk = null; |
554 | 0 | totalCharsInList = 0; |
555 | 0 | totalCharsInDynamicChunks = -1; |
556 | 0 | sizeAtLeast = -1; |
557 | 0 | dynamicChunkMap.clear(); |
558 | |
} |
559 | 7 | allocBuffer.writeTo(target); |
560 | 7 | if (emptyAfter) |
561 | |
{ |
562 | 0 | allocBuffer.reuseBuffer(); |
563 | |
} |
564 | 7 | if (flushTarget) |
565 | |
{ |
566 | 0 | target.flush(); |
567 | |
} |
568 | 7 | } |
569 | |
|
570 | |
|
571 | |
|
572 | |
|
573 | |
|
574 | |
|
575 | |
public char[] readAsCharArray() |
576 | |
{ |
577 | 7 | int currentSize = size(); |
578 | 7 | if (currentSize == 0) |
579 | |
{ |
580 | 0 | return new char[0]; |
581 | |
} |
582 | |
|
583 | 7 | FixedCharArrayWriter target = new FixedCharArrayWriter(currentSize); |
584 | |
try |
585 | |
{ |
586 | 7 | writeTo(target); |
587 | |
} |
588 | 0 | catch (IOException e) |
589 | |
{ |
590 | 0 | throw new RuntimeException("Unexpected IOException", e); |
591 | 7 | } |
592 | 7 | return target.getCharArray(); |
593 | |
} |
594 | |
|
595 | |
|
596 | |
|
597 | |
|
598 | |
|
599 | |
|
600 | |
public String readAsString() |
601 | |
{ |
602 | 7 | char[] buf = readAsCharArray(); |
603 | 7 | if (buf.length > 0) |
604 | |
{ |
605 | 7 | return StringCharArrayAccessor.createString(buf); |
606 | |
} |
607 | |
|
608 | 0 | return ""; |
609 | |
} |
610 | |
|
611 | |
|
612 | |
|
613 | |
|
614 | |
|
615 | |
|
616 | |
|
617 | |
|
618 | |
|
619 | |
|
620 | |
@Override |
621 | |
public String toString() |
622 | |
{ |
623 | 10 | if (firstChunk == lastChunk && firstChunk instanceof StringChunk |
624 | |
&& allocBuffer.charsUsed() == 0 |
625 | |
&& ((StringChunk) firstChunk).isSingleBuffer()) |
626 | |
{ |
627 | 3 | return ((StringChunk) firstChunk).str; |
628 | |
} |
629 | |
|
630 | 7 | int initialReaderCount = readerCount; |
631 | 7 | String str = readAsString(); |
632 | 7 | if (initialReaderCount == 0) |
633 | |
{ |
634 | |
|
635 | 7 | reset(); |
636 | 7 | if (str.length() > 0) |
637 | |
{ |
638 | 7 | addChunk(new StringChunk(str, 0, str.length())); |
639 | |
} |
640 | |
} |
641 | 7 | return str; |
642 | |
} |
643 | |
|
644 | |
|
645 | |
|
646 | |
|
647 | |
|
648 | |
|
649 | |
|
650 | |
|
651 | |
@Override |
652 | |
public int hashCode() |
653 | |
{ |
654 | 0 | return toString().hashCode(); |
655 | |
} |
656 | |
|
657 | |
|
658 | |
|
659 | |
|
660 | |
|
661 | |
|
662 | |
|
663 | |
@Override |
664 | |
public boolean equals(Object o) |
665 | |
{ |
666 | 0 | if (o == this) |
667 | |
{ |
668 | 0 | return true; |
669 | |
} |
670 | |
|
671 | 0 | if (!(o instanceof CharSequence)) |
672 | |
{ |
673 | 0 | return false; |
674 | |
} |
675 | |
|
676 | 0 | CharSequence other = (CharSequence) o; |
677 | |
|
678 | 0 | return toString().equals(other.toString()); |
679 | |
} |
680 | |
|
681 | |
public String plus(String value) |
682 | |
{ |
683 | 0 | return toString() + value; |
684 | |
} |
685 | |
|
686 | |
public String plus(Object value) |
687 | |
{ |
688 | 0 | return toString() + String.valueOf(value); |
689 | |
} |
690 | |
|
691 | |
|
692 | |
|
693 | |
|
694 | |
|
695 | |
|
696 | |
|
697 | |
|
698 | |
public char[] toCharArray() |
699 | |
{ |
700 | |
|
701 | 0 | if (firstChunk == lastChunk && firstChunk instanceof CharBufferChunk |
702 | |
&& allocBuffer.charsUsed() == 0 |
703 | |
&& ((CharBufferChunk) firstChunk).isSingleBuffer()) |
704 | |
{ |
705 | 0 | return ((CharBufferChunk) firstChunk).buffer; |
706 | |
} |
707 | |
|
708 | 0 | int initialReaderCount = readerCount; |
709 | 0 | char[] buf = readAsCharArray(); |
710 | 0 | if (initialReaderCount == 0) |
711 | |
{ |
712 | |
|
713 | 0 | reset(); |
714 | 0 | if (buf.length > 0) |
715 | |
{ |
716 | 0 | addChunk(new CharBufferChunk(-1, buf, 0, buf.length)); |
717 | |
} |
718 | |
} |
719 | 0 | return buf; |
720 | |
} |
721 | |
|
722 | |
public int size() |
723 | |
{ |
724 | 7 | int total = totalCharsInList; |
725 | 7 | if (totalCharsInDynamicChunks == -1) |
726 | |
{ |
727 | 7 | totalCharsInDynamicChunks = 0; |
728 | 7 | for (StreamCharBufferSubChunk chunk : dynamicChunkMap.values()) |
729 | |
{ |
730 | 0 | totalCharsInDynamicChunks += chunk.size(); |
731 | 0 | } |
732 | |
} |
733 | 7 | total += totalCharsInDynamicChunks; |
734 | 7 | total += allocBuffer.charsUsed(); |
735 | 7 | sizeAtLeast = total; |
736 | 7 | return total; |
737 | |
} |
738 | |
|
739 | |
public boolean isEmpty() |
740 | |
{ |
741 | 0 | return !isNotEmpty(); |
742 | |
} |
743 | |
|
744 | |
boolean isNotEmpty() |
745 | |
{ |
746 | 0 | if (totalCharsInList > 0) |
747 | |
{ |
748 | 0 | return true; |
749 | |
} |
750 | 0 | if (totalCharsInDynamicChunks > 0) |
751 | |
{ |
752 | 0 | return true; |
753 | |
} |
754 | 0 | if (allocBuffer.charsUsed() > 0) |
755 | |
{ |
756 | 0 | return true; |
757 | |
} |
758 | 0 | if (totalCharsInDynamicChunks == -1) |
759 | |
{ |
760 | 0 | for (StreamCharBufferSubChunk chunk : dynamicChunkMap.values()) |
761 | |
{ |
762 | 0 | if (chunk.getSubBuffer().isNotEmpty()) |
763 | |
{ |
764 | 0 | return true; |
765 | |
} |
766 | 0 | } |
767 | |
} |
768 | 0 | return false; |
769 | |
} |
770 | |
|
771 | |
boolean isSizeLarger(int minSize) |
772 | |
{ |
773 | 0 | if (minSize <= sizeAtLeast) |
774 | |
{ |
775 | 0 | return true; |
776 | |
} |
777 | |
|
778 | 0 | boolean retval = calculateIsSizeLarger(minSize); |
779 | 0 | if (retval && minSize > sizeAtLeast) |
780 | |
{ |
781 | 0 | sizeAtLeast = minSize; |
782 | |
} |
783 | 0 | return retval; |
784 | |
} |
785 | |
|
786 | |
private boolean calculateIsSizeLarger(int minSize) |
787 | |
{ |
788 | 0 | int total = totalCharsInList; |
789 | 0 | total += allocBuffer.charsUsed(); |
790 | 0 | if (total > minSize) |
791 | |
{ |
792 | 0 | return true; |
793 | |
} |
794 | 0 | if (totalCharsInDynamicChunks != -1) |
795 | |
{ |
796 | 0 | total += totalCharsInDynamicChunks; |
797 | 0 | if (total > minSize) |
798 | |
{ |
799 | 0 | return true; |
800 | |
} |
801 | |
} |
802 | |
else |
803 | |
{ |
804 | 0 | for (StreamCharBufferSubChunk chunk : dynamicChunkMap.values()) |
805 | |
{ |
806 | 0 | if (!chunk.hasCachedSize() |
807 | |
&& chunk.getSubBuffer().isSizeLarger(minSize - total)) |
808 | |
{ |
809 | 0 | return true; |
810 | |
} |
811 | 0 | total += chunk.size(); |
812 | 0 | if (total > minSize) |
813 | |
{ |
814 | 0 | return true; |
815 | |
} |
816 | 0 | } |
817 | |
} |
818 | 0 | return false; |
819 | |
} |
820 | |
|
821 | |
int allocateSpace() throws IOException |
822 | |
{ |
823 | 16 | int spaceLeft = allocBuffer.spaceLeft(); |
824 | 16 | if (spaceLeft == 0) |
825 | |
{ |
826 | 0 | spaceLeft = appendCharBufferChunk(true); |
827 | |
} |
828 | 16 | return spaceLeft; |
829 | |
} |
830 | |
|
831 | |
private int appendCharBufferChunk(boolean flushInConnected) |
832 | |
throws IOException |
833 | |
{ |
834 | 0 | int spaceLeft = 0; |
835 | 0 | if (flushInConnected && isConnectedMode()) |
836 | |
{ |
837 | 0 | flushToConnected(); |
838 | 0 | if (!isChunkSizeResizeable()) |
839 | |
{ |
840 | 0 | allocBuffer.reuseBuffer(); |
841 | 0 | spaceLeft = allocBuffer.spaceLeft(); |
842 | |
} |
843 | |
else |
844 | |
{ |
845 | 0 | spaceLeft = 0; |
846 | |
} |
847 | |
} |
848 | |
else |
849 | |
{ |
850 | 0 | if (allocBuffer.hasChunk()) |
851 | |
{ |
852 | 0 | addChunk(allocBuffer.createChunk()); |
853 | |
} |
854 | 0 | spaceLeft = allocBuffer.spaceLeft(); |
855 | |
} |
856 | 0 | if (spaceLeft == 0) |
857 | |
{ |
858 | 0 | totalChunkSize += allocBuffer.chunkSize(); |
859 | 0 | resizeChunkSizeAsProcentageOfTotalSize(); |
860 | 0 | allocBuffer = new AllocatedBuffer(chunkSize); |
861 | 0 | spaceLeft = allocBuffer.spaceLeft(); |
862 | |
} |
863 | 0 | return spaceLeft; |
864 | |
} |
865 | |
|
866 | |
void appendStringChunk(String str, int off, int len) throws IOException |
867 | |
{ |
868 | 0 | appendCharBufferChunk(false); |
869 | 0 | addChunk(new StringChunk(str, off, len)); |
870 | 0 | } |
871 | |
|
872 | |
public void appendStreamCharBufferChunk(StreamCharBuffer subBuffer) |
873 | |
throws IOException |
874 | |
{ |
875 | 0 | appendCharBufferChunk(false); |
876 | 0 | addChunk(new StreamCharBufferSubChunk(subBuffer)); |
877 | 0 | } |
878 | |
|
879 | |
void addChunk(AbstractChunk newChunk) |
880 | |
{ |
881 | 7 | if (lastChunk != null) |
882 | |
{ |
883 | 0 | lastChunk.next = newChunk; |
884 | 0 | if (hasReaders) |
885 | |
{ |
886 | |
|
887 | |
|
888 | 0 | newChunk.prev = lastChunk; |
889 | |
} |
890 | |
} |
891 | 7 | lastChunk = newChunk; |
892 | 7 | if (firstChunk == null) |
893 | |
{ |
894 | 7 | firstChunk = newChunk; |
895 | |
} |
896 | 7 | if (newChunk instanceof StreamCharBufferSubChunk) |
897 | |
{ |
898 | 0 | StreamCharBufferSubChunk bufSubChunk = (StreamCharBufferSubChunk) newChunk; |
899 | 0 | dynamicChunkMap.put(bufSubChunk.streamCharBuffer.bufferKey, |
900 | |
bufSubChunk); |
901 | 0 | } |
902 | |
else |
903 | |
{ |
904 | 7 | totalCharsInList += newChunk.size(); |
905 | |
} |
906 | 7 | } |
907 | |
|
908 | |
public boolean isConnectedMode() |
909 | |
{ |
910 | 10 | return connectedWriters != null && !connectedWriters.isEmpty(); |
911 | |
} |
912 | |
|
913 | |
private void flushToConnected() throws IOException |
914 | |
{ |
915 | 0 | writeTo(connectedWritersWriter, true, true); |
916 | 0 | } |
917 | |
|
918 | |
protected boolean isChunkSizeResizeable() |
919 | |
{ |
920 | 0 | return (growProcent > 0); |
921 | |
} |
922 | |
|
923 | |
protected void resizeChunkSizeAsProcentageOfTotalSize() |
924 | |
{ |
925 | 0 | if (growProcent == 0) |
926 | |
{ |
927 | 0 | return; |
928 | |
} |
929 | |
|
930 | 0 | if (growProcent == 100) |
931 | |
{ |
932 | 0 | chunkSize = Math.min(totalChunkSize, maxChunkSize); |
933 | |
} |
934 | 0 | else if (growProcent == 200) |
935 | |
{ |
936 | 0 | chunkSize = Math.min(totalChunkSize << 1, maxChunkSize); |
937 | |
} |
938 | 0 | else if (growProcent > 0) |
939 | |
{ |
940 | 0 | chunkSize = Math.max(Math.min((totalChunkSize * growProcent) / 100, |
941 | |
maxChunkSize), firstChunkSize); |
942 | |
} |
943 | 0 | } |
944 | |
|
945 | |
protected static final void arrayCopy(char[] src, int srcPos, char[] dest, |
946 | |
int destPos, int length) |
947 | |
{ |
948 | 7 | if (length == 1) |
949 | |
{ |
950 | 0 | dest[destPos] = src[srcPos]; |
951 | |
} |
952 | |
else |
953 | |
{ |
954 | 7 | System.arraycopy(src, srcPos, dest, destPos, length); |
955 | |
} |
956 | 7 | } |
957 | |
|
958 | |
|
959 | |
|
960 | |
|
961 | |
|
962 | |
|
963 | 7 | public final class StreamCharBufferWriter extends Writer |
964 | |
{ |
965 | 7 | boolean closed = false; |
966 | 7 | int writerUsedCounter = 0; |
967 | 7 | boolean increaseCounter = true; |
968 | |
|
969 | |
@Override |
970 | |
public final void write(final char[] b, final int off, final int len) |
971 | |
throws IOException |
972 | |
{ |
973 | 0 | if (b == null) |
974 | |
{ |
975 | 0 | throw new NullPointerException(); |
976 | |
} |
977 | |
|
978 | 0 | if ((off < 0) || (off > b.length) || (len < 0) |
979 | |
|| ((off + len) > b.length) || ((off + len) < 0)) |
980 | |
{ |
981 | 0 | throw new IndexOutOfBoundsException(); |
982 | |
} |
983 | |
|
984 | 0 | if (len == 0) |
985 | |
{ |
986 | 0 | return; |
987 | |
} |
988 | |
|
989 | 0 | markUsed(); |
990 | 0 | if (shouldWriteDirectly(len)) |
991 | |
{ |
992 | 0 | appendCharBufferChunk(true); |
993 | 0 | connectedWritersWriter.write(b, off, len); |
994 | |
} |
995 | |
else |
996 | |
{ |
997 | 0 | int charsLeft = len; |
998 | 0 | int currentOffset = off; |
999 | 0 | while (charsLeft > 0) |
1000 | |
{ |
1001 | 0 | int spaceLeft = allocateSpace(); |
1002 | 0 | int writeChars = Math.min(spaceLeft, charsLeft); |
1003 | 0 | allocBuffer.write(b, currentOffset, writeChars); |
1004 | 0 | charsLeft -= writeChars; |
1005 | 0 | currentOffset += writeChars; |
1006 | 0 | } |
1007 | |
} |
1008 | 0 | } |
1009 | |
|
1010 | |
private final boolean shouldWriteDirectly(final int len) |
1011 | |
{ |
1012 | 10 | if (!isConnectedMode()) |
1013 | |
{ |
1014 | 10 | return false; |
1015 | |
} |
1016 | |
|
1017 | 0 | if (!(writeDirectlyToConnectedMinSize >= 0 && len >= writeDirectlyToConnectedMinSize)) |
1018 | |
{ |
1019 | 0 | return false; |
1020 | |
} |
1021 | |
|
1022 | 0 | return isNextChunkBigEnough(len); |
1023 | |
} |
1024 | |
|
1025 | |
private final boolean isNextChunkBigEnough(final int len) |
1026 | |
{ |
1027 | 0 | return (len > getNewChunkMinSize()); |
1028 | |
} |
1029 | |
|
1030 | |
private final int getDirectChunkMinSize() |
1031 | |
{ |
1032 | 0 | if (!isConnectedMode()) |
1033 | |
{ |
1034 | 0 | return -1; |
1035 | |
} |
1036 | 0 | if (writeDirectlyToConnectedMinSize >= 0) |
1037 | |
{ |
1038 | 0 | return writeDirectlyToConnectedMinSize; |
1039 | |
} |
1040 | |
|
1041 | 0 | return getNewChunkMinSize(); |
1042 | |
} |
1043 | |
|
1044 | |
private final int getNewChunkMinSize() |
1045 | |
{ |
1046 | 0 | if (chunkMinSize <= 0 || allocBuffer.charsUsed() == 0 |
1047 | |
|| allocBuffer.charsUsed() >= chunkMinSize) |
1048 | |
{ |
1049 | 0 | return 0; |
1050 | |
} |
1051 | 0 | return allocBuffer.spaceLeft(); |
1052 | |
} |
1053 | |
|
1054 | |
@Override |
1055 | |
public final void write(final String str) throws IOException |
1056 | |
{ |
1057 | 10 | write(str, 0, str.length()); |
1058 | 10 | } |
1059 | |
|
1060 | |
@Override |
1061 | |
public final void write(final String str, final int off, final int len) |
1062 | |
throws IOException |
1063 | |
{ |
1064 | 10 | if (len == 0) |
1065 | |
{ |
1066 | 0 | return; |
1067 | |
} |
1068 | 10 | markUsed(); |
1069 | 10 | if (shouldWriteDirectly(len)) |
1070 | |
{ |
1071 | 0 | appendCharBufferChunk(true); |
1072 | 0 | connectedWritersWriter.write(str, off, len); |
1073 | |
} |
1074 | 10 | else if (len >= subStringChunkMinSize && isNextChunkBigEnough(len)) |
1075 | |
{ |
1076 | 0 | appendStringChunk(str, off, len); |
1077 | |
} |
1078 | |
else |
1079 | |
{ |
1080 | 10 | int charsLeft = len; |
1081 | 10 | int currentOffset = off; |
1082 | 20 | while (charsLeft > 0) |
1083 | |
{ |
1084 | 10 | int spaceLeft = allocateSpace(); |
1085 | 10 | int writeChars = Math.min(spaceLeft, charsLeft); |
1086 | 10 | allocBuffer.writeString(str, currentOffset, writeChars); |
1087 | 10 | charsLeft -= writeChars; |
1088 | 10 | currentOffset += writeChars; |
1089 | 10 | } |
1090 | |
} |
1091 | 10 | } |
1092 | |
|
1093 | |
public final void write(StreamCharBuffer subBuffer) throws IOException |
1094 | |
{ |
1095 | 0 | markUsed(); |
1096 | 0 | int directChunkMinSize = getDirectChunkMinSize(); |
1097 | 0 | if (directChunkMinSize != -1 |
1098 | |
&& subBuffer.isSizeLarger(directChunkMinSize)) |
1099 | |
{ |
1100 | 0 | appendCharBufferChunk(true); |
1101 | 0 | subBuffer.writeToImpl(connectedWritersWriter, false, false); |
1102 | |
} |
1103 | 0 | else if (subBuffer.preferSubChunkWhenWritingToOtherBuffer |
1104 | |
|| subBuffer.isSizeLarger(Math.max(subBufferChunkMinSize, |
1105 | |
getNewChunkMinSize()))) |
1106 | |
{ |
1107 | 0 | if (subBuffer.preferSubChunkWhenWritingToOtherBuffer) |
1108 | |
{ |
1109 | 0 | StreamCharBuffer.this.preferSubChunkWhenWritingToOtherBuffer = true; |
1110 | |
} |
1111 | 0 | appendStreamCharBufferChunk(subBuffer); |
1112 | 0 | subBuffer.addParentBuffer(StreamCharBuffer.this); |
1113 | |
} |
1114 | |
else |
1115 | |
{ |
1116 | 0 | subBuffer.writeToImpl(this, false, false); |
1117 | |
} |
1118 | 0 | } |
1119 | |
|
1120 | |
@Override |
1121 | |
public final Writer append(final CharSequence csq, final int start, |
1122 | |
final int end) throws IOException |
1123 | |
{ |
1124 | 0 | markUsed(); |
1125 | 0 | if (csq == null) |
1126 | |
{ |
1127 | 0 | write("null"); |
1128 | |
} |
1129 | |
else |
1130 | |
{ |
1131 | 0 | if (csq instanceof String || csq instanceof StringBuffer |
1132 | |
|| csq instanceof StringBuilder) |
1133 | |
{ |
1134 | 0 | int len = end - start; |
1135 | 0 | int charsLeft = len; |
1136 | 0 | int currentOffset = start; |
1137 | 0 | while (charsLeft > 0) |
1138 | |
{ |
1139 | 0 | int spaceLeft = allocateSpace(); |
1140 | 0 | int writeChars = Math.min(spaceLeft, charsLeft); |
1141 | 0 | if (csq instanceof String) |
1142 | |
{ |
1143 | 0 | allocBuffer.writeString((String) csq, |
1144 | |
currentOffset, writeChars); |
1145 | |
} |
1146 | 0 | else if (csq instanceof StringBuffer) |
1147 | |
{ |
1148 | 0 | allocBuffer.writeStringBuffer((StringBuffer) csq, |
1149 | |
currentOffset, writeChars); |
1150 | |
} |
1151 | 0 | else if (csq instanceof StringBuilder) |
1152 | |
{ |
1153 | 0 | allocBuffer.writeStringBuilder((StringBuilder) csq, |
1154 | |
currentOffset, writeChars); |
1155 | |
} |
1156 | 0 | charsLeft -= writeChars; |
1157 | 0 | currentOffset += writeChars; |
1158 | 0 | } |
1159 | 0 | } |
1160 | |
else |
1161 | |
{ |
1162 | 0 | write(csq.subSequence(start, end).toString()); |
1163 | |
} |
1164 | |
} |
1165 | 0 | return this; |
1166 | |
} |
1167 | |
|
1168 | |
@Override |
1169 | |
public final Writer append(final CharSequence csq) throws IOException |
1170 | |
{ |
1171 | 0 | markUsed(); |
1172 | 0 | if (csq == null) |
1173 | |
{ |
1174 | 0 | write("null"); |
1175 | |
} |
1176 | |
else |
1177 | |
{ |
1178 | 0 | append(csq, 0, csq.length()); |
1179 | |
|
1180 | |
} |
1181 | 0 | return this; |
1182 | |
} |
1183 | |
|
1184 | |
@Override |
1185 | |
public void close() throws IOException |
1186 | |
{ |
1187 | 0 | closed = true; |
1188 | 0 | flush(); |
1189 | 0 | } |
1190 | |
|
1191 | |
public boolean isClosed() |
1192 | |
{ |
1193 | 0 | return closed; |
1194 | |
} |
1195 | |
|
1196 | |
public boolean isUsed() |
1197 | |
{ |
1198 | 0 | return writerUsedCounter > 0; |
1199 | |
} |
1200 | |
|
1201 | |
public final void markUsed() |
1202 | |
{ |
1203 | 16 | if (increaseCounter) |
1204 | |
{ |
1205 | 7 | writerUsedCounter++; |
1206 | 7 | if (!hasReaders) |
1207 | |
{ |
1208 | 7 | increaseCounter = false; |
1209 | |
} |
1210 | |
} |
1211 | 16 | } |
1212 | |
|
1213 | |
public int resetUsed() |
1214 | |
{ |
1215 | 0 | int prevUsed = writerUsedCounter; |
1216 | 0 | writerUsedCounter = 0; |
1217 | 0 | increaseCounter = true; |
1218 | 0 | return prevUsed; |
1219 | |
} |
1220 | |
|
1221 | |
@Override |
1222 | |
public void write(final int b) throws IOException |
1223 | |
{ |
1224 | 6 | markUsed(); |
1225 | 6 | allocateSpace(); |
1226 | 6 | allocBuffer.write((char) b); |
1227 | 6 | } |
1228 | |
|
1229 | |
@Override |
1230 | |
public void flush() throws IOException |
1231 | |
{ |
1232 | 0 | if (isConnectedMode()) |
1233 | |
{ |
1234 | 0 | flushToConnected(); |
1235 | |
} |
1236 | 0 | notifyBufferChange(); |
1237 | 0 | } |
1238 | |
|
1239 | |
public final StreamCharBuffer getBuffer() |
1240 | |
{ |
1241 | 0 | return StreamCharBuffer.this; |
1242 | |
} |
1243 | |
} |
1244 | |
|
1245 | |
|
1246 | |
|
1247 | |
|
1248 | |
|
1249 | |
|
1250 | |
|
1251 | |
final public class StreamCharBufferReader extends Reader |
1252 | |
{ |
1253 | 0 | boolean eofException = false; |
1254 | 0 | int eofReachedCounter = 0; |
1255 | |
ChunkReader chunkReader; |
1256 | |
ChunkReader lastChunkReader; |
1257 | |
boolean removeAfterReading; |
1258 | |
|
1259 | |
public StreamCharBufferReader(boolean removeAfterReading) |
1260 | 0 | { |
1261 | 0 | this.removeAfterReading = removeAfterReading; |
1262 | 0 | } |
1263 | |
|
1264 | |
private int prepareRead(int len) |
1265 | |
{ |
1266 | 0 | if (hasReaders && eofReachedCounter != 0) |
1267 | |
{ |
1268 | 0 | if (eofReachedCounter != writer.writerUsedCounter) |
1269 | |
{ |
1270 | 0 | eofReachedCounter = 0; |
1271 | 0 | eofException = false; |
1272 | 0 | repositionChunkReader(); |
1273 | |
} |
1274 | |
} |
1275 | 0 | if (chunkReader == null && eofReachedCounter == 0) |
1276 | |
{ |
1277 | 0 | if (firstChunk != null) |
1278 | |
{ |
1279 | 0 | chunkReader = firstChunk.getChunkReader(removeAfterReading); |
1280 | 0 | if (removeAfterReading) |
1281 | |
{ |
1282 | 0 | firstChunk.subtractFromTotalCount(); |
1283 | |
} |
1284 | |
} |
1285 | |
else |
1286 | |
{ |
1287 | 0 | chunkReader = new AllocatedBufferReader(allocBuffer, |
1288 | |
removeAfterReading); |
1289 | |
} |
1290 | |
} |
1291 | 0 | int available = 0; |
1292 | 0 | if (chunkReader != null) |
1293 | |
{ |
1294 | 0 | available = chunkReader.getReadLenLimit(len); |
1295 | 0 | while (available == 0 && chunkReader != null) |
1296 | |
{ |
1297 | 0 | chunkReader = chunkReader.next(); |
1298 | 0 | if (chunkReader != null) |
1299 | |
{ |
1300 | 0 | available = chunkReader.getReadLenLimit(len); |
1301 | |
} |
1302 | |
else |
1303 | |
{ |
1304 | 0 | available = 0; |
1305 | |
} |
1306 | |
} |
1307 | |
} |
1308 | 0 | if (chunkReader == null) |
1309 | |
{ |
1310 | 0 | if (hasReaders) |
1311 | |
{ |
1312 | 0 | eofReachedCounter = writer.writerUsedCounter; |
1313 | |
} |
1314 | |
else |
1315 | |
{ |
1316 | 0 | eofReachedCounter = 1; |
1317 | |
} |
1318 | |
} |
1319 | 0 | else if (hasReaders) |
1320 | |
{ |
1321 | 0 | lastChunkReader = chunkReader; |
1322 | |
} |
1323 | 0 | return available; |
1324 | |
} |
1325 | |
|
1326 | |
|
1327 | |
private void repositionChunkReader() |
1328 | |
{ |
1329 | 0 | if (lastChunkReader instanceof AllocatedBufferReader) |
1330 | |
{ |
1331 | 0 | if (lastChunkReader.isValid()) |
1332 | |
{ |
1333 | 0 | chunkReader = lastChunkReader; |
1334 | |
} |
1335 | |
else |
1336 | |
{ |
1337 | 0 | AllocatedBufferReader allocBufferReader = (AllocatedBufferReader) lastChunkReader; |
1338 | |
|
1339 | 0 | int currentPosition = allocBufferReader.position; |
1340 | 0 | AbstractChunk chunk = lastChunk; |
1341 | 0 | while (chunk != null |
1342 | |
&& chunk.writerUsedCounter >= lastChunkReader |
1343 | |
.getWriterUsedCounter()) |
1344 | |
{ |
1345 | 0 | if (chunk instanceof CharBufferChunk) |
1346 | |
{ |
1347 | 0 | CharBufferChunk charBufChunk = (CharBufferChunk) chunk; |
1348 | 0 | if (charBufChunk.allocatedBufferId == allocBufferReader.parent.id) |
1349 | |
{ |
1350 | 0 | if (currentPosition >= charBufChunk.offset |
1351 | |
&& currentPosition <= charBufChunk.lastposition) |
1352 | |
{ |
1353 | 0 | CharBufferChunkReader charBufChunkReader = (CharBufferChunkReader) charBufChunk |
1354 | |
.getChunkReader(removeAfterReading); |
1355 | 0 | int oldpointer = charBufChunkReader.pointer; |
1356 | |
|
1357 | 0 | charBufChunkReader.pointer = currentPosition; |
1358 | 0 | if (removeAfterReading) |
1359 | |
{ |
1360 | 0 | int diff = charBufChunkReader.pointer |
1361 | |
- oldpointer; |
1362 | 0 | totalCharsInList -= diff; |
1363 | 0 | charBufChunk.subtractFromTotalCount(); |
1364 | |
} |
1365 | 0 | chunkReader = charBufChunkReader; |
1366 | 0 | break; |
1367 | |
} |
1368 | |
} |
1369 | |
} |
1370 | 0 | chunk = chunk.prev; |
1371 | |
} |
1372 | |
} |
1373 | |
} |
1374 | 0 | } |
1375 | |
|
1376 | |
@Override |
1377 | |
public boolean ready() throws IOException |
1378 | |
{ |
1379 | 0 | return true; |
1380 | |
} |
1381 | |
|
1382 | |
@Override |
1383 | |
public final int read(final char[] b, final int off, final int len) |
1384 | |
throws IOException |
1385 | |
{ |
1386 | 0 | return readImpl(b, off, len); |
1387 | |
} |
1388 | |
|
1389 | |
final int readImpl(final char[] b, final int off, final int len) |
1390 | |
throws IOException |
1391 | |
{ |
1392 | 0 | if (b == null) |
1393 | |
{ |
1394 | 0 | throw new NullPointerException(); |
1395 | |
} |
1396 | |
|
1397 | 0 | if ((off < 0) || (off > b.length) || (len < 0) |
1398 | |
|| ((off + len) > b.length) || ((off + len) < 0)) |
1399 | |
{ |
1400 | 0 | throw new IndexOutOfBoundsException(); |
1401 | |
} |
1402 | |
|
1403 | 0 | if (len == 0) |
1404 | |
{ |
1405 | 0 | return 0; |
1406 | |
} |
1407 | |
|
1408 | 0 | int charsLeft = len; |
1409 | 0 | int currentOffset = off; |
1410 | 0 | int readChars = prepareRead(charsLeft); |
1411 | 0 | if (eofException) |
1412 | |
{ |
1413 | 0 | throw new EOFException(); |
1414 | |
} |
1415 | |
|
1416 | 0 | int totalCharsRead = 0; |
1417 | 0 | while (charsLeft > 0 && readChars > 0) |
1418 | |
{ |
1419 | 0 | chunkReader.read(b, currentOffset, readChars); |
1420 | 0 | charsLeft -= readChars; |
1421 | 0 | currentOffset += readChars; |
1422 | 0 | totalCharsRead += readChars; |
1423 | 0 | if (charsLeft > 0) |
1424 | |
{ |
1425 | 0 | readChars = prepareRead(charsLeft); |
1426 | |
} |
1427 | |
} |
1428 | |
|
1429 | 0 | if (totalCharsRead > 0) |
1430 | |
{ |
1431 | 0 | return totalCharsRead; |
1432 | |
} |
1433 | |
|
1434 | 0 | eofException = true; |
1435 | 0 | return -1; |
1436 | |
} |
1437 | |
|
1438 | |
@Override |
1439 | |
public void close() throws IOException |
1440 | |
{ |
1441 | |
|
1442 | 0 | } |
1443 | |
|
1444 | |
public final StreamCharBuffer getBuffer() |
1445 | |
{ |
1446 | 0 | return StreamCharBuffer.this; |
1447 | |
} |
1448 | |
|
1449 | |
public int getReadLenLimit(int askedAmount) |
1450 | |
{ |
1451 | 0 | return prepareRead(askedAmount); |
1452 | |
} |
1453 | |
} |
1454 | |
|
1455 | |
abstract class AbstractChunk |
1456 | |
{ |
1457 | |
AbstractChunk next; |
1458 | |
AbstractChunk prev; |
1459 | |
int writerUsedCounter; |
1460 | |
|
1461 | |
public AbstractChunk() |
1462 | 7 | { |
1463 | 7 | if (hasReaders) |
1464 | |
{ |
1465 | 0 | writerUsedCounter = writer.writerUsedCounter; |
1466 | |
} |
1467 | |
else |
1468 | |
{ |
1469 | 7 | writerUsedCounter = 1; |
1470 | |
} |
1471 | 7 | } |
1472 | |
|
1473 | |
public abstract void writeTo(Writer target) throws IOException; |
1474 | |
|
1475 | |
public abstract ChunkReader getChunkReader(boolean removeAfterReading); |
1476 | |
|
1477 | |
public abstract int size(); |
1478 | |
|
1479 | |
public int getWriterUsedCounter() |
1480 | |
{ |
1481 | 0 | return writerUsedCounter; |
1482 | |
} |
1483 | |
|
1484 | |
public void subtractFromTotalCount() |
1485 | |
{ |
1486 | 0 | totalCharsInList -= size(); |
1487 | 0 | } |
1488 | |
} |
1489 | |
|
1490 | |
|
1491 | 0 | static abstract class ChunkReader |
1492 | |
{ |
1493 | |
public abstract int read(char[] ch, int off, int len) |
1494 | |
throws IOException; |
1495 | |
|
1496 | |
public abstract int getReadLenLimit(int askedAmount); |
1497 | |
|
1498 | |
public abstract ChunkReader next(); |
1499 | |
|
1500 | |
public abstract int getWriterUsedCounter(); |
1501 | |
|
1502 | |
public abstract boolean isValid(); |
1503 | |
} |
1504 | |
|
1505 | 0 | final class AllocatedBuffer |
1506 | |
{ |
1507 | 14 | private int id = allocatedBufferIdSequence++; |
1508 | |
private int size; |
1509 | |
private char[] buffer; |
1510 | 14 | private int used = 0; |
1511 | 14 | private int chunkStart = 0; |
1512 | |
|
1513 | |
public AllocatedBuffer(int size) |
1514 | 14 | { |
1515 | 14 | this.size = size; |
1516 | 14 | buffer = new char[size]; |
1517 | 14 | } |
1518 | |
|
1519 | |
public int charsUsed() |
1520 | |
{ |
1521 | 10 | return used - chunkStart; |
1522 | |
} |
1523 | |
|
1524 | |
public void writeTo(Writer target) throws IOException |
1525 | |
{ |
1526 | 7 | if (used - chunkStart > 0) |
1527 | |
{ |
1528 | 7 | target.write(buffer, chunkStart, used - chunkStart); |
1529 | |
} |
1530 | 7 | } |
1531 | |
|
1532 | |
public void reuseBuffer() |
1533 | |
{ |
1534 | 0 | used = 0; |
1535 | 0 | chunkStart = 0; |
1536 | 0 | } |
1537 | |
|
1538 | |
public int chunkSize() |
1539 | |
{ |
1540 | 0 | return buffer.length; |
1541 | |
} |
1542 | |
|
1543 | |
public int spaceLeft() |
1544 | |
{ |
1545 | 16 | return size - used; |
1546 | |
} |
1547 | |
|
1548 | |
public boolean write(final char ch) |
1549 | |
{ |
1550 | 6 | if (used < size) |
1551 | |
{ |
1552 | 6 | buffer[used++] = ch; |
1553 | 6 | return true; |
1554 | |
} |
1555 | |
|
1556 | 0 | return false; |
1557 | |
} |
1558 | |
|
1559 | |
public final void write(final char[] ch, final int off, final int len) |
1560 | |
{ |
1561 | 0 | arrayCopy(ch, off, buffer, used, len); |
1562 | 0 | used += len; |
1563 | 0 | } |
1564 | |
|
1565 | |
public final void writeString(final String str, final int off, |
1566 | |
final int len) |
1567 | |
{ |
1568 | 10 | str.getChars(off, off + len, buffer, used); |
1569 | 10 | used += len; |
1570 | 10 | } |
1571 | |
|
1572 | |
public final void writeStringBuilder(final StringBuilder stringBuilder, |
1573 | |
final int off, final int len) |
1574 | |
{ |
1575 | 0 | stringBuilder.getChars(off, off + len, buffer, used); |
1576 | 0 | used += len; |
1577 | 0 | } |
1578 | |
|
1579 | |
public final void writeStringBuffer(final StringBuffer stringBuffer, |
1580 | |
final int off, final int len) |
1581 | |
{ |
1582 | 0 | stringBuffer.getChars(off, off + len, buffer, used); |
1583 | 0 | used += len; |
1584 | 0 | } |
1585 | |
|
1586 | |
|
1587 | |
|
1588 | |
|
1589 | |
|
1590 | |
|
1591 | |
|
1592 | |
public CharBufferChunk createChunk() |
1593 | |
{ |
1594 | 0 | CharBufferChunk chunk = new CharBufferChunk(id, buffer, chunkStart, |
1595 | |
used - chunkStart); |
1596 | 0 | chunkStart = used; |
1597 | 0 | return chunk; |
1598 | |
} |
1599 | |
|
1600 | |
public boolean hasChunk() |
1601 | |
{ |
1602 | 0 | return (used > chunkStart); |
1603 | |
} |
1604 | |
|
1605 | |
} |
1606 | |
|
1607 | |
|
1608 | |
|
1609 | |
|
1610 | |
|
1611 | |
|
1612 | |
|
1613 | |
|
1614 | |
|
1615 | |
|
1616 | |
|
1617 | |
|
1618 | |
|
1619 | |
final class CharBufferChunk extends AbstractChunk |
1620 | |
{ |
1621 | |
int allocatedBufferId; |
1622 | |
char[] buffer; |
1623 | |
int offset; |
1624 | |
int lastposition; |
1625 | |
int length; |
1626 | |
|
1627 | |
public CharBufferChunk(int allocatedBufferId, char[] buffer, |
1628 | |
int offset, int len) |
1629 | 0 | { |
1630 | 0 | super(); |
1631 | 0 | this.allocatedBufferId = allocatedBufferId; |
1632 | 0 | this.buffer = buffer; |
1633 | 0 | this.offset = offset; |
1634 | 0 | this.lastposition = offset + len; |
1635 | 0 | this.length = len; |
1636 | 0 | } |
1637 | |
|
1638 | |
@Override |
1639 | |
public void writeTo(final Writer target) throws IOException |
1640 | |
{ |
1641 | 0 | target.write(buffer, offset, length); |
1642 | 0 | } |
1643 | |
|
1644 | |
@Override |
1645 | |
public ChunkReader getChunkReader(boolean removeAfterReading) |
1646 | |
{ |
1647 | 0 | return new CharBufferChunkReader(this, removeAfterReading); |
1648 | |
} |
1649 | |
|
1650 | |
@Override |
1651 | |
public int size() |
1652 | |
{ |
1653 | 0 | return length; |
1654 | |
} |
1655 | |
|
1656 | |
public boolean isSingleBuffer() |
1657 | |
{ |
1658 | 0 | return offset == 0 && length == buffer.length; |
1659 | |
} |
1660 | |
} |
1661 | |
|
1662 | |
abstract class AbstractChunkReader extends ChunkReader |
1663 | |
{ |
1664 | |
private AbstractChunk parentChunk; |
1665 | |
private boolean removeAfterReading; |
1666 | |
|
1667 | |
public AbstractChunkReader(AbstractChunk parentChunk, |
1668 | |
boolean removeAfterReading) |
1669 | 0 | { |
1670 | 0 | this.parentChunk = parentChunk; |
1671 | 0 | this.removeAfterReading = removeAfterReading; |
1672 | 0 | } |
1673 | |
|
1674 | |
@Override |
1675 | |
public boolean isValid() |
1676 | |
{ |
1677 | 0 | return true; |
1678 | |
} |
1679 | |
|
1680 | |
@Override |
1681 | |
public ChunkReader next() |
1682 | |
{ |
1683 | 0 | if (removeAfterReading) |
1684 | |
{ |
1685 | 0 | if (firstChunk == parentChunk) |
1686 | |
{ |
1687 | 0 | firstChunk = null; |
1688 | |
} |
1689 | 0 | if (lastChunk == parentChunk) |
1690 | |
{ |
1691 | 0 | lastChunk = null; |
1692 | |
} |
1693 | |
} |
1694 | 0 | AbstractChunk nextChunk = parentChunk.next; |
1695 | 0 | if (nextChunk != null) |
1696 | |
{ |
1697 | 0 | if (removeAfterReading) |
1698 | |
{ |
1699 | 0 | if (firstChunk == null) |
1700 | |
{ |
1701 | 0 | firstChunk = nextChunk; |
1702 | |
} |
1703 | 0 | if (lastChunk == null) |
1704 | |
{ |
1705 | 0 | lastChunk = nextChunk; |
1706 | |
} |
1707 | 0 | nextChunk.prev = null; |
1708 | 0 | nextChunk.subtractFromTotalCount(); |
1709 | |
} |
1710 | 0 | return nextChunk.getChunkReader(removeAfterReading); |
1711 | |
} |
1712 | |
|
1713 | 0 | return new AllocatedBufferReader(allocBuffer, removeAfterReading); |
1714 | |
} |
1715 | |
|
1716 | |
@Override |
1717 | |
public int getWriterUsedCounter() |
1718 | |
{ |
1719 | 0 | return parentChunk.getWriterUsedCounter(); |
1720 | |
} |
1721 | |
} |
1722 | |
|
1723 | |
final class CharBufferChunkReader extends AbstractChunkReader |
1724 | |
{ |
1725 | |
CharBufferChunk parent; |
1726 | |
int pointer; |
1727 | |
|
1728 | |
public CharBufferChunkReader(CharBufferChunk parent, |
1729 | |
boolean removeAfterReading) |
1730 | 0 | { |
1731 | 0 | super(parent, removeAfterReading); |
1732 | 0 | this.parent = parent; |
1733 | 0 | this.pointer = parent.offset; |
1734 | 0 | } |
1735 | |
|
1736 | |
@Override |
1737 | |
public int read(final char[] ch, final int off, final int len) |
1738 | |
throws IOException |
1739 | |
{ |
1740 | 0 | arrayCopy(parent.buffer, pointer, ch, off, len); |
1741 | 0 | pointer += len; |
1742 | 0 | return len; |
1743 | |
} |
1744 | |
|
1745 | |
@Override |
1746 | |
public int getReadLenLimit(int askedAmount) |
1747 | |
{ |
1748 | 0 | return Math.min(parent.lastposition - pointer, askedAmount); |
1749 | |
} |
1750 | |
} |
1751 | |
|
1752 | |
|
1753 | |
|
1754 | |
|
1755 | |
|
1756 | |
|
1757 | |
|
1758 | |
|
1759 | |
|
1760 | |
|
1761 | |
|
1762 | |
final class StringChunk extends AbstractChunk |
1763 | |
{ |
1764 | |
String str; |
1765 | |
int offset; |
1766 | |
int lastposition; |
1767 | |
int length; |
1768 | |
|
1769 | |
public StringChunk(String str, int offset, int length) |
1770 | 7 | { |
1771 | 7 | this.str = str; |
1772 | 7 | this.offset = offset; |
1773 | 7 | this.length = length; |
1774 | 7 | this.lastposition = offset + length; |
1775 | 7 | } |
1776 | |
|
1777 | |
@Override |
1778 | |
public ChunkReader getChunkReader(boolean removeAfterReading) |
1779 | |
{ |
1780 | 0 | return new StringChunkReader(this, removeAfterReading); |
1781 | |
} |
1782 | |
|
1783 | |
@Override |
1784 | |
public void writeTo(Writer target) throws IOException |
1785 | |
{ |
1786 | 0 | target.write(str, offset, length); |
1787 | 0 | } |
1788 | |
|
1789 | |
@Override |
1790 | |
public int size() |
1791 | |
{ |
1792 | 7 | return length; |
1793 | |
} |
1794 | |
|
1795 | |
public boolean isSingleBuffer() |
1796 | |
{ |
1797 | 3 | return offset == 0 && length == str.length(); |
1798 | |
} |
1799 | |
} |
1800 | |
|
1801 | |
final class StringChunkReader extends AbstractChunkReader |
1802 | |
{ |
1803 | |
StringChunk parent; |
1804 | |
int position; |
1805 | |
|
1806 | |
public StringChunkReader(StringChunk parent, boolean removeAfterReading) |
1807 | 0 | { |
1808 | 0 | super(parent, removeAfterReading); |
1809 | 0 | this.parent = parent; |
1810 | 0 | this.position = parent.offset; |
1811 | 0 | } |
1812 | |
|
1813 | |
@Override |
1814 | |
public int read(final char[] ch, final int off, final int len) |
1815 | |
{ |
1816 | 0 | parent.str.getChars(position, (position + len), ch, off); |
1817 | 0 | position += len; |
1818 | 0 | return len; |
1819 | |
} |
1820 | |
|
1821 | |
@Override |
1822 | |
public int getReadLenLimit(int askedAmount) |
1823 | |
{ |
1824 | 0 | return Math.min(parent.lastposition - position, askedAmount); |
1825 | |
} |
1826 | |
} |
1827 | |
|
1828 | |
final class StreamCharBufferSubChunk extends AbstractChunk |
1829 | |
{ |
1830 | |
StreamCharBuffer streamCharBuffer; |
1831 | |
int cachedSize; |
1832 | |
|
1833 | |
public StreamCharBufferSubChunk(StreamCharBuffer streamCharBuffer) |
1834 | 0 | { |
1835 | 0 | this.streamCharBuffer = streamCharBuffer; |
1836 | 0 | if (totalCharsInDynamicChunks != -1) |
1837 | |
{ |
1838 | 0 | cachedSize = streamCharBuffer.size(); |
1839 | 0 | totalCharsInDynamicChunks += cachedSize; |
1840 | |
} |
1841 | |
else |
1842 | |
{ |
1843 | 0 | cachedSize = -1; |
1844 | |
} |
1845 | 0 | } |
1846 | |
|
1847 | |
@Override |
1848 | |
public void writeTo(Writer target) throws IOException |
1849 | |
{ |
1850 | 0 | streamCharBuffer.writeTo(target); |
1851 | 0 | } |
1852 | |
|
1853 | |
@Override |
1854 | |
public ChunkReader getChunkReader(boolean removeAfterReading) |
1855 | |
{ |
1856 | 0 | return new StreamCharBufferSubChunkReader(this, removeAfterReading); |
1857 | |
} |
1858 | |
|
1859 | |
@Override |
1860 | |
public int size() |
1861 | |
{ |
1862 | 0 | if (cachedSize == -1) |
1863 | |
{ |
1864 | 0 | cachedSize = streamCharBuffer.size(); |
1865 | |
} |
1866 | 0 | return cachedSize; |
1867 | |
} |
1868 | |
|
1869 | |
public boolean hasCachedSize() |
1870 | |
{ |
1871 | 0 | return (cachedSize != -1); |
1872 | |
} |
1873 | |
|
1874 | |
public StreamCharBuffer getSubBuffer() |
1875 | |
{ |
1876 | 0 | return this.streamCharBuffer; |
1877 | |
} |
1878 | |
|
1879 | |
public boolean resetSize() |
1880 | |
{ |
1881 | 0 | if (cachedSize != -1) |
1882 | |
{ |
1883 | 0 | cachedSize = -1; |
1884 | 0 | return true; |
1885 | |
} |
1886 | 0 | return false; |
1887 | |
} |
1888 | |
|
1889 | |
@Override |
1890 | |
public void subtractFromTotalCount() |
1891 | |
{ |
1892 | 0 | if (totalCharsInDynamicChunks != -1) |
1893 | |
{ |
1894 | 0 | totalCharsInDynamicChunks -= size(); |
1895 | |
} |
1896 | 0 | dynamicChunkMap.remove(streamCharBuffer.bufferKey); |
1897 | 0 | } |
1898 | |
} |
1899 | |
|
1900 | |
final class StreamCharBufferSubChunkReader extends AbstractChunkReader |
1901 | |
{ |
1902 | |
StreamCharBufferSubChunk parent; |
1903 | |
private StreamCharBufferReader reader; |
1904 | |
|
1905 | |
public StreamCharBufferSubChunkReader(StreamCharBufferSubChunk parent, |
1906 | |
boolean removeAfterReading) |
1907 | 0 | { |
1908 | 0 | super(parent, removeAfterReading); |
1909 | 0 | this.parent = parent; |
1910 | 0 | reader = (StreamCharBufferReader) parent.streamCharBuffer |
1911 | |
.getReader(); |
1912 | 0 | } |
1913 | |
|
1914 | |
@Override |
1915 | |
public int getReadLenLimit(int askedAmount) |
1916 | |
{ |
1917 | 0 | return reader.getReadLenLimit(askedAmount); |
1918 | |
} |
1919 | |
|
1920 | |
@Override |
1921 | |
public int read(char[] ch, int off, int len) throws IOException |
1922 | |
{ |
1923 | 0 | return reader.read(ch, off, len); |
1924 | |
} |
1925 | |
} |
1926 | |
|
1927 | |
final class AllocatedBufferReader extends ChunkReader |
1928 | |
{ |
1929 | |
AllocatedBuffer parent; |
1930 | |
int position; |
1931 | |
int writerUsedCounter; |
1932 | |
boolean removeAfterReading; |
1933 | |
|
1934 | |
public AllocatedBufferReader(AllocatedBuffer parent, |
1935 | |
boolean removeAfterReading) |
1936 | 0 | { |
1937 | 0 | this.parent = parent; |
1938 | 0 | this.position = parent.chunkStart; |
1939 | 0 | if (hasReaders) |
1940 | |
{ |
1941 | 0 | writerUsedCounter = writer.writerUsedCounter; |
1942 | |
} |
1943 | |
else |
1944 | |
{ |
1945 | 0 | writerUsedCounter = 1; |
1946 | |
} |
1947 | 0 | this.removeAfterReading = removeAfterReading; |
1948 | 0 | } |
1949 | |
|
1950 | |
@Override |
1951 | |
public int getReadLenLimit(int askedAmount) |
1952 | |
{ |
1953 | 0 | return Math.min(parent.used - position, askedAmount); |
1954 | |
} |
1955 | |
|
1956 | |
@Override |
1957 | |
public int read(char[] ch, int off, int len) throws IOException |
1958 | |
{ |
1959 | 0 | arrayCopy(parent.buffer, position, ch, off, len); |
1960 | 0 | position += len; |
1961 | 0 | if (removeAfterReading) |
1962 | |
{ |
1963 | 0 | parent.chunkStart = position; |
1964 | |
} |
1965 | 0 | return len; |
1966 | |
} |
1967 | |
|
1968 | |
@Override |
1969 | |
public ChunkReader next() |
1970 | |
{ |
1971 | 0 | return null; |
1972 | |
} |
1973 | |
|
1974 | |
@Override |
1975 | |
public int getWriterUsedCounter() |
1976 | |
{ |
1977 | 0 | return writerUsedCounter; |
1978 | |
} |
1979 | |
|
1980 | |
@Override |
1981 | |
public boolean isValid() |
1982 | |
{ |
1983 | 0 | return (allocBuffer == parent && (lastChunk == null || lastChunk.writerUsedCounter < writerUsedCounter)); |
1984 | |
} |
1985 | |
} |
1986 | |
|
1987 | |
|
1988 | |
|
1989 | |
|
1990 | |
|
1991 | |
|
1992 | |
private static final class FixedCharArrayWriter extends Writer |
1993 | |
{ |
1994 | |
char buf[]; |
1995 | 7 | int count = 0; |
1996 | |
|
1997 | |
public FixedCharArrayWriter(int fixedSize) |
1998 | 7 | { |
1999 | 7 | buf = new char[fixedSize]; |
2000 | 7 | } |
2001 | |
|
2002 | |
@Override |
2003 | |
public void write(char[] cbuf, int off, int len) throws IOException |
2004 | |
{ |
2005 | 7 | arrayCopy(cbuf, off, buf, count, len); |
2006 | 7 | count += len; |
2007 | 7 | } |
2008 | |
|
2009 | |
@Override |
2010 | |
public void write(char[] cbuf) throws IOException |
2011 | |
{ |
2012 | 0 | write(cbuf, 0, cbuf.length); |
2013 | 0 | } |
2014 | |
|
2015 | |
@Override |
2016 | |
public void write(String str, int off, int len) throws IOException |
2017 | |
{ |
2018 | 0 | str.getChars(off, off + len, buf, count); |
2019 | 0 | count += len; |
2020 | 0 | } |
2021 | |
|
2022 | |
@Override |
2023 | |
public void write(String str) throws IOException |
2024 | |
{ |
2025 | 0 | write(str, 0, str.length()); |
2026 | 0 | } |
2027 | |
|
2028 | |
@Override |
2029 | |
public void close() throws IOException |
2030 | |
{ |
2031 | |
|
2032 | 0 | } |
2033 | |
|
2034 | |
@Override |
2035 | |
public void flush() throws IOException |
2036 | |
{ |
2037 | |
|
2038 | 0 | } |
2039 | |
|
2040 | |
public char[] getCharArray() |
2041 | |
{ |
2042 | 7 | return buf; |
2043 | |
} |
2044 | |
} |
2045 | |
|
2046 | |
|
2047 | |
|
2048 | |
|
2049 | |
|
2050 | |
|
2051 | |
|
2052 | |
|
2053 | |
public static interface LazyInitializingWriter |
2054 | |
{ |
2055 | |
public Writer getWriter() throws IOException; |
2056 | |
} |
2057 | |
|
2058 | |
|
2059 | |
|
2060 | |
|
2061 | |
|
2062 | |
|
2063 | |
|
2064 | |
static final class ConnectedWriter |
2065 | |
{ |
2066 | |
Writer writer; |
2067 | |
LazyInitializingWriter lazyInitializingWriter; |
2068 | |
final boolean autoFlush; |
2069 | |
|
2070 | |
ConnectedWriter(final Writer writer, final boolean autoFlush) |
2071 | 0 | { |
2072 | 0 | this.writer = writer; |
2073 | 0 | this.autoFlush = autoFlush; |
2074 | 0 | } |
2075 | |
|
2076 | |
ConnectedWriter(final LazyInitializingWriter lazyInitializingWriter, |
2077 | |
final boolean autoFlush) |
2078 | 0 | { |
2079 | 0 | this.lazyInitializingWriter = lazyInitializingWriter; |
2080 | 0 | this.autoFlush = autoFlush; |
2081 | 0 | } |
2082 | |
|
2083 | |
Writer getWriter() throws IOException |
2084 | |
{ |
2085 | 0 | if (writer == null && lazyInitializingWriter != null) |
2086 | |
{ |
2087 | 0 | writer = lazyInitializingWriter.getWriter(); |
2088 | |
} |
2089 | 0 | return writer; |
2090 | |
} |
2091 | |
|
2092 | |
public void flush() throws IOException |
2093 | |
{ |
2094 | 0 | if (writer != null && isAutoFlush()) |
2095 | |
{ |
2096 | 0 | writer.flush(); |
2097 | |
} |
2098 | 0 | } |
2099 | |
|
2100 | |
public boolean isAutoFlush() |
2101 | |
{ |
2102 | 0 | return autoFlush; |
2103 | |
} |
2104 | |
} |
2105 | |
|
2106 | 0 | static final class SingleOutputWriter extends Writer |
2107 | |
{ |
2108 | |
private ConnectedWriter writer; |
2109 | |
|
2110 | |
public SingleOutputWriter(ConnectedWriter writer) |
2111 | 0 | { |
2112 | 0 | this.writer = writer; |
2113 | 0 | } |
2114 | |
|
2115 | |
@Override |
2116 | |
public void close() throws IOException |
2117 | |
{ |
2118 | |
|
2119 | 0 | } |
2120 | |
|
2121 | |
@Override |
2122 | |
public void flush() throws IOException |
2123 | |
{ |
2124 | 0 | writer.flush(); |
2125 | 0 | } |
2126 | |
|
2127 | |
@Override |
2128 | |
public void write(final char[] cbuf, final int off, final int len) |
2129 | |
throws IOException |
2130 | |
{ |
2131 | 0 | writer.getWriter().write(cbuf, off, len); |
2132 | 0 | } |
2133 | |
|
2134 | |
@Override |
2135 | |
public Writer append(final CharSequence csq, final int start, |
2136 | |
final int end) throws IOException |
2137 | |
{ |
2138 | 0 | writer.getWriter().append(csq, start, end); |
2139 | 0 | return this; |
2140 | |
} |
2141 | |
|
2142 | |
@Override |
2143 | |
public void write(String str, int off, int len) throws IOException |
2144 | |
{ |
2145 | 0 | StringCharArrayAccessor.writeStringAsCharArray(writer.getWriter(), |
2146 | |
str, off, len); |
2147 | 0 | } |
2148 | |
} |
2149 | |
|
2150 | |
|
2151 | |
|
2152 | |
|
2153 | |
|
2154 | 0 | static final class MultiOutputWriter extends Writer |
2155 | |
{ |
2156 | |
final List<ConnectedWriter> writers; |
2157 | |
|
2158 | |
public MultiOutputWriter(final List<ConnectedWriter> writers) |
2159 | 0 | { |
2160 | 0 | this.writers = writers; |
2161 | 0 | } |
2162 | |
|
2163 | |
@Override |
2164 | |
public void close() throws IOException |
2165 | |
{ |
2166 | |
|
2167 | 0 | } |
2168 | |
|
2169 | |
@Override |
2170 | |
public void flush() throws IOException |
2171 | |
{ |
2172 | 0 | for (ConnectedWriter writer : writers) |
2173 | |
{ |
2174 | 0 | writer.flush(); |
2175 | 0 | } |
2176 | 0 | } |
2177 | |
|
2178 | |
@Override |
2179 | |
public void write(final char[] cbuf, final int off, final int len) |
2180 | |
throws IOException |
2181 | |
{ |
2182 | 0 | for (ConnectedWriter writer : writers) |
2183 | |
{ |
2184 | 0 | writer.getWriter().write(cbuf, off, len); |
2185 | 0 | } |
2186 | 0 | } |
2187 | |
|
2188 | |
@Override |
2189 | |
public Writer append(final CharSequence csq, final int start, |
2190 | |
final int end) throws IOException |
2191 | |
{ |
2192 | 0 | for (ConnectedWriter writer : writers) |
2193 | |
{ |
2194 | 0 | writer.getWriter().append(csq, start, end); |
2195 | 0 | } |
2196 | 0 | return this; |
2197 | |
} |
2198 | |
|
2199 | |
@Override |
2200 | |
public void write(String str, int off, int len) throws IOException |
2201 | |
{ |
2202 | 0 | for (ConnectedWriter writer : writers) |
2203 | |
{ |
2204 | 0 | StringCharArrayAccessor.writeStringAsCharArray( |
2205 | |
writer.getWriter(), str, off, len); |
2206 | 0 | } |
2207 | 0 | } |
2208 | |
} |
2209 | |
|
2210 | |
|
2211 | |
|
2212 | |
public char charAt(int index) |
2213 | |
{ |
2214 | 0 | return toString().charAt(index); |
2215 | |
} |
2216 | |
|
2217 | |
public int length() |
2218 | |
{ |
2219 | 0 | return size(); |
2220 | |
} |
2221 | |
|
2222 | |
public CharSequence subSequence(int start, int end) |
2223 | |
{ |
2224 | 0 | return toString().subSequence(start, end); |
2225 | |
} |
2226 | |
|
2227 | |
public boolean asBoolean() |
2228 | |
{ |
2229 | 0 | return isNotEmpty(); |
2230 | |
} |
2231 | |
|
2232 | |
|
2233 | |
|
2234 | |
void addParentBuffer(StreamCharBuffer parent) |
2235 | |
{ |
2236 | 0 | if (parentBuffers == null) |
2237 | |
{ |
2238 | 0 | parentBuffers = new HashSet<SoftReference<StreamCharBufferKey>>(); |
2239 | |
} |
2240 | 0 | parentBuffers.add(new SoftReference<StreamCharBufferKey>( |
2241 | |
parent.bufferKey)); |
2242 | 0 | } |
2243 | |
|
2244 | |
boolean bufferChanged(StreamCharBuffer buffer) |
2245 | |
{ |
2246 | 0 | StreamCharBufferSubChunk subChunk = dynamicChunkMap |
2247 | |
.get(buffer.bufferKey); |
2248 | 0 | if (subChunk == null) |
2249 | |
{ |
2250 | |
|
2251 | 0 | return false; |
2252 | |
} |
2253 | |
|
2254 | 0 | if (subChunk.resetSize()) |
2255 | |
{ |
2256 | 0 | totalCharsInDynamicChunks = -1; |
2257 | 0 | sizeAtLeast = -1; |
2258 | |
|
2259 | 0 | notifyBufferChange(); |
2260 | |
} |
2261 | 0 | return true; |
2262 | |
} |
2263 | |
|
2264 | |
void notifyBufferChange() |
2265 | |
{ |
2266 | 0 | if (parentBuffers == null) |
2267 | |
{ |
2268 | 0 | return; |
2269 | |
} |
2270 | |
|
2271 | 0 | for (Iterator<SoftReference<StreamCharBufferKey>> i = parentBuffers |
2272 | 0 | .iterator(); i.hasNext();) |
2273 | |
{ |
2274 | 0 | SoftReference<StreamCharBufferKey> ref = i.next(); |
2275 | 0 | final StreamCharBuffer.StreamCharBufferKey parentKey = ref.get(); |
2276 | 0 | boolean removeIt = true; |
2277 | 0 | if (parentKey != null) |
2278 | |
{ |
2279 | 0 | StreamCharBuffer parent = parentKey.getBuffer(); |
2280 | 0 | removeIt = !parent.bufferChanged(this); |
2281 | |
} |
2282 | 0 | if (removeIt) |
2283 | |
{ |
2284 | 0 | i.remove(); |
2285 | |
} |
2286 | 0 | } |
2287 | 0 | } |
2288 | |
|
2289 | |
public void readExternal(ObjectInput in) throws IOException, |
2290 | |
ClassNotFoundException |
2291 | |
{ |
2292 | 0 | String str = in.readUTF(); |
2293 | 0 | reset(); |
2294 | 0 | if (str.length() > 0) |
2295 | |
{ |
2296 | 0 | addChunk(new StringChunk(str, 0, str.length())); |
2297 | |
} |
2298 | 0 | } |
2299 | |
|
2300 | |
public void writeExternal(ObjectOutput out) throws IOException |
2301 | |
{ |
2302 | 0 | String str = toString(); |
2303 | 0 | out.writeUTF(str); |
2304 | 0 | } |
2305 | |
|
2306 | |
} |