1 /* Copyright (c) 2001-2016, The HSQL Development Group 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of the HSQL Development Group nor the names of its 15 * contributors may be used to endorse or promote products derived from this 16 * software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, 22 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 32 package org.hsqldb.result; 33 34 import java.io.DataInput; 35 import java.io.IOException; 36 import java.io.InputStream; 37 import java.io.Reader; 38 39 import org.hsqldb.SessionInterface; 40 import org.hsqldb.error.Error; 41 import org.hsqldb.error.ErrorCode; 42 import org.hsqldb.lib.DataOutputStream; 43 import org.hsqldb.lib.HsqlByteArrayOutputStream; 44 import org.hsqldb.rowio.RowOutputInterface; 45 46 /** 47 * Sub-class of Result for communicating Blob and Clob operations.<p> 48 * 49 * @author Fred Toussi (fredt@users dot sourceforge.net) 50 * @version 2.0.1 51 * @since 1.9.0 52 */ 53 public final class ResultLob extends Result { 54 55 public interface LobResultTypes { 56 57 int REQUEST_GET_BYTES = 1; 58 int REQUEST_SET_BYTES = 2; 59 int REQUEST_GET_CHARS = 3; 60 int REQUEST_SET_CHARS = 4; 61 int REQUEST_GET_BYTE_PATTERN_POSITION = 5; 62 int REQUEST_GET_CHAR_PATTERN_POSITION = 6; 63 int REQUEST_CREATE_BYTES = 7; 64 int REQUEST_CREATE_CHARS = 8; 65 int REQUEST_TRUNCATE = 9; 66 int REQUEST_GET_LENGTH = 10; 67 int REQUEST_GET_LOB = 11; 68 int REQUEST_DUPLICATE_LOB = 12; 69 70 // non-network 71 int REQUEST_GET_TRUNCATE_LENGTH = 13; 72 73 // 74 int RESPONSE_GET_BYTES = 21; 75 int RESPONSE_SET = 22; 76 int RESPONSE_GET_CHARS = 23; 77 int RESPONSE_GET_BYTE_PATTERN_POSITION = 25; 78 int RESPONSE_GET_CHAR_PATTERN_POSITION = 26; 79 int RESPONSE_CREATE_BYTES = 27; 80 int RESPONSE_CREATE_CHARS = 28; 81 int RESPONSE_TRUNCATE = 29; 82 } 83 84 long lobID; 85 int subType; 86 long blockOffset; 87 long blockLength; 88 byte[] byteBlock; 89 char[] charBlock; 90 Reader reader; 91 InputStream stream; 92 ResultLob()93 private ResultLob() { 94 super(ResultConstants.LARGE_OBJECT_OP); 95 } 96 newLobGetLengthRequest(long id)97 public static ResultLob newLobGetLengthRequest(long id) { 98 99 ResultLob result = new ResultLob(); 100 101 result.subType = LobResultTypes.REQUEST_GET_LENGTH; 102 result.lobID = id; 103 104 return result; 105 } 106 newLobGetBytesRequest(long id, long offset, int length)107 public static ResultLob newLobGetBytesRequest(long id, long offset, 108 int length) { 109 110 ResultLob result = new ResultLob(); 111 112 result.subType = LobResultTypes.REQUEST_GET_BYTES; 113 result.lobID = id; 114 result.blockOffset = offset; 115 result.blockLength = length; 116 117 return result; 118 } 119 newLobGetCharsRequest(long id, long offset, int length)120 public static ResultLob newLobGetCharsRequest(long id, long offset, 121 int length) { 122 123 ResultLob result = new ResultLob(); 124 125 result.subType = LobResultTypes.REQUEST_GET_CHARS; 126 result.lobID = id; 127 result.blockOffset = offset; 128 result.blockLength = length; 129 130 return result; 131 } 132 newLobSetBytesRequest(long id, long offset, byte[] block)133 public static ResultLob newLobSetBytesRequest(long id, long offset, 134 byte[] block) { 135 136 ResultLob result = new ResultLob(); 137 138 result.subType = LobResultTypes.REQUEST_SET_BYTES; 139 result.lobID = id; 140 result.blockOffset = offset; 141 result.byteBlock = block; 142 result.blockLength = block.length; 143 144 return result; 145 } 146 newLobSetCharsRequest(long id, long offset, char[] chars)147 public static ResultLob newLobSetCharsRequest(long id, long offset, 148 char[] chars) { 149 150 ResultLob result = new ResultLob(); 151 152 result.subType = LobResultTypes.REQUEST_SET_CHARS; 153 result.lobID = id; 154 result.blockOffset = offset; 155 result.charBlock = chars; 156 result.blockLength = chars.length; 157 158 return result; 159 } 160 newLobTruncateRequest(long id, long offset)161 public static ResultLob newLobTruncateRequest(long id, long offset) { 162 163 ResultLob result = new ResultLob(); 164 165 result.subType = LobResultTypes.REQUEST_TRUNCATE; 166 result.lobID = id; 167 result.blockOffset = offset; 168 169 return result; 170 } 171 newLobGetBytesResponse(long id, long offset, byte[] block)172 public static ResultLob newLobGetBytesResponse(long id, long offset, 173 byte[] block) { 174 175 ResultLob result = new ResultLob(); 176 177 result.subType = LobResultTypes.RESPONSE_GET_BYTES; 178 result.lobID = id; 179 result.blockOffset = offset; 180 result.byteBlock = block; 181 result.blockLength = block.length; 182 183 return result; 184 } 185 newLobGetCharsResponse(long id, long offset, char[] chars)186 public static ResultLob newLobGetCharsResponse(long id, long offset, 187 char[] chars) { 188 189 ResultLob result = new ResultLob(); 190 191 result.subType = LobResultTypes.RESPONSE_GET_CHARS; 192 result.lobID = id; 193 result.blockOffset = offset; 194 result.charBlock = chars; 195 result.blockLength = chars.length; 196 197 return result; 198 } 199 newLobSetResponse(long id, long length)200 public static ResultLob newLobSetResponse(long id, long length) { 201 202 ResultLob result = new ResultLob(); 203 204 result.subType = LobResultTypes.RESPONSE_SET; 205 result.lobID = id; 206 result.blockLength = length; 207 208 return result; 209 } 210 newLobGetBytePatternPositionRequest(long id, byte[] pattern, long offset)211 public static ResultLob newLobGetBytePatternPositionRequest(long id, 212 byte[] pattern, long offset) { 213 214 ResultLob result = new ResultLob(); 215 216 result.subType = LobResultTypes.REQUEST_GET_BYTE_PATTERN_POSITION; 217 result.lobID = id; 218 result.blockOffset = offset; 219 result.byteBlock = pattern; 220 result.blockLength = pattern.length; 221 222 return result; 223 } 224 newLobGetBytePatternPositionRequest(long id, long otherId, long offset)225 public static ResultLob newLobGetBytePatternPositionRequest(long id, 226 long otherId, long offset) { 227 228 ResultLob result = new ResultLob(); 229 230 result.subType = LobResultTypes.REQUEST_GET_BYTE_PATTERN_POSITION; 231 result.lobID = id; 232 result.blockOffset = offset; 233 234 return result; 235 } 236 newLobGetCharPatternPositionRequest(long id, char[] pattern, long offset)237 public static ResultLob newLobGetCharPatternPositionRequest(long id, 238 char[] pattern, long offset) { 239 240 ResultLob result = new ResultLob(); 241 242 result.subType = LobResultTypes.REQUEST_GET_CHAR_PATTERN_POSITION; 243 result.lobID = id; 244 result.blockOffset = offset; 245 result.charBlock = pattern; 246 result.blockLength = pattern.length; 247 248 return result; 249 } 250 newLobGetCharPatternPositionRequest(long id, long otherId, long offset)251 public static ResultLob newLobGetCharPatternPositionRequest(long id, 252 long otherId, long offset) { 253 254 ResultLob result = new ResultLob(); 255 256 result.subType = LobResultTypes.REQUEST_GET_CHAR_PATTERN_POSITION; 257 result.lobID = id; 258 result.blockOffset = offset; 259 result.blockLength = otherId; 260 261 return result; 262 } 263 newLobCreateBlobRequest(long sessionID, long lobID, InputStream stream, long length)264 public static ResultLob newLobCreateBlobRequest(long sessionID, 265 long lobID, InputStream stream, long length) { 266 267 ResultLob result = new ResultLob(); 268 269 result.lobID = lobID; 270 result.subType = LobResultTypes.REQUEST_CREATE_BYTES; 271 result.blockLength = length; 272 result.stream = stream; 273 274 return result; 275 } 276 newLobCreateClobRequest(long sessionID, long lobID, Reader reader, long length)277 public static ResultLob newLobCreateClobRequest(long sessionID, 278 long lobID, Reader reader, long length) { 279 280 ResultLob result = new ResultLob(); 281 282 result.lobID = lobID; 283 result.subType = LobResultTypes.REQUEST_CREATE_CHARS; 284 result.blockLength = length; 285 result.reader = reader; 286 287 return result; 288 } 289 newLobGetTruncateLength(long id)290 public static ResultLob newLobGetTruncateLength(long id) { 291 292 ResultLob result = new ResultLob(); 293 294 result.subType = LobResultTypes.REQUEST_GET_TRUNCATE_LENGTH; 295 result.lobID = id; 296 297 return result; 298 } 299 newLobCreateBlobResponse(long id)300 public static ResultLob newLobCreateBlobResponse(long id) { 301 302 ResultLob result = new ResultLob(); 303 304 result.subType = LobResultTypes.RESPONSE_CREATE_BYTES; 305 result.lobID = id; 306 307 return result; 308 } 309 newLobCreateClobResponse(long id)310 public static ResultLob newLobCreateClobResponse(long id) { 311 312 ResultLob result = new ResultLob(); 313 314 result.subType = LobResultTypes.RESPONSE_CREATE_CHARS; 315 result.lobID = id; 316 317 return result; 318 } 319 newLobTruncateResponse(long id, long length)320 public static ResultLob newLobTruncateResponse(long id, long length) { 321 322 ResultLob result = new ResultLob(); 323 324 result.subType = LobResultTypes.RESPONSE_TRUNCATE; 325 result.lobID = id; 326 result.blockLength = length; 327 328 return result; 329 } 330 newLobGetRequest(long id, long offset, long length)331 public static ResultLob newLobGetRequest(long id, long offset, 332 long length) { 333 334 ResultLob result = new ResultLob(); 335 336 result.subType = LobResultTypes.REQUEST_GET_LOB; 337 result.lobID = id; 338 result.blockOffset = offset; 339 result.blockLength = length; 340 341 return result; 342 } 343 newLobDuplicateRequest(long id)344 public static ResultLob newLobDuplicateRequest(long id) { 345 346 ResultLob result = new ResultLob(); 347 348 result.subType = LobResultTypes.REQUEST_DUPLICATE_LOB; 349 result.lobID = id; 350 351 return result; 352 } 353 newLob(DataInput dataInput, boolean readTerminate)354 public static ResultLob newLob(DataInput dataInput, 355 boolean readTerminate) throws IOException { 356 357 ResultLob result = new ResultLob(); 358 359 result.databaseID = dataInput.readInt(); 360 result.sessionID = dataInput.readLong(); 361 result.lobID = dataInput.readLong(); 362 result.subType = dataInput.readInt(); 363 364 switch (result.subType) { 365 366 case LobResultTypes.REQUEST_CREATE_BYTES : 367 case LobResultTypes.REQUEST_CREATE_CHARS : 368 result.blockOffset = dataInput.readLong(); 369 result.blockLength = dataInput.readLong(); 370 break; 371 372 case LobResultTypes.REQUEST_GET_LOB : 373 case LobResultTypes.REQUEST_DUPLICATE_LOB : 374 375 // 376 case LobResultTypes.REQUEST_GET_BYTES : 377 case LobResultTypes.REQUEST_GET_CHARS : 378 result.blockOffset = dataInput.readLong(); 379 result.blockLength = dataInput.readLong(); 380 break; 381 382 case LobResultTypes.REQUEST_SET_BYTES : 383 case LobResultTypes.REQUEST_GET_BYTE_PATTERN_POSITION : 384 result.blockOffset = dataInput.readLong(); 385 result.blockLength = dataInput.readLong(); 386 result.byteBlock = new byte[(int) result.blockLength]; 387 388 dataInput.readFully(result.byteBlock); 389 break; 390 391 case LobResultTypes.REQUEST_SET_CHARS : 392 case LobResultTypes.REQUEST_GET_CHAR_PATTERN_POSITION : 393 result.blockOffset = dataInput.readLong(); 394 result.blockLength = dataInput.readLong(); 395 result.charBlock = new char[(int) result.blockLength]; 396 397 for (int i = 0; i < result.charBlock.length; i++) { 398 result.charBlock[i] = dataInput.readChar(); 399 } 400 break; 401 402 case LobResultTypes.REQUEST_GET_LENGTH : 403 case LobResultTypes.REQUEST_TRUNCATE : 404 result.blockOffset = dataInput.readLong(); 405 break; 406 407 case LobResultTypes.RESPONSE_GET_BYTES : 408 result.blockOffset = dataInput.readLong(); 409 result.blockLength = dataInput.readLong(); 410 result.byteBlock = new byte[(int) result.blockLength]; 411 412 dataInput.readFully(result.byteBlock); 413 break; 414 415 case LobResultTypes.RESPONSE_GET_CHARS : 416 result.blockOffset = dataInput.readLong(); 417 result.blockLength = dataInput.readLong(); 418 result.charBlock = new char[(int) result.blockLength]; 419 420 for (int i = 0; i < result.charBlock.length; i++) { 421 result.charBlock[i] = dataInput.readChar(); 422 } 423 break; 424 425 case LobResultTypes.RESPONSE_SET : 426 case LobResultTypes.RESPONSE_CREATE_BYTES : 427 case LobResultTypes.RESPONSE_CREATE_CHARS : 428 case LobResultTypes.RESPONSE_TRUNCATE : 429 result.blockLength = dataInput.readLong(); 430 break; 431 432 case LobResultTypes.RESPONSE_GET_BYTE_PATTERN_POSITION : 433 case LobResultTypes.RESPONSE_GET_CHAR_PATTERN_POSITION : 434 result.blockOffset = dataInput.readLong(); 435 break; 436 437 default : 438 throw Error.runtimeError(ErrorCode.U_S0500, "ResultLob"); 439 } 440 441 if (readTerminate) { 442 dataInput.readByte(); 443 } 444 445 return result; 446 } 447 write(SessionInterface session, DataOutputStream dataOut, RowOutputInterface rowOut)448 public void write(SessionInterface session, DataOutputStream dataOut, 449 RowOutputInterface rowOut) throws IOException { 450 451 writeBody(session, dataOut); 452 dataOut.writeByte(ResultConstants.NONE); 453 dataOut.flush(); 454 } 455 writeBody(SessionInterface session, DataOutputStream dataOut)456 public void writeBody(SessionInterface session, 457 DataOutputStream dataOut) throws IOException { 458 459 switch (subType) { 460 461 case LobResultTypes.REQUEST_CREATE_BYTES : 462 if (blockLength >= 0) { 463 writeCreate(session, dataOut); 464 465 return; 466 } 467 468 writeCreateByteSegments(session, dataOut); 469 470 return; 471 472 case LobResultTypes.REQUEST_CREATE_CHARS : { 473 if (blockLength >= 0) { 474 writeCreate(session, dataOut); 475 476 return; 477 } 478 479 writeCreateCharSegments(session, dataOut); 480 481 return; 482 } 483 } 484 485 dataOut.writeByte(mode); 486 dataOut.writeInt(databaseID); 487 dataOut.writeLong(sessionID); 488 dataOut.writeLong(lobID); 489 dataOut.writeInt(subType); 490 491 switch (subType) { 492 493 case LobResultTypes.REQUEST_SET_BYTES : 494 case LobResultTypes.REQUEST_GET_BYTE_PATTERN_POSITION : 495 dataOut.writeLong(blockOffset); 496 dataOut.writeLong(blockLength); 497 dataOut.write(byteBlock); 498 break; 499 500 case LobResultTypes.REQUEST_SET_CHARS : 501 case LobResultTypes.REQUEST_GET_CHAR_PATTERN_POSITION : 502 dataOut.writeLong(blockOffset); 503 dataOut.writeLong(blockLength); 504 dataOut.writeChars(charBlock); 505 break; 506 507 case LobResultTypes.REQUEST_GET_LOB : 508 case LobResultTypes.REQUEST_DUPLICATE_LOB : 509 510 // 511 case LobResultTypes.REQUEST_GET_BYTES : 512 case LobResultTypes.REQUEST_GET_CHARS : 513 dataOut.writeLong(blockOffset); 514 dataOut.writeLong(blockLength); 515 break; 516 517 case LobResultTypes.REQUEST_GET_LENGTH : 518 case LobResultTypes.REQUEST_TRUNCATE : 519 dataOut.writeLong(blockOffset); 520 break; 521 522 case LobResultTypes.RESPONSE_GET_BYTES : 523 dataOut.writeLong(blockOffset); 524 dataOut.writeLong(blockLength); 525 dataOut.write(byteBlock); 526 break; 527 528 case LobResultTypes.RESPONSE_GET_CHARS : 529 dataOut.writeLong(blockOffset); 530 dataOut.writeLong(blockLength); 531 dataOut.writeChars(charBlock); 532 break; 533 534 case LobResultTypes.RESPONSE_SET : 535 case LobResultTypes.RESPONSE_CREATE_BYTES : 536 case LobResultTypes.RESPONSE_CREATE_CHARS : 537 case LobResultTypes.RESPONSE_TRUNCATE : 538 dataOut.writeLong(blockLength); 539 break; 540 541 case LobResultTypes.RESPONSE_GET_BYTE_PATTERN_POSITION : 542 case LobResultTypes.RESPONSE_GET_CHAR_PATTERN_POSITION : 543 dataOut.writeLong(blockOffset); 544 break; 545 546 default : 547 throw Error.runtimeError(ErrorCode.U_S0500, "ResultLob"); 548 } 549 } 550 writeCreate(SessionInterface session, DataOutputStream dataOut)551 private void writeCreate(SessionInterface session, 552 DataOutputStream dataOut) throws IOException { 553 554 dataOut.writeByte(mode); 555 dataOut.writeInt(databaseID); 556 dataOut.writeLong(sessionID); 557 dataOut.writeLong(lobID); 558 dataOut.writeInt(subType); 559 dataOut.writeLong(blockOffset); 560 dataOut.writeLong(blockLength); 561 562 switch (subType) { 563 564 case LobResultTypes.REQUEST_CREATE_BYTES : 565 dataOut.write(stream, blockLength); 566 break; 567 568 case LobResultTypes.REQUEST_CREATE_CHARS : 569 dataOut.write(reader, blockLength); 570 break; 571 } 572 } 573 writeCreateByteSegments(SessionInterface session, DataOutputStream dataOut)574 private void writeCreateByteSegments(SessionInterface session, 575 DataOutputStream dataOut) 576 throws IOException { 577 578 // 579 int bufferLength = session.getStreamBlockSize(); 580 long currentOffset = blockOffset; 581 582 dataOut.writeByte(mode); 583 dataOut.writeInt(databaseID); 584 dataOut.writeLong(sessionID); 585 dataOut.writeLong(lobID); 586 dataOut.writeInt(subType); 587 588 HsqlByteArrayOutputStream byteArrayOS = 589 new HsqlByteArrayOutputStream(bufferLength); 590 591 byteArrayOS.reset(); 592 byteArrayOS.write(stream, bufferLength); 593 dataOut.writeLong(currentOffset); 594 dataOut.writeLong(byteArrayOS.size()); 595 dataOut.write(byteArrayOS.getBuffer(), 0, byteArrayOS.size()); 596 597 currentOffset += byteArrayOS.size(); 598 599 if (byteArrayOS.size() < bufferLength) { 600 return; 601 } 602 603 // 604 while (true) { 605 byteArrayOS.reset(); 606 byteArrayOS.write(stream, bufferLength); 607 608 if (byteArrayOS.size() == 0) { 609 break; 610 } 611 612 // 613 dataOut.writeByte(mode); 614 dataOut.writeInt(databaseID); 615 dataOut.writeLong(sessionID); 616 dataOut.writeLong(lobID); 617 dataOut.writeInt(LobResultTypes.REQUEST_SET_BYTES); 618 dataOut.writeLong(currentOffset); 619 dataOut.writeLong(byteArrayOS.size()); 620 dataOut.write(byteArrayOS.getBuffer(), 0, byteArrayOS.size()); 621 622 currentOffset += byteArrayOS.size(); 623 624 if (byteArrayOS.size() < bufferLength) { 625 break; 626 } 627 } 628 } 629 writeCreateCharSegments(SessionInterface session, DataOutputStream dataOut)630 private void writeCreateCharSegments(SessionInterface session, 631 DataOutputStream dataOut) 632 throws IOException { 633 634 // 635 int bufferLength = session.getStreamBlockSize(); 636 long currentOffset = blockOffset; 637 638 dataOut.writeByte(mode); 639 dataOut.writeInt(databaseID); 640 dataOut.writeLong(sessionID); 641 dataOut.writeLong(lobID); 642 dataOut.writeInt(subType); 643 644 HsqlByteArrayOutputStream byteArrayOS = 645 new HsqlByteArrayOutputStream(bufferLength); 646 647 byteArrayOS.reset(); 648 byteArrayOS.write(reader, bufferLength / 2); 649 650 // 651 dataOut.writeLong(currentOffset); 652 dataOut.writeLong(byteArrayOS.size() / 2); 653 dataOut.write(byteArrayOS.getBuffer(), 0, byteArrayOS.size()); 654 655 currentOffset += byteArrayOS.size() / 2; 656 657 if (byteArrayOS.size() < bufferLength) { 658 return; 659 } 660 661 // 662 while (true) { 663 byteArrayOS.reset(); 664 byteArrayOS.write(reader, bufferLength / 2); 665 666 if (byteArrayOS.size() == 0) { 667 break; 668 } 669 670 // 671 dataOut.writeByte(mode); 672 dataOut.writeInt(databaseID); 673 dataOut.writeLong(sessionID); 674 dataOut.writeLong(lobID); 675 dataOut.writeInt(LobResultTypes.REQUEST_SET_CHARS); 676 dataOut.writeLong(currentOffset); 677 dataOut.writeLong(byteArrayOS.size() / 2); 678 dataOut.write(byteArrayOS.getBuffer(), 0, byteArrayOS.size()); 679 680 currentOffset += byteArrayOS.size() / 2; 681 682 if (byteArrayOS.size() < bufferLength) { 683 break; 684 } 685 } 686 } 687 getLobID()688 public long getLobID() { 689 return lobID; 690 } 691 getSubType()692 public int getSubType() { 693 return subType; 694 } 695 getOffset()696 public long getOffset() { 697 return blockOffset; 698 } 699 getBlockLength()700 public long getBlockLength() { 701 return blockLength; 702 } 703 getByteArray()704 public byte[] getByteArray() { 705 return byteBlock; 706 } 707 getCharArray()708 public char[] getCharArray() { 709 return charBlock; 710 } 711 getInputStream()712 public InputStream getInputStream() { 713 return stream; 714 } 715 getReader()716 public Reader getReader() { 717 return reader; 718 } 719 } 720