1 19 20 package org.netbeans.core.startup.layers; 21 22 import java.beans.PropertyVetoException ; 23 import java.io.ByteArrayInputStream ; 24 import java.io.File ; 25 import java.io.FileNotFoundException ; 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.io.OutputStream ; 29 import java.io.RandomAccessFile ; 30 import java.lang.reflect.Field ; 31 import java.lang.reflect.Method ; 32 import java.net.URL ; 33 import java.nio.ByteBuffer ; 34 import java.nio.ByteOrder ; 35 import java.util.AbstractMap ; 36 import java.util.AbstractSet ; 37 import java.nio.MappedByteBuffer ; 38 import java.nio.channels.FileChannel ; 39 import java.util.ArrayList ; 40 import java.util.Arrays ; 41 import java.util.Collections ; 42 import java.util.Date ; 43 import java.util.Iterator ; 44 import java.util.Enumeration ; 45 import java.util.List ; 46 import java.util.Set ; 47 import java.util.HashMap ; 48 import java.util.Map ; 49 import java.util.StringTokenizer ; 50 import java.util.logging.Level ; 51 import java.util.logging.Logger ; 52 import org.openide.filesystems.FileChangeListener; 53 import org.openide.filesystems.FileLock; 54 import org.openide.filesystems.FileObject; 55 import org.openide.filesystems.FileSystem; 56 import org.openide.util.Enumerations; 57 import org.openide.util.Exceptions; 58 import org.openide.util.Lookup; 59 import org.openide.util.SharedClassObject; 60 import org.openide.util.Union2; 61 import org.openide.util.actions.SystemAction; 62 import org.openide.util.io.NbObjectInputStream; 63 64 71 public class BinaryFS extends FileSystem { 72 79 80 static final byte[] MAGIC = "org.netbeans.core.projects.cache.BinaryV3".getBytes(); 82 83 static final SystemAction[] NO_ACTIONS = new SystemAction[0]; 84 85 86 static final FileObject[] NO_CHILDREN = new FileObject[0]; 87 88 String binaryFile; 89 90 ByteBuffer content; 92 93 private FileObject root; 94 95 private List <Union2<String ,Long >> modifications; 96 private final Date lastModified = new Date (); 97 98 @SuppressWarnings ("deprecation") 99 private void _setSystemName(String s) throws PropertyVetoException { 100 setSystemName(s); 101 } 102 103 104 public BinaryFS(String binaryFile) throws IOException { 105 try { 106 _setSystemName("BinaryFS" + binaryFile.replace('/', '-').replace(File.separatorChar, '-')); } catch (PropertyVetoException ex) { 108 throw (IOException )new IOException ().initCause(ex); 109 } 110 this.binaryFile = binaryFile; 111 RandomAccessFile file = new RandomAccessFile (binaryFile, "r"); long len = file.length(); 113 MappedByteBuffer buff = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, len); 114 buff.order(ByteOrder.LITTLE_ENDIAN); 115 file.close(); 116 117 byte[] magic = new byte[MAGIC.length]; 119 buff.get(magic); 120 if (!Arrays.equals(magic, MAGIC)) { 121 throw new IOException ("Bad magic header: " + new String (magic)); } 123 long storedLen = buff.getInt(); 124 if (len != storedLen) { 125 throw new IOException ("Corrupted image, correct length=" + storedLen); } 127 128 129 int stop = buff.getInt() + 8 + MAGIC.length; 131 modifications = new ArrayList <Union2<String ,Long >>(); 132 while (buff.position() < stop) { 133 modifications.add(Union2.<String ,Long >createFirst(getString(buff))); 134 } 135 136 137 content = buff.slice().order(ByteOrder.LITTLE_ENDIAN); 139 root = new BFSFolder("", null, 0); 140 } 141 142 148 public FileObject findResource(String name) { 149 if (name.length () == 0) { 150 return root; 151 } 152 153 StringTokenizer st = new StringTokenizer (name, "/"); 154 FileObject fo = root; 155 while (fo != null && st.hasMoreTokens()) { 156 String next = st.nextToken(); 157 fo = fo.getFileObject(next, null); } 159 160 return fo; 161 } 162 163 173 public SystemAction[] getActions() { 174 return NO_ACTIONS; 175 } 176 177 189 public String getDisplayName() { 190 return "BinaryFS[" + binaryFile + "]"; 191 } 192 193 198 public FileObject getRoot() { 199 return root; 201 } 202 203 207 public boolean isReadOnly() { 208 return true; 209 } 210 211 static String getString(ByteBuffer buffer) throws IOException { 212 int len = buffer.getInt(); 213 byte[] arr = new byte[len]; 214 buffer.get(arr); 215 return new String (arr, "UTF-8"); } 217 218 219 231 private abstract class BFSBase extends FileObject { private final FileObject parent; 234 protected final String name; 235 protected final int offset; 236 237 private boolean initialized = false; 239 private Map <String , AttrImpl> attrs = Collections.emptyMap(); 240 241 public BFSBase(String name, FileObject parent, int offset) { 242 this.name = name; 243 this.parent = parent; 244 this.offset = offset; 245 } 246 247 public final boolean equals(Object o) { 248 if (!(o instanceof BFSBase)) return false; 249 if (o == this) return true; 250 BFSBase f = (BFSBase)o; 251 return f.getPath().equals(getPath()) && specificEquals(f) && attributeEquals(f); 252 } 253 254 private final boolean attributeEquals(BFSBase base) { 255 initialize(); 256 base.initialize(); 257 return attrs.equals(base.attrs); 258 } 259 260 public final int hashCode() { 261 return getPath().hashCode(); 262 } 263 264 protected abstract boolean specificEquals(BFSBase f); 265 266 267 public void addFileChangeListener(FileChangeListener fcl) {} 268 public void removeFileChangeListener(FileChangeListener fcl) {} 269 @Deprecated 270 public boolean isReadOnly() { return true; } 271 @Deprecated 272 public void setImportant(boolean b) {} 273 274 public FileObject createData(String name, String ext) throws IOException { 275 throw new IOException (); } 277 public FileObject createFolder(String name) throws IOException { 278 throw new IOException (); } 280 public void delete(FileLock lock) throws IOException { 281 throw new IOException (); } 283 public OutputStream getOutputStream(FileLock lock) throws java.io.IOException { 284 throw new IOException (); } 286 public FileLock lock() throws IOException { 287 throw new IOException (); } 289 public void rename(FileLock lock, String name, String ext) throws IOException { 290 throw new IOException (); } 292 public void setAttribute(String attrName, Object value) throws IOException { 293 throw new IOException (); } 295 296 297 299 300 public FileSystem getFileSystem() { 301 return BinaryFS.this; 302 } 303 304 305 public FileObject getParent() { 306 return parent; 307 } 308 309 310 public boolean isRoot() { 311 return parent == null; 312 } 313 314 public boolean isValid() { 315 return true; 316 } 317 318 319 public java.util.Date lastModified() { 320 return lastModified; 321 } 322 323 324 public String getName () { 325 int i = name.lastIndexOf ('.'); 326 return i <= 0 ? name : name.substring (0, i); 327 } 328 329 330 public String getExt () { 331 int i = name.lastIndexOf ('.') + 1; 332 return i <= 1 || i == name.length () ? "" : name.substring (i); } 334 335 336 public String getNameExt () { 337 return name; 338 } 339 340 342 343 public Object getAttribute(String attrName) { 344 initialize(); 345 AttrImpl attr = attrs.get(attrName); 346 return (attr != null) ? attr.getValue(this, attrName) : null; 347 } 348 349 public FileObject getFileObjectForAttr() { 350 FileObject topFO = null; 351 try { 353 Class mfoClass = Class.forName("org.openide.filesystems.MultiFileObject"); Field field = mfoClass.getDeclaredField("attrAskedFileObject"); field.setAccessible(true); 356 357 ThreadLocal <?> attrAskedFileObject = ThreadLocal .class.cast(field.get(null)); 358 topFO = (FileObject)attrAskedFileObject.get(); 359 attrAskedFileObject.set(null); 360 } catch (Exception e) { 361 Exceptions.printStackTrace(e); 362 } 363 364 return topFO == null ? this : topFO; 365 } 366 367 368 public Enumeration <String > getAttributes() { 369 initialize(); 370 if (attrs == null) { 371 return Enumerations.empty(); 372 } 373 return Collections.enumeration(attrs.keySet()); 374 } 375 376 381 protected final void initialize() { 382 if (initialized) return; 383 384 try { 385 ByteBuffer sub = (ByteBuffer )content.duplicate().order(ByteOrder.LITTLE_ENDIAN).position(offset); 386 int attrCount = sub.getInt(); 387 if (attrCount > 0) attrs = new HashMap <String , AttrImpl>(attrCount*4/3+1); 388 389 for (int i=0; i< attrCount; i++) { String name = getString(sub).intern(); byte type = sub.get(); String value = getString(sub).intern(); attrs.put(name, new AttrImpl(type, value)); 398 } 399 400 doInitialize(sub); 401 } catch (Exception e) { 402 System.err.println("exception in initialize() on " + name + ": " + e); 403 } 404 initialized = true; 405 } 406 407 410 protected abstract void doInitialize(ByteBuffer sub) throws Exception ; 411 } 412 413 static final class AttrImpl { 414 private int index; 415 private String value; 416 417 AttrImpl(int type, String textValue) { 418 index = type; 419 value = textValue; 420 } 421 422 public boolean equals(Object o) { 423 if (o instanceof AttrImpl) { 424 AttrImpl impl = (AttrImpl)o; 425 return index == impl.index && value.equals(impl.value); 426 } 427 return false; 428 } 429 430 public int hashCode() { 431 return 2343 + index + value.hashCode(); 432 } 433 434 public Object getValue( BFSBase foProvider, String attrName) { 435 try { 436 switch(index) { 437 case 0: return Byte.valueOf(value); 439 case 1: return Short.valueOf(value); 441 case 2: return Integer.valueOf(value); 443 case 3: return Long.valueOf(value); 445 case 4: return Float.valueOf(value); 447 case 5: return Double.valueOf(value); 449 case 6: return Boolean.valueOf(value); 451 case 7: if (value.trim().length() != 1) break; 453 return new Character (value.charAt(0)); 454 case 8: return value; 456 case 9: return new URL (value); 458 case 10: return methodValue (value,foProvider,attrName); 460 case 11: Class <?> cls = findClass (value); 462 if (SharedClassObject.class.isAssignableFrom(cls)) { 464 return SharedClassObject.findObject(cls.asSubclass(SharedClassObject.class), true); 465 } else { 466 return cls.newInstance(); 467 } 468 469 case 12: return decodeValue(value); 471 default: 472 throw new IllegalStateException ("Bad index: " + index); } 474 } catch (Exception exc) { 475 Exceptions.attachLocalizedMessage(exc, "value = " + value); Logger.getLogger(BinaryFS.class.getName()).log(Level.WARNING, null, exc); 477 } 478 return null; } 480 481 482 private Object methodValue(String method, BFSBase foProvider, String attr) throws Exception { 483 String className,methodName; 484 int i = method.lastIndexOf('.'); 485 if (i != -1) { 486 methodName = value.substring(i+1); 487 className = value.substring(0,i); 488 Class cls = findClass (className); 489 490 Object objArray[][] = {null,null,null}; 491 Method methArray[] = {null,null,null}; 492 493 FileObject fo = null; 494 495 Method [] allMethods = cls.getDeclaredMethods(); 496 Class <?>[] paramClss; 497 498 for (int j=0; j < allMethods.length; j++) { 499 if (!allMethods[j].getName().equals(methodName)) continue; 500 501 paramClss = allMethods[j].getParameterTypes(); 502 if (paramClss.length == 0) { 503 if (methArray[0] == null) { 504 methArray[0] = allMethods[j]; 505 objArray[0] = new Object [] {}; 506 continue; 507 } 508 continue; 509 } 510 511 if (paramClss.length == 2 && methArray[2] == null) { 512 if (paramClss[0].isAssignableFrom(FileObject.class) && paramClss[1].isAssignableFrom(String .class)) { 513 methArray[2] = allMethods[j]; 514 if (fo == null) fo = foProvider.getFileObjectForAttr(); 515 objArray[2] = new Object [] {fo,attr}; 516 break; 517 } 518 if (paramClss[0].isAssignableFrom(Map .class) && paramClss[1].isAssignableFrom(String .class)) { 519 methArray[2] = allMethods[j]; 520 if (fo == null) fo = foProvider.getFileObjectForAttr(); 521 objArray[2] = new Object []{wrapToMap(fo),attr}; 522 } 523 524 if (paramClss[0].isAssignableFrom(String .class) && paramClss[1].isAssignableFrom(FileObject.class)) { 525 methArray[2] = allMethods[j]; 526 if (fo == null) fo = foProvider.getFileObjectForAttr(); 527 objArray[2] = new Object [] {attr,fo}; 528 break; 529 } 530 continue; 531 } 532 533 if (paramClss.length == 1 && methArray[1] == null) { 534 if (paramClss[0].isAssignableFrom(FileObject.class)) { 535 methArray[1] = allMethods[j]; 536 if (fo == null) fo = foProvider.getFileObjectForAttr(); 537 objArray[1] = new Object [] {fo}; 538 continue; 539 } 540 if (paramClss[0].isAssignableFrom(Map .class)) { 541 methArray[2] = allMethods[j]; 542 if (fo == null) fo = foProvider.getFileObjectForAttr(); 543 objArray[2] = new Object []{wrapToMap(fo)}; 544 } 545 546 if (paramClss[0].isAssignableFrom(String .class)) { 547 methArray[1] = allMethods[j]; 548 objArray[1] = new Object [] {attr}; 549 continue; 550 } 551 continue; 552 } 553 } 554 555 for (int k = 2; k >= 0; k-- ) { if (methArray[k] != null) { 557 methArray[k].setAccessible(true); return methArray[k].invoke(null,objArray[k]); 560 } 561 } 562 } 563 throw new InstantiationException (value); 565 } 566 567 private Object decodeValue(String value) throws Exception { 568 if ((value == null) ||(value.length() == 0)) return null; 569 570 byte[] bytes = new byte[value.length()/2]; 571 int count = 0; 572 for (int i = 0; i < value.length(); i += 2) { 573 int tempI = Integer.parseInt(value.substring(i,i+2),16); 575 if (tempI > 127) tempI -=256; bytes[count++] = (byte) tempI; 577 } 578 579 ByteArrayInputStream bis = new ByteArrayInputStream (bytes, 0, count); 580 return new NbObjectInputStream(bis).readObject(); 581 } 582 583 588 private Class findClass (String name) throws ClassNotFoundException { 589 ClassLoader c = Lookup.getDefault().lookup(ClassLoader .class); 590 String tname = org.openide.util.Utilities.translate (name); 591 try { 592 if (c == null) { 593 return Class.forName (tname); 594 } else { 595 return Class.forName (tname, true, c); 596 } 597 } catch (NoClassDefFoundError err) { 598 throw new ClassNotFoundException ("Cannot read " + name, err); 599 } 600 } 601 } 602 603 604 610 final class BFSFile extends BFSBase { 611 private int len; 612 private int contentOffset; 613 private String uri; 614 private long lastModified = -1; 615 616 public BFSFile(String name, FileObject parent, int offset) { 617 super(name, parent, offset); 618 } 619 620 621 622 public FileObject[] getChildren() { 623 return NO_CHILDREN; 624 } 625 626 627 public FileObject getFileObject(String name, String ext) { 628 return null; } 630 631 632 public boolean isData() { 633 return true; 634 } 635 636 637 public boolean isFolder() { 638 return false; 639 } 640 641 private byte[] data() throws IOException { 642 if (len == -1) throw new IllegalArgumentException (); 643 byte[] data = new byte[len]; 644 ((ByteBuffer )content.duplicate().position(contentOffset)).get(data); 645 return data; 646 } 647 648 649 public InputStream getInputStream() throws java.io.FileNotFoundException { 650 initialize(); 651 try { 652 return len == -1 ? new URL (uri).openConnection().getInputStream() : new ByteArrayInputStream (data()); 655 } catch (Exception e) { 656 throw (FileNotFoundException ) new FileNotFoundException (e.toString()).initCause(e); 657 } 658 } 659 660 661 public long getSize() { 662 initialize(); 663 try { 664 return len == -1 ? new URL (uri).openConnection().getContentLength() : len; } catch (Exception e) { 668 System.err.println("exception in getSize() on " + name + ": " + e); 669 return 0; 670 } 671 } 672 673 680 protected void doInitialize(ByteBuffer sub) throws Exception { 681 len = sub.getInt();; 682 contentOffset = sub.position(); 683 if (len == -1) { 684 uri = getString(sub); 685 } else { 686 sub.position(contentOffset+len); 687 } 688 int base = sub.getInt (); 689 lastModified = -10 - base; 690 } 691 692 694 protected boolean specificEquals(BFSBase _f) { 695 if (!(_f instanceof BFSFile)) return false; 696 BFSFile f = (BFSFile)_f; 697 initialize(); 698 f.initialize(); 699 if (len == -1 && f.len == -1) { 700 return uri.equals(f.uri); 701 } else if (len != -1 && f.len != -1) { 702 byte[] data, fdata; 703 try { 704 data = data(); 705 fdata = f.data(); 706 } catch (IOException ioe) { 707 return false; 708 } 709 if (data.length != fdata.length) return false; 710 for (int i = 0; i < data.length; i++) { 711 if (data[i] != fdata[i]) return false; 712 } 713 return true; 714 } else { 715 return false; 716 } 717 } 718 719 public Date lastModified() { 720 initialize(); 721 synchronized (modifications) { 722 if (lastModified >= 0) { 723 return new Date (lastModified); 724 } 725 try { 726 int index = -1; 727 java.net.URLConnection conn = null; 728 if (len == -1) { 729 conn = new URL (uri).openConnection (); 730 } else { 731 index = ((int)-lastModified) - 10; 732 Union2<String ,Long > obj = modifications.get(index); 733 if (obj.hasSecond()) { 734 return new Date (obj.second()); 735 } 736 conn = new URL (obj.first()).openConnection(); 737 } 738 739 if (conn instanceof java.net.JarURLConnection ) { 740 conn = ((java.net.JarURLConnection )conn).getJarFileURL ().openConnection (); 741 } 742 743 if (conn != null) { 744 long date = conn.getLastModified (); 745 if (date > 0) { 746 747 lastModified = date; 748 if (index >= 0) { 749 modifications.set(index, Union2.<String ,Long >createSecond(date)); 750 } 751 return new Date (date); 752 } 753 } 754 } catch (Exception e) { 755 Logger.getLogger(BinaryFS.class.getName()).log(Level.WARNING, null, e); 756 } 757 } 758 return super.lastModified (); 759 } 760 } 761 762 private class BFSFolder extends BFSBase { 763 Map <String ,BFSBase> childrenMap = Collections.<String ,BFSBase>emptyMap(); 764 765 public BFSFolder(String name, FileObject parent, int offset) { 766 super(name, parent, offset); 767 } 768 769 770 public boolean isData() { 771 return false; 772 } 773 774 775 public boolean isFolder() { 776 return true; 777 } 778 779 780 public long getSize() { 781 return 0; } 783 784 785 public InputStream getInputStream() throws java.io.FileNotFoundException { 786 throw new FileNotFoundException ("Is a directory: " + this); 787 } 788 789 790 791 public FileObject[] getChildren() { 792 initialize(); 793 return childrenMap.values().toArray(NO_CHILDREN); 794 } 795 796 797 public FileObject getFileObject(String name, String ext) { 798 initialize(); 799 String fullName = ext == null ? name : name + "." + ext; return childrenMap.get(fullName); 801 } 802 803 811 protected void doInitialize(ByteBuffer sub) throws Exception { 812 int files = sub.getInt(); 813 if (files > 0) { 814 childrenMap = new HashMap <String ,BFSBase>(files*4/3+1); 815 for (int i=0; i<files; i++) { String name = getString(sub); byte isFolder = sub.get(); int off = sub.getInt(); childrenMap.put(name, isFolder == 0 ? 820 new BFSFile(name, this, off) : 821 new BFSFolder(name, this, off)); 822 } 823 } 824 } 825 826 828 protected boolean specificEquals(BFSBase _f) { 829 if (!(_f instanceof BFSFolder)) return false; 830 BFSFolder f = (BFSFolder)_f; 831 initialize(); 832 return childrenMap.equals(f.childrenMap); 833 } 834 835 } 836 837 static final Map wrapToMap(FileObject fo) { 838 return fo == null ? Collections.emptyMap() : new FileMap(fo); 839 } 840 841 842 private static final class FileMap extends AbstractMap <String ,Object > { 843 private FileObject fo; 844 845 private FileMap(FileObject fo) { 846 this.fo = fo; 847 } 848 849 public Set <Map.Entry <String ,Object >> entrySet() { 850 return new AttrFileSet(fo); 851 } 852 853 public Object get(String key) { 854 return fo.getAttribute(key); 855 } 856 857 public Object remove(Object key) { 858 throw new UnsupportedOperationException (); 859 } 860 861 public Object put(String key, Object value) { 862 throw new UnsupportedOperationException (); 863 } 864 865 } 866 private static final class AttrFileSet extends AbstractSet <Map.Entry <String ,Object >> { 867 private FileObject fo; 868 869 private AttrFileSet(FileObject fo) { 870 this.fo = fo; 871 } 872 873 public Iterator <Map.Entry <String , Object >> iterator() { 874 class Iter implements Iterator <Map.Entry <String , Object >> { 875 Enumeration <String > attrs = fo.getAttributes(); 876 877 public boolean hasNext() { 878 return attrs.hasMoreElements(); 879 } 880 881 public Map.Entry <String , Object > next() { 882 String s = attrs.nextElement(); 883 return new FOEntry(fo, s); 884 } 885 886 public void remove() { 887 throw new UnsupportedOperationException (); 888 } 889 } 890 return new Iter(); 891 } 892 893 public int size() { 894 Enumeration <String > all = fo.getAttributes(); 895 int cnt = 0; 896 while (all.hasMoreElements()) { 897 cnt++; 898 all.nextElement(); 899 } 900 return cnt; 901 } 902 903 public boolean remove(Object o) { 904 throw new UnsupportedOperationException (); 905 } 906 } 908 private static final class FOEntry implements Map.Entry <String , Object > { 909 private FileObject fo; 910 private String attr; 911 912 private FOEntry(FileObject fo, String attr) { 913 this.fo = fo; 914 this.attr = attr; 915 } 916 917 public String getKey() { 918 return attr; 919 } 920 921 public Object getValue() { 922 return fo.getAttribute(attr); 923 } 924 925 public Object setValue(Object value) { 926 throw new UnsupportedOperationException (); 927 } 928 } } 930 | Popular Tags |