1 19 20 package org.openide.filesystems; 21 22 import java.beans.PropertyVetoException ; 23 import java.io.ByteArrayInputStream ; 24 import java.io.ByteArrayOutputStream ; 25 import java.io.File ; 26 import java.io.FileInputStream ; 27 import java.io.FileOutputStream ; 28 import java.io.IOException ; 29 import java.io.InputStream ; 30 import java.io.ObjectInputStream ; 31 import java.io.OutputStream ; 32 import java.io.UnsupportedEncodingException ; 33 import java.lang.ref.Reference ; 34 import java.lang.ref.SoftReference ; 35 import java.lang.ref.WeakReference ; 36 import java.util.Collection ; 37 import java.util.Collections ; 38 import java.util.Enumeration ; 39 import java.util.HashMap ; 40 import java.util.Iterator ; 41 import java.util.Map ; 42 import java.util.jar.Attributes ; 43 import java.util.jar.JarEntry ; 44 import java.util.jar.JarFile ; 45 import java.util.jar.Manifest ; 46 import java.util.zip.ZipException ; 47 import org.openide.util.Enumerations; 48 import org.openide.util.RequestProcessor; 49 import org.openide.util.Utilities; 50 51 59 public class JarFileSystem extends AbstractFileSystem { 60 61 static final long serialVersionUID = -98124752801761145L; 62 63 64 private static RequestProcessor req = new RequestProcessor("JarFs - modification watcher"); 66 69 private static final int REFRESH_TIME = Integer.getInteger("org.openide.filesystems.JarFileSystem.REFRESH_TIME", 0) 70 .intValue(); 72 73 private static final long MEM_STREAM_SIZE = 100000; 74 75 78 private transient JarFile jar; 79 80 82 private transient Manifest manifest; 83 84 86 private File root = new File ("."); 88 89 private transient RequestProcessor.Task watcherTask = null; 90 private transient RequestProcessor.Task closeTask = null; 91 private transient long lastModification = 0; 92 93 95 private transient Object closeSync = new Object (); 96 private int checkTime = REFRESH_TIME; 97 98 100 private transient long aliveCount = 0; 101 102 105 private transient Cache strongCache; 106 107 108 private transient Reference <Cache> softCache = new SoftReference <Cache>(null); 109 private transient FileObject foRoot; 110 private transient FileChangeListener fcl; 111 112 117 public JarFileSystem() { 118 Impl impl = new Impl(this); 119 this.list = impl; 120 this.info = impl; 121 this.change = impl; 122 this.attr = impl; 123 } 124 125 130 @Deprecated 131 public JarFileSystem(FileSystemCapability cap) { 132 this(); 133 setCapability(cap); 134 } 135 136 141 protected <T extends FileObject> Reference <T> createReference(T fo) { 142 aliveCount++; 143 144 if ((checkTime > 0) && (watcherTask == null)) { 145 watcherTask = req.post(watcherTask(), checkTime); 146 } 147 148 return new Ref<T>(fo); 149 } 150 151 private void freeReference() { 152 aliveCount--; 153 154 if (aliveCount == 0) { 156 if (watcherTask != null) { 157 watcherTask.cancel(); 158 watcherTask = null; 159 } 160 161 strongCache = null; closeCurrentRoot(false); 163 } 164 } 165 166 170 public Manifest getManifest() { 171 if (manifest == null) { 172 try { 173 synchronized (closeSync) { 174 JarFile j = reOpenJarFile(); 175 manifest = (j == null) ? null : j.getManifest(); 176 manifest = (manifest == null) ? null : new Manifest (manifest); 177 } 178 } catch (IOException ex) { 179 } finally { 180 closeCurrentRoot(false); 181 } 182 183 if (manifest == null) { 184 manifest = new Manifest (); 185 } 186 } 187 188 return manifest; 189 } 190 191 196 public void setJarFile(final File aRoot) throws IOException , PropertyVetoException { 197 setJarFile(aRoot, true); 198 } 199 200 @SuppressWarnings ("deprecation") private void _setSystemName(String s) throws PropertyVetoException { 202 setSystemName(s); 203 } 204 205 private void setJarFile(final File aRoot, boolean refreshRoot) 206 throws IOException , PropertyVetoException { 207 if (!aRoot.equals(FileUtil.normalizeFile(aRoot))) { 208 throw new IllegalArgumentException ( 209 "Parameter aRoot was not " + "normalized. Was " + aRoot + " instead of " + FileUtil.normalizeFile(aRoot) 211 ); } 213 214 FileObject newRoot = null; 215 String oldDisplayName = getDisplayName(); 216 217 if (getRefreshTime() > 0) { 218 setRefreshTime(0); 219 } 220 221 if (aRoot == null) { 222 FSException.io("EXC_NotValidFile", aRoot); } 224 225 if (!aRoot.exists()) { 226 FSException.io("EXC_FileNotExists", aRoot.getAbsolutePath()); } 228 229 if (!aRoot.canRead()) { 230 FSException.io("EXC_CanntRead", aRoot.getAbsolutePath()); } 232 233 if (!aRoot.isFile()) { 234 FSException.io("EXC_NotValidFile", aRoot.getAbsolutePath()); } 236 237 String s; 238 s = aRoot.getAbsolutePath(); 239 s = s.intern(); 240 241 JarFile tempJar = null; 242 243 try { 244 tempJar = new JarFile (s); 245 } catch (ZipException e) { 246 FSException.io("EXC_NotValidJarFile2", e.getLocalizedMessage(), s); } 248 249 synchronized (closeSync) { 250 _setSystemName(s); 251 252 closeCurrentRoot(false); 253 jar = tempJar; 254 root = new File (s); 255 256 if (refreshRoot) { 257 strongCache = null; 258 softCache.clear(); 259 aliveCount = 0; 260 newRoot = refreshRoot(); 261 manifest = null; 262 lastModification = 0; 263 264 if (newRoot != null) { 265 firePropertyChange("root", null, newRoot); } 267 } 268 } 269 270 firePropertyChange(PROP_DISPLAY_NAME, oldDisplayName, getDisplayName()); 271 272 foRoot = FileUtil.toFileObject(root); 273 274 if ((foRoot != null) && (fcl == null)) { 275 fcl = new FileChangeAdapter() { 276 public void fileChanged(FileEvent fe) { 277 if (watcherTask == null) { 278 parse(true); 279 } 280 } 281 282 public void fileRenamed(FileRenameEvent fe) { 283 File f = FileUtil.toFile(fe.getFile()); 284 285 if ((f != null) && !f.equals(aRoot)) { 286 try { 287 setJarFile(f, false); 288 } catch (IOException iex) { 289 ExternalUtil.exception(iex); 290 } catch (PropertyVetoException pvex) { 291 ExternalUtil.exception(pvex); 292 } 293 } 294 } 295 296 public void fileDeleted(FileEvent fe) { 297 Enumeration <? extends FileObject> en = existingFileObjects(getRoot()); 298 299 while (en.hasMoreElements()) { 300 AbstractFolder fo = (AbstractFolder) en.nextElement(); 301 fo.validFlag = false; 302 fo.fileDeleted0(new FileEvent(fo)); 303 } 304 305 refreshRoot(); 306 } 307 }; 308 309 if (refreshRoot) { 310 foRoot.addFileChangeListener(FileUtil.weakFileChangeListener(fcl, foRoot)); 311 } 312 } 313 } 314 315 318 public File getJarFile() { 319 return root; 320 } 321 322 326 public String getDisplayName() { 327 return (root != null) ? root.getAbsolutePath() : getString("JAR_UnknownJar"); 328 } 329 330 333 public boolean isReadOnly() { 334 return true; 335 } 336 337 338 public void removeNotify() { 339 closeCurrentRoot(true); 340 } 341 342 343 344 348 353 @Deprecated 354 public void prepareEnvironment(Environment env) { 355 if (root != null) { 356 env.addClassPath(root.getAbsolutePath()); 357 } 358 } 359 360 protected String [] children(String name) { 364 Cache cache = getCache(); 365 366 return cache.getChildrenOf(name); 367 } 368 369 protected void createFolder(String name) throws java.io.IOException { 373 throw new IOException (); 374 } 375 376 protected void createData(String name) throws IOException { 377 throw new IOException (); 378 } 379 380 protected void rename(String oldName, String newName) 381 throws IOException { 382 throw new IOException (); 383 } 384 385 protected void delete(String name) throws IOException { 386 throw new IOException (); 387 } 388 389 protected java.util.Date lastModified(String name) { 393 try { 394 return new java.util.Date (getEntry(name).getTime()); 395 } finally { 396 closeCurrentRoot(false); 397 } 398 } 399 400 protected boolean folder(String name) { 401 if ("".equals(name)) { 402 return true; } 404 405 Cache cache = getCache(); 406 407 return cache.isFolder(name); 408 } 409 410 protected boolean readOnly(String name) { 411 return true; 412 } 413 414 protected String mimeType(String name) { 415 return null; 416 } 417 418 protected long size(String name) { 419 long retVal = getEntry(name).getSize(); 420 closeCurrentRoot(false); 421 422 return (retVal == -1) ? 0 : retVal; 423 } 424 425 private InputStream getMemInputStream(JarFile jf, JarEntry je) 426 throws IOException { 427 InputStream is = getInputStream4336753(jf, je); 428 ByteArrayOutputStream os = new ByteArrayOutputStream (is.available()); 429 430 try { 431 FileUtil.copy(is, os); 432 } finally { 433 os.close(); 434 } 435 436 return new ByteArrayInputStream (os.toByteArray()); 437 } 438 439 private InputStream getTemporaryInputStream(JarFile jf, JarEntry je, boolean forceRecreate) 440 throws IOException { 441 String filePath = jf.getName(); 442 String entryPath = je.getName(); 443 StringBuffer jarCacheFolder = new StringBuffer ("jarfscache"); jarCacheFolder.append(System.getProperty("user.name")).append("/"); 446 File jarfscache = new File (System.getProperty("java.io.tmpdir"), jarCacheFolder.toString()); 448 if (!jarfscache.exists()) { 449 jarfscache.mkdirs(); 450 } 451 452 File f = new File (jarfscache, temporaryName(filePath, entryPath)); 453 454 boolean createContent = !f.exists(); 455 456 if (createContent) { 457 f.createNewFile(); 458 } else { 459 forceRecreate |= (Math.abs((System.currentTimeMillis() - f.lastModified())) > 10000); 460 } 461 462 if (createContent || forceRecreate) { 463 InputStream is = getInputStream4336753(jf, je); 466 467 try { 468 OutputStream os = new FileOutputStream (f); 469 470 try { 471 FileUtil.copy(is, os); 472 } finally { 473 os.close(); 474 } 475 } finally { 476 is.close(); 477 } 478 } 479 480 f.deleteOnExit(); 481 482 return new FileInputStream (f); 483 } 484 485 private static String temporaryName(String filePath, String entryPath) { 486 String fileHash = String.valueOf(filePath.hashCode()); 487 String entryHash = String.valueOf(entryPath.hashCode()); 488 489 StringBuffer sb = new StringBuffer (); 490 sb.append("f").append(fileHash).append("e").append(entryHash); 491 492 return sb.toString().replace('-', 'x'); } 494 495 protected InputStream inputStream(String name) throws java.io.FileNotFoundException { 496 InputStream is = null; 497 498 try { 499 synchronized (closeSync) { 500 JarFile j = reOpenJarFile(); 501 502 if (j != null) { 503 JarEntry je = j.getJarEntry(name); 504 505 if (je != null) { 506 if (je.getSize() < MEM_STREAM_SIZE) { 507 is = getMemInputStream(j, je); 508 } else { 509 is = getTemporaryInputStream(j, je, (strongCache != null)); 510 } 511 } 512 } 513 } 514 } catch (java.io.FileNotFoundException e) { 515 throw e; 516 } catch (IOException e) { 517 throw new java.io.FileNotFoundException (e.getMessage()); 518 } catch (RuntimeException e) { 519 throw new java.io.FileNotFoundException (e.getMessage()); 520 } finally { 521 closeCurrentRoot(false); 522 } 523 524 if (is == null) { 525 throw new java.io.FileNotFoundException (name); 526 } 527 528 return is; 529 } 530 531 private InputStream getInputStream4336753(JarFile j, JarEntry je) 533 throws IOException { 534 InputStream in = null; 535 536 while (in == null) { 537 try { 538 in = j.getInputStream(je); 539 540 break; 541 } catch (NullPointerException ex) { 542 } 545 } 546 547 return in; 548 } 549 550 protected OutputStream outputStream(String name) throws java.io.IOException { 551 throw new IOException (); 552 } 553 554 protected void lock(String name) throws IOException { 555 FSException.io("EXC_CannotLock", name, getDisplayName(), name); } 557 558 protected void unlock(String name) { 559 } 560 561 protected void markUnimportant(String name) { 562 } 563 564 protected Object readAttribute(String name, String attrName) { 565 Attributes attr = getManifest().getAttributes(name); 566 567 try { 568 return (attr == null) ? null : attr.getValue(attrName); 569 } catch (IllegalArgumentException iax) { 570 return null; 571 } 572 } 573 574 protected void writeAttribute(String name, String attrName, Object value) 575 throws IOException { 576 throw new IOException (); 577 } 578 579 protected Enumeration <String > attributes(String name) { 580 Attributes attr = getManifest().getAttributes(name); 581 582 if (attr != null) { 583 class ToString implements org.openide.util.Enumerations.Processor<Object , String > { 584 public String process(Object obj, Collection <Object > ignore) { 585 return obj.toString(); 586 } 587 } 588 589 return org.openide.util.Enumerations.convert(Collections.enumeration(attr.keySet()), new ToString()); 590 } else { 591 return org.openide.util.Enumerations.empty(); 592 } 593 } 594 595 protected void renameAttributes(String oldName, String newName) { 596 } 597 598 protected void deleteAttributes(String name) { 599 } 600 601 602 protected void finalize() throws Throwable { 603 super.finalize(); 604 closeCurrentRoot(false); 605 } 606 607 609 private void readObject(ObjectInputStream ois) throws IOException , ClassNotFoundException { 610 ois.defaultReadObject(); 611 closeSync = new Object (); 612 strongCache = null; 613 softCache = new SoftReference <Cache>(null); 614 aliveCount = 0; 615 616 try { 617 setJarFile(root); 618 } catch (PropertyVetoException ex) { 619 throw new IOException (ex.getMessage()); 620 } catch (IOException iex) { 621 ExternalUtil.log(iex.getLocalizedMessage()); 622 } 623 } 624 625 626 private JarFile reOpenJarFile() throws IOException { 627 synchronized (closeSync) { 628 if (closeTask != null) { 629 closeTask.cancel(); 630 } 631 632 JarFile j = jar; 633 634 if (j != null) { 635 return j; 636 } 637 638 if ((jar == null) && (root != null)) { 639 jar = new JarFile (root); 640 } 641 642 return jar; 643 } 644 } 645 646 649 private void closeCurrentRoot(boolean isRealClose) { 650 synchronized (closeSync) { 651 if (closeTask != null) { 652 closeTask.cancel(); 653 } 654 655 if (isRealClose) { 656 realClose().run(); 657 } else { 658 closeTask = req.post(realClose(), 300); 659 } 660 } 661 } 662 663 private Runnable realClose() { 664 return new Runnable () { 665 public void run() { 666 synchronized (closeSync) { 667 if (jar != null) { 668 try { 669 jar.close(); 670 } catch (Exception exc) { 671 ExternalUtil.exception(exc); 673 } finally { 674 jar = null; 675 closeTask = null; 676 } 677 } 678 } 679 } 680 }; 681 } 682 683 private Cache getCache() { 684 Cache ret = strongCache; 685 686 if (ret == null) { 687 ret = softCache.get(); 688 } 689 690 if (ret == null) { 691 ret = parse(false); 692 } 693 694 assert ret != null; 695 696 return ret; 697 } 698 699 700 private void refreshExistingFileObjects() { 701 Cache cache = getCache(); 702 String [] empty = new String [0]; 703 704 Enumeration <? extends FileObject> en = existingFileObjects(getRoot()); 705 706 while (en.hasMoreElements()) { 707 AbstractFolder fo = (AbstractFolder) en.nextElement(); 708 assert fo != null; 709 710 if (fo.isFolder() && !fo.isInitialized()) { 711 continue; 712 } 713 714 String [] children = cache.getChildrenOf(fo.getPath()); 715 716 if (children == null) { 717 children = empty; 718 } 719 720 fo.refresh(null, null, true, true, children); 721 } 722 } 723 724 727 private Cache parse(boolean refresh) { 728 JarFile j = null; 730 long start; 731 732 beginAtomicAction(); 733 734 try { 735 synchronized (closeSync) { 736 start = System.currentTimeMillis(); 737 738 lastModification = 0; 739 closeCurrentRoot(false); 740 741 for (int i = 0; i <= 2; i++) { 742 try { 743 j = reOpenJarFile(); 744 745 break; 746 } catch (IOException ex) { 747 if (i >= 2) { 748 return Cache.INVALID; 749 } 750 751 continue; 752 } 753 } 754 755 try { 756 Enumeration <JarEntry > en = j.entries(); 757 Cache newCache = new Cache(en); 758 lastModification = root.lastModified(); 759 strongCache = newCache; 760 softCache = new SoftReference <Cache>(newCache); 761 762 return newCache; 763 } catch (Throwable t) { 764 return Cache.INVALID; 767 } 768 } 769 } finally { 770 closeCurrentRoot(false); 771 772 if (refresh) { 773 refreshExistingFileObjects(); 774 } 775 776 if ((checkTime > 0) && (watcherTask == null)) { 777 watcherTask = req.post(watcherTask(), checkTime); 778 } 779 780 finishAtomicAction(); 781 } 782 } 783 784 787 private Runnable watcherTask() { 788 return new Runnable () { 789 public void run() { 790 try { 791 if (root == null) { 792 return; 793 } 794 795 796 if (root.lastModified() != lastModification) { 797 parse(true); 798 } 799 } finally { 800 801 if (watcherTask != null) { 802 watcherTask.schedule(checkTime); 803 } 804 } 805 } 806 }; 807 } 808 809 811 private final JarEntry getEntry(String file) { 812 JarFile j = null; 813 814 try { 815 synchronized (closeSync) { 816 j = reOpenJarFile(); 817 818 JarEntry je = j.getJarEntry(file); 819 820 if (je != null) { 821 return je; 822 } 823 } 824 } catch (IOException iox) { 825 } 826 827 return new JarEntry (file); 828 } 829 830 834 private class Ref<T extends FileObject> extends WeakReference <T> implements Runnable { 835 public Ref(T fo) { 836 super(fo, Utilities.activeReferenceQueue()); 837 } 838 839 public void run() { 841 freeReference(); 842 } 843 } 844 845 848 public static class Impl extends Object implements AbstractFileSystem.List, AbstractFileSystem.Info, 849 AbstractFileSystem.Change, AbstractFileSystem.Attr { 850 851 static final long serialVersionUID = -67233308132567232L; 852 853 854 private JarFileSystem fs; 855 856 859 public Impl(JarFileSystem fs) { 860 this.fs = fs; 861 } 862 863 867 public String [] children(String name) { 868 return fs.children(name); 869 } 870 871 875 880 public void createFolder(String name) throws java.io.IOException { 881 fs.createFolder(name); 882 } 883 884 892 public void createData(String name) throws IOException { 893 fs.createData(name); 894 } 895 896 902 public void rename(String oldName, String newName) 903 throws IOException { 904 fs.rename(oldName, newName); 905 } 906 907 913 public void delete(String name) throws IOException { 914 fs.delete(name); 915 } 916 917 921 927 public java.util.Date lastModified(String name) { 928 return fs.lastModified(name); 929 } 930 931 936 public boolean folder(String name) { 937 return fs.folder(name); 938 } 939 940 945 public boolean readOnly(String name) { 946 return fs.readOnly(name); 947 } 948 949 956 public String mimeType(String name) { 957 return fs.mimeType(name); 958 } 959 960 967 public long size(String name) { 968 return fs.size(name); 969 } 970 971 978 public InputStream inputStream(String name) throws java.io.FileNotFoundException { 979 return fs.inputStream(name); 980 } 981 982 989 public OutputStream outputStream(String name) throws java.io.IOException { 990 return fs.outputStream(name); 991 } 992 993 998 public void lock(String name) throws IOException { 999 fs.lock(name); 1000 } 1001 1002 1007 public void unlock(String name) { 1008 fs.unlock(name); 1009 } 1010 1011 1016 public void markUnimportant(String name) { 1017 fs.markUnimportant(name); 1018 } 1019 1020 1026 public Object readAttribute(String name, String attrName) { 1027 return fs.readAttribute(name, attrName); 1028 } 1029 1030 1037 public void writeAttribute(String name, String attrName, Object value) 1038 throws IOException { 1039 fs.writeAttribute(name, attrName, value); 1040 } 1041 1042 1047 public Enumeration <String > attributes(String name) { 1048 return fs.attributes(name); 1049 } 1050 1051 1057 public void renameAttributes(String oldName, String newName) { 1058 fs.renameAttributes(oldName, newName); 1059 } 1060 1061 1066 public void deleteAttributes(String name) { 1067 fs.deleteAttributes(name); 1068 } 1069 } 1070 1071 private static class Cache { 1072 static Cache INVALID = new Cache(Enumerations.<JarEntry >empty()); 1073 byte[] names = new byte[1000]; 1074 private int nameOffset = 0; 1075 int[] EMPTY = new int[0]; 1076 private Map <String , Folder> folders = new HashMap <String , Folder>(); 1077 1078 public Cache(Enumeration <JarEntry > en) { 1079 parse(en); 1080 trunc(); 1081 } 1082 1083 public boolean isFolder(String name) { 1084 return folders.get(name) != null; 1085 } 1086 1087 public String [] getChildrenOf(String folder) { 1088 Folder fol = folders.get(folder); 1089 1090 if (fol != null) { 1091 return fol.getNames(); 1092 } 1093 1094 return new String [] { }; 1095 } 1096 1097 private void parse(Enumeration <JarEntry > en) { 1098 folders.put("", new Folder()); 1100 while (en.hasMoreElements()) { 1101 JarEntry je = en.nextElement(); 1102 String name = je.getName(); 1103 boolean isFolder = false; 1104 1105 name = name.replace('\\', '/'); 1107 1108 if (name.startsWith("/")) { 1109 name = name.substring(1); } 1111 1112 if (name.endsWith("/")) { 1113 name = name.substring(0, name.length() - 1); isFolder = true; 1115 } 1116 1117 int lastSlash = name.lastIndexOf('/'); 1118 String dirName = ""; String realName = name; 1120 1121 if (lastSlash > 0) { 1122 dirName = name.substring(0, lastSlash); realName = name.substring(lastSlash + 1); 1124 } 1125 1126 if (isFolder) { 1127 getFolder(name); } else { 1129 Folder fl = getFolder(dirName); 1130 fl.addChild(realName); 1131 } 1132 } 1133 } 1134 1135 private Folder getFolder(String name) { 1136 Folder fl = folders.get(name); 1137 1138 if (fl == null) { 1139 int lastSlash = name.lastIndexOf('/'); 1141 String dirName = ""; String realName = name; 1143 1144 if (lastSlash > 0) { 1145 dirName = name.substring(0, lastSlash); realName = name.substring(lastSlash + 1); 1147 } 1148 1149 getFolder(dirName).addChild(realName); 1150 1151 fl = new Folder(); 1152 folders.put(name, fl); 1153 } 1154 1155 return fl; 1156 } 1157 1158 private void trunc() { 1159 byte[] newNames = new byte[nameOffset]; 1161 System.arraycopy(names, 0, newNames, 0, nameOffset); 1162 names = newNames; 1163 1164 for (Iterator it = folders.values().iterator(); it.hasNext();) { 1166 ((Folder) it.next()).trunc(); 1167 } 1168 } 1169 1170 private int putName(byte[] name) { 1171 int start = nameOffset; 1172 1173 if ((start + name.length) > names.length) { 1174 byte[] newNames = new byte[(names.length * 2) + name.length]; 1175 System.arraycopy(names, 0, newNames, 0, start); 1176 names = newNames; 1177 } 1178 1179 System.arraycopy(name, 0, names, start, name.length); 1180 nameOffset += name.length; 1181 1182 return start; 1183 } 1184 1185 private class Folder { 1186 private int[] indices = EMPTY; 1187 private int idx = 0; 1188 1189 public Folder() { 1190 } 1191 1192 public String [] getNames() { 1193 String [] ret = new String [idx / 2]; 1194 1195 for (int i = 0; i < ret.length; i++) { 1196 byte[] name = new byte[indices[(2 * i) + 1]]; 1197 System.arraycopy(names, indices[2 * i], name, 0, name.length); 1198 1199 try { 1200 ret[i] = new String (name, "UTF-8"); 1201 } catch (UnsupportedEncodingException e) { 1202 throw new InternalError ("No UTF-8"); 1203 } 1204 } 1205 1206 return ret; 1207 } 1208 1209 void addChild(String name) { 1210 if ((idx + 2) > indices.length) { 1212 int[] newInd = new int[(2 * indices.length) + 2]; 1213 System.arraycopy(indices, 0, newInd, 0, idx); 1214 indices = newInd; 1215 } 1216 1217 try { 1218 byte[] bytes = name.getBytes("UTF-8"); 1219 indices[idx++] = putName(bytes); 1220 indices[idx++] = bytes.length; 1221 } catch (UnsupportedEncodingException e) { 1222 throw new InternalError ("No UTF-8"); 1223 } 1224 } 1225 1226 void trunc() { 1227 if (indices.length > idx) { 1228 int[] newInd = new int[idx]; 1229 System.arraycopy(indices, 0, newInd, 0, idx); 1230 indices = newInd; 1231 } 1232 } 1233 } 1234 } 1235} 1236 | Popular Tags |