1 30 31 32 package org.hsqldb.persist; 33 34 import java.io.IOException ; 35 import java.io.UnsupportedEncodingException ; 36 37 import org.hsqldb.HsqlException; 38 import org.hsqldb.Table; 39 import org.hsqldb.Trace; 40 import org.hsqldb.lib.FileUtil; 41 import org.hsqldb.lib.HsqlByteArrayOutputStream; 42 import org.hsqldb.rowio.RowInputInterface; 43 import org.hsqldb.rowio.RowInputText; 44 import org.hsqldb.rowio.RowInputTextQuoted; 45 import org.hsqldb.rowio.RowOutputText; 46 import org.hsqldb.rowio.RowOutputTextQuoted; 47 import org.hsqldb.scriptio.ScriptWriterText; 48 import org.hsqldb.store.ObjectCacheHashMap; 49 import org.hsqldb.Database; 50 51 56 67 public class TextCache extends DataFileCache { 68 69 public static final String NL = System.getProperty("line.separator"); 71 public String fs; 72 public String vs; 73 public String lvs; 74 public String stringEncoding; 75 public boolean isQuoted; 76 public boolean isAllQuoted; 77 public boolean ignoreFirst; 78 protected String header; 79 protected Table table; 80 private ObjectCacheHashMap uncommittedCache; 81 82 final static char DOUBLE_QUOTE_CHAR = '\"'; 84 final static char BACKSLASH_CHAR = '\\'; 85 final static char LF_CHAR = '\n'; 86 final static char CR_CHAR = '\r'; 87 88 100 TextCache(Table table, String name) throws HsqlException { 101 102 super(table.database, name); 103 104 this.table = table; 105 uncommittedCache = new ObjectCacheHashMap(5); 106 } 107 108 protected void initParams(Database database, 109 String baseFileName) throws HsqlException { 110 111 fileName = baseFileName; 112 this.database = database; 113 fa = FileUtil.getDefaultInstance(); 114 115 HsqlProperties tableprops = 116 HsqlProperties.delimitedArgPairsToProps(fileName, "=", ";", null); 117 118 switch (tableprops.errorCodes.length) { 120 121 case 0 : 122 throw Trace.error(Trace.TEXT_TABLE_SOURCE, 123 Trace.TEXT_TABLE_SOURCE_FILENAME); 124 case 1 : 125 126 fileName = tableprops.errorKeys[0].trim(); 128 break; 129 130 default : 131 throw Trace.error(Trace.TEXT_TABLE_SOURCE, 132 Trace.TEXT_TABLE_SOURCE_VALUE_MISSING, 133 tableprops.errorKeys[1]); 134 } 135 136 HsqlDatabaseProperties dbProps = database.getProperties(); 138 139 fs = translateSep(tableprops.getProperty("fs", 140 dbProps.getProperty(HsqlDatabaseProperties.textdb_fs, ","))); 141 vs = translateSep(tableprops.getProperty("vs", 142 dbProps.getProperty(HsqlDatabaseProperties.textdb_vs, fs))); 143 lvs = translateSep(tableprops.getProperty("lvs", 144 dbProps.getProperty(HsqlDatabaseProperties.textdb_lvs, fs))); 145 146 if (fs.length() == 0 || vs.length() == 0 || lvs.length() == 0) { 147 throw Trace.error(Trace.TEXT_TABLE_SOURCE, 148 Trace.TEXT_TABLE_SOURCE_SEPARATOR); 149 } 150 151 ignoreFirst = tableprops.isPropertyTrue( 153 "ignore_first", 154 dbProps.isPropertyTrue( 155 HsqlDatabaseProperties.textdb_ignore_first, false)); 156 isQuoted = tableprops.isPropertyTrue( 157 "quoted", 158 dbProps.isPropertyTrue( 159 HsqlDatabaseProperties.textdb_quoted, true)); 160 isAllQuoted = tableprops.isPropertyTrue( 161 "all_quoted", 162 dbProps.isPropertyTrue( 163 HsqlDatabaseProperties.textdb_all_quoted, false)); 164 165 stringEncoding = translateSep(tableprops.getProperty("encoding", 167 dbProps.getProperty(HsqlDatabaseProperties.textdb_encoding, 168 "ASCII"))); 169 170 int cacheScale = tableprops.getIntegerProperty( 172 "cache_scale", 173 dbProps.getIntegerProperty( 174 HsqlDatabaseProperties.textdb_cache_scale, 10, 8, 16)); 175 int cacheSizeScale = tableprops.getIntegerProperty( 176 "cache_size_scale", 177 dbProps.getIntegerProperty( 178 HsqlDatabaseProperties.textdb_cache_size_scale, 10, 8, 20)); 179 int lookupTableLength = 1 << cacheScale; 180 int avgRowBytes = 1 << cacheSizeScale; 181 182 maxCacheSize = lookupTableLength * 3; 183 maxCacheBytes = maxCacheSize * avgRowBytes; 184 maxDataFileSize = Integer.MAX_VALUE; 185 cachedRowPadding = 1; 186 cacheFileScale = 1; 187 } 188 189 protected void initBuffers() { 190 191 if (isQuoted || isAllQuoted) { 192 rowIn = new RowInputTextQuoted(fs, vs, lvs, isAllQuoted); 193 rowOut = new RowOutputTextQuoted(fs, vs, lvs, isAllQuoted, 194 stringEncoding); 195 } else { 196 rowIn = new RowInputText(fs, vs, lvs, false); 197 rowOut = new RowOutputText(fs, vs, lvs, false, stringEncoding); 198 } 199 } 200 201 private String translateSep(String sep) { 202 return translateSep(sep, false); 203 } 204 205 209 private String translateSep(String sep, boolean isProperty) { 210 211 if (sep == null) { 212 return (null); 213 } 214 215 int next = 0; 216 217 if ((next = sep.indexOf(BACKSLASH_CHAR)) != -1) { 218 int start = 0; 219 char[] sepArray = sep.toCharArray(); 220 char ch = 0; 221 int len = sep.length(); 222 StringBuffer realSep = new StringBuffer (len); 223 224 do { 225 realSep.append(sepArray, start, next - start); 226 227 start = ++next; 228 229 if (next >= len) { 230 realSep.append(BACKSLASH_CHAR); 231 232 break; 233 } 234 235 if (!isProperty) { 236 ch = sepArray[next]; 237 } 238 239 if (ch == 'n') { 240 realSep.append(LF_CHAR); 241 242 start++; 243 } else if (ch == 'r') { 244 realSep.append(CR_CHAR); 245 246 start++; 247 } else if (ch == 't') { 248 realSep.append('\t'); 249 250 start++; 251 } else if (ch == BACKSLASH_CHAR) { 252 realSep.append(BACKSLASH_CHAR); 253 254 start++; 255 } else if (ch == 'u') { 256 start++; 257 258 realSep.append( 259 (char) Integer.parseInt( 260 sep.substring(start, start + 4), 16)); 261 262 start += 4; 263 } else if (sep.startsWith("semi", next)) { 264 realSep.append(';'); 265 266 start += 4; 267 } else if (sep.startsWith("space", next)) { 268 realSep.append(' '); 269 270 start += 5; 271 } else if (sep.startsWith("quote", next)) { 272 realSep.append(DOUBLE_QUOTE_CHAR); 273 274 start += 5; 275 } else if (sep.startsWith("apos", next)) { 276 realSep.append('\''); 277 278 start += 4; 279 } else { 280 realSep.append(BACKSLASH_CHAR); 281 realSep.append(sepArray[next]); 282 283 start++; 284 } 285 } while ((next = sep.indexOf(BACKSLASH_CHAR, start)) != -1); 286 287 realSep.append(sepArray, start, len - start); 288 289 sep = realSep.toString(); 290 } 291 292 return sep; 293 } 294 295 298 public void open(boolean readonly) throws HsqlException { 299 300 fileFreePosition = 0; 301 302 try { 303 dataFile = ScaledRAFile.newScaledRAFile(database, fileName, 304 readonly, ScaledRAFile.DATA_FILE_RAF, null, null); 305 fileFreePosition = dataFile.length(); 306 307 if (fileFreePosition > Integer.MAX_VALUE) { 308 throw new IOException (); 309 } 310 311 initBuffers(); 312 } catch (Exception e) { 313 throw Trace.error(Trace.FILE_IO_ERROR, 314 Trace.TextCache_openning_file_error, 315 new Object [] { 316 fileName, e 317 }); 318 } 319 320 cacheReadonly = readonly; 321 } 322 323 void reopen() throws HsqlException { 324 open(cacheReadonly); 325 } 326 327 332 public void close(boolean write) throws HsqlException { 333 334 if (dataFile == null) { 335 return; 336 } 337 338 try { 339 cache.saveAll(); 340 341 boolean empty = (dataFile.length() <= NL.length()); 342 343 dataFile.close(); 344 345 dataFile = null; 346 347 if (empty &&!cacheReadonly) { 348 FileUtil.delete(fileName); 349 } 350 } catch (Exception e) { 351 throw Trace.error(Trace.FILE_IO_ERROR, 352 Trace.TextCache_closing_file_error, 353 new Object [] { 354 fileName, e 355 }); 356 } 357 } 358 359 362 void purge() throws HsqlException { 363 364 uncommittedCache.clear(); 365 366 try { 367 if (cacheReadonly) { 368 close(false); 369 } else { 370 if (dataFile != null) { 371 dataFile.close(); 372 373 dataFile = null; 374 } 375 376 FileUtil.delete(fileName); 377 } 378 } catch (Exception e) { 379 throw Trace.error(Trace.FILE_IO_ERROR, 380 Trace.TextCache_purging_file_error, 381 new Object [] { 382 fileName, e 383 }); 384 } 385 } 386 387 390 public synchronized void remove(int pos, 391 PersistentStore store) 392 throws IOException { 393 394 CachedObject row = (CachedObject) uncommittedCache.remove(pos); 395 396 if (row != null) { 397 return; 398 } 399 400 row = cache.release(pos); 401 402 clearRowImage(row); 403 release(pos); 404 } 405 406 private void clearRowImage(CachedObject row) throws IOException { 407 408 int length = row.getStorageSize() 409 - ScriptWriterText.BYTES_LINE_SEP.length; 410 411 rowOut.reset(); 412 413 HsqlByteArrayOutputStream out = rowOut.getOutputStream(); 414 415 out.fill(' ', length); 416 out.write(ScriptWriterText.BYTES_LINE_SEP); 417 dataFile.seek(row.getPos()); 418 dataFile.write(out.getBuffer(), 0, out.size()); 419 } 420 421 public synchronized void removePersistence(int pos, 422 PersistentStore store) throws IOException { 423 424 CachedObject row = (CachedObject) uncommittedCache.get(pos); 425 426 if (row != null) { 427 return; 428 } 429 430 row = cache.get(pos); 431 432 clearRowImage(row); 433 } 434 435 protected synchronized RowInputInterface readObject(int pos) 436 throws IOException { 437 438 ByteArray buffer = new ByteArray(80); 439 boolean complete = false; 440 boolean wasCR = false; 441 int c; 442 boolean hasQuote = false; 443 boolean wasNormal = false; 444 445 pos = findNextUsedLinePos(pos); 446 447 if (pos == -1) { 448 return null; 449 } 450 451 dataFile.seek(pos); 452 453 while (!complete) { 454 wasNormal = false; 455 c = dataFile.read(); 456 457 if (c == -1) { 458 if (buffer.length() == 0) { 459 return null; 460 } 461 462 complete = true; 463 464 if (wasCR) { 465 break; 466 } 467 468 if (!cacheReadonly) { 469 dataFile.write(ScriptWriterText.BYTES_LINE_SEP, 0, 470 ScriptWriterText.BYTES_LINE_SEP.length); 471 } 472 473 break; 474 } 475 476 switch (c) { 477 478 case DOUBLE_QUOTE_CHAR : 479 wasNormal = true; 480 complete = wasCR; 481 wasCR = false; 482 hasQuote = !hasQuote; 483 break; 484 485 case CR_CHAR : 486 wasCR = !hasQuote; 487 break; 488 489 case LF_CHAR : 490 complete = !hasQuote; 491 break; 492 493 default : 494 wasNormal = true; 495 complete = wasCR; 496 wasCR = false; 497 } 498 499 buffer.append(c); 500 } 501 502 if (complete) { 503 int length = (int) dataFile.getFilePointer() - pos; 504 505 if (wasNormal) { 506 length--; 507 } 508 509 ((RowInputText) rowIn).setSource(buffer.toString(), pos, length); 510 511 return rowIn; 512 } 513 514 return null; 515 } 516 517 public int readHeaderLine() throws HsqlException { 518 519 boolean complete = false; 520 boolean wasCR = false; 521 boolean wasNormal = false; 522 ByteArray buffer = new ByteArray(80); 523 524 while (!complete) { 525 wasNormal = false; 526 527 int c; 528 529 try { 530 c = dataFile.read(); 531 532 if (c == -1) { 533 if (buffer.length() == 0) { 534 return 0; 535 } 536 537 complete = true; 538 539 if (!cacheReadonly) { 540 dataFile.write( 541 ScriptWriterText.BYTES_LINE_SEP, 0, 542 ScriptWriterText.BYTES_LINE_SEP.length); 543 } 544 545 break; 546 } 547 } catch (IOException e) { 548 throw Trace.error(Trace.TEXT_FILE); 549 } 550 551 switch (c) { 552 553 case CR_CHAR : 554 wasCR = true; 555 break; 556 557 case LF_CHAR : 558 complete = true; 559 break; 560 561 default : 562 wasNormal = true; 563 complete = wasCR; 564 wasCR = false; 565 } 566 567 buffer.append(c); 568 } 569 570 header = buffer.toString(); 571 572 try { 573 int length = (int) dataFile.getFilePointer(); 574 575 if (wasNormal) { 576 length--; 577 } 578 579 return length; 580 } catch (IOException e) { 581 throw Trace.error(Trace.TEXT_FILE); 582 } 583 } 584 585 587 594 int findNextUsedLinePos(int pos) throws IOException { 595 596 int firstPos = pos; 597 int currentPos = pos; 598 boolean wasCR = false; 599 600 dataFile.seek(pos); 601 602 while (true) { 603 int c = dataFile.read(); 604 605 currentPos++; 606 607 switch (c) { 608 609 case CR_CHAR : 610 wasCR = true; 611 break; 612 613 case LF_CHAR : 614 wasCR = false; 615 616 ((RowInputText) rowIn).skippedLine(); 617 618 firstPos = currentPos; 619 break; 620 621 case ' ' : 622 if (wasCR) { 623 wasCR = false; 624 625 ((RowInputText) rowIn).skippedLine(); 626 } 627 break; 628 629 case -1 : 630 return -1; 631 632 default : 633 return firstPos; 634 } 635 } 636 } 637 638 public synchronized void add(CachedObject object) throws IOException { 639 super.add(object); 640 clearRowImage(object); 641 } 642 643 public synchronized CachedObject get(int i, PersistentStore store, 644 boolean keep) throws HsqlException { 645 646 if (i < 0) { 647 return null; 648 } 649 650 CachedObject o = (CachedObject) uncommittedCache.get(i); 651 652 if (o == null) { 653 o = super.get(i, store, keep); 654 } 655 656 661 return o; 662 } 663 664 671 protected synchronized void saveRows(CachedObject[] rows, int offset, 672 int count) throws IOException { 673 674 if (count == 0) { 675 return; 676 } 677 678 for (int i = offset; i < offset + count; i++) { 679 CachedObject r = rows[i]; 680 681 uncommittedCache.put(r.getPos(), r); 682 683 rows[i] = null; 684 } 685 } 686 687 691 public synchronized void saveRow(CachedObject row) throws IOException { 692 uncommittedCache.remove(row.getPos()); 693 super.saveRow(row); 694 } 695 696 public String getHeader() { 697 return header; 698 } 699 700 public void setHeader(String header) throws HsqlException { 701 702 if (ignoreFirst && fileFreePosition == 0) { 703 try { 704 writeHeader(header); 705 706 this.header = header; 707 } catch (IOException e) { 708 throw new HsqlException( 709 e, Trace.getMessage(Trace.GENERAL_IO_ERROR), 710 Trace.GENERAL_IO_ERROR); 711 } 712 713 return; 714 } 715 716 throw Trace.error(Trace.TEXT_TABLE_HEADER); 717 } 718 719 private void writeHeader(String header) throws IOException { 720 721 byte[] buf = null; 722 String firstLine = header + NL; 723 724 try { 725 buf = firstLine.getBytes(stringEncoding); 726 } catch (UnsupportedEncodingException e) { 727 buf = firstLine.getBytes(); 728 } 729 730 dataFile.write(buf, 0, buf.length); 731 732 fileFreePosition = buf.length; 733 } 734 735 private class ByteArray { 736 737 private byte[] buffer; 738 private int buflen; 739 740 public ByteArray(int n) { 741 buffer = new byte[n]; 742 buflen = 0; 743 } 744 745 public void append(int c) { 746 747 if (buflen >= buffer.length) { 748 byte[] newbuf = new byte[buflen + 80]; 749 750 System.arraycopy(buffer, 0, newbuf, 0, buflen); 751 752 buffer = newbuf; 753 } 754 755 buffer[buflen] = (byte) c; 756 757 buflen++; 758 } 759 760 public int length() { 761 return buflen; 762 } 763 764 public void setLength(int l) { 765 buflen = l; 766 } 767 768 public String toString() { 769 770 try { 771 return new String (buffer, 0, buflen, stringEncoding); 772 } catch (UnsupportedEncodingException e) { 773 return new String (buffer, 0, buflen); 774 } 775 } 776 } 777 778 public int getLineNumber() { 779 return ((RowInputText) rowIn).getLineNumber(); 780 } 781 782 protected void setFileModified() throws IOException { 783 fileModified = true; 784 } 785 } 786 | Popular Tags |