1 19 20 package org.openide.loaders; 21 22 23 import java.io.*; 24 import java.lang.ref.*; 25 import java.lang.reflect.*; 26 import java.util.*; 27 import java.util.logging.*; 28 import org.openide.ServiceType; 29 import org.openide.actions.DeleteAction; 30 import org.openide.cookies.*; 31 import org.openide.filesystems.*; 32 import org.openide.modules.ModuleInfo; 33 import org.openide.nodes.*; 34 import org.openide.util.*; 35 import org.openide.util.actions.SystemAction; 36 import org.openide.util.lookup.*; 37 38 66 public class InstanceDataObject extends MultiDataObject implements InstanceCookie.Of { 67 68 static final long serialVersionUID = -6134784731744777123L; 69 70 private static final String EA_INSTANCE_CLASS = "instanceClass"; private static final String EA_INSTANCE_CREATE = "instanceCreate"; private static final String EA_INSTANCE_OF = "instanceOf"; 75 static final String EA_NAME = "name"; 77 private static final int SAVE_DELAY = 2000; 78 79 82 private static final char OPEN = '['; 83 84 private static final char CLOSE = ']'; 85 86 87 public static final String INSTANCE = "instance"; 89 90 static final String SER_EXT = "ser"; 92 static final String XML_EXT = "settings"; 94 95 private static final String ICON_NAME = "icon"; 97 98 private Ser ser; 99 100 101 private boolean savingCanceled = false; 102 private String nameCache; 103 104 105 private static final RequestProcessor PROCESSOR = new RequestProcessor ("Instance processor"); private static final Logger err = Logger.getLogger("org.openide.loaders.InstanceDataObject"); 108 114 public InstanceDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException { 115 super (pf, loader); 116 117 if (pf.hasExt (SER_EXT)) { ser = new Ser (this); 121 getCookieSet ().add (ser); 122 } else if (!pf.hasExt(XML_EXT)) { 124 ser = new Ser (this); 126 } 127 128 try { 129 if (!pf.getFileSystem ().isDefault ()) { 130 getCookieSet ().add (new DefaultES (this, getPrimaryEntry (), getCookieSet ())); 131 } 132 } catch (FileStateInvalidException ex) { 133 } 135 } 136 137 138 private final Object IDO_LOCK = new Object (); 139 142 private Object getLock() { 143 return IDO_LOCK; 144 } 145 146 152 private static FileObject findFO (DataFolder folder, String name, String className) { 153 FileObject fo = folder.getPrimaryFile (); 154 String classNameEnc = className.replace ('.', '-'); 155 156 Enumeration en = fo.getChildren(false); 157 FileObject newFile; 158 while (en.hasMoreElements()) { 159 newFile = (FileObject) en.nextElement(); 160 if (!newFile.hasExt(INSTANCE)) continue; 161 if (name != null) { 162 if (!name.equals(getName(newFile))) continue; 163 } else { 164 if (!classNameEnc.equals(getName(newFile))) continue; 165 } 166 if (className.equals(InstanceDataObject.Ser.getClassName(newFile))) { 167 return newFile; 168 } 169 } 170 return null; 171 } 172 173 174 private static String getName(FileObject fo) { 175 String superName = (String ) fo.getAttribute(EA_NAME); 176 if (superName != null) return superName; 177 178 superName = fo.getName(); 179 int bracket = superName.indexOf (OPEN); 180 if (bracket == -1) { 181 return unescape(superName); 182 } else { 183 warnAboutBrackets(fo); 184 return unescape(superName.substring(0, bracket)); 185 } 186 } 187 188 194 public static InstanceDataObject find (DataFolder folder, String name, String className) { 195 FileObject newFile = findFO (folder, name, className); 196 if (newFile != null) { 197 try { 198 return (InstanceDataObject)DataObject.find (newFile); 199 } catch (DataObjectNotFoundException e) { 200 } 201 } 202 return null; 203 } 204 205 211 public static InstanceDataObject find(DataFolder folder, String name, Class <?> clazz) { 212 return find (folder, name, clazz.getName ()); 213 } 214 215 227 public static InstanceDataObject create (DataFolder folder, final String name, final String className) throws IOException { 228 final FileObject fo = folder.getPrimaryFile (); 229 if (name != null && name.length() == 0) { 230 throw new IOException("name cannot be empty"); } 232 FileObject newFile = findFO (folder, name, className); 233 if (newFile == null) { 234 final FileObject[] fos = new FileObject[1]; 235 236 DataObjectPool.getPOOL().runAtomicAction (fo, new FileSystem.AtomicAction() { 237 public void run () throws IOException { 238 String fileName; 239 if (name == null) { 240 fileName = FileUtil.findFreeFileName( 241 fo, className.replace ('.', '-'), INSTANCE); 242 } else { 243 fileName = escape(name); 244 } 245 fos[0] = fo.createData (fileName, INSTANCE); 246 fos[0].setAttribute(EA_INSTANCE_CLASS, className); 247 } 248 }); 249 newFile = fos[0]; 250 } 251 return (InstanceDataObject)DataObject.find (newFile); 252 } 253 254 265 public static InstanceDataObject create(DataFolder folder, String name, Class <?> clazz) throws IOException { 266 return create (folder, name, clazz.getName ()); 267 } 268 269 283 public static InstanceDataObject create (DataFolder folder, String name, 284 Object instance, ModuleInfo info) throws IOException { 285 return create(folder, name, instance, info, false); 286 } 287 288 304 public static InstanceDataObject create (DataFolder folder, String name, 305 Object instance, ModuleInfo info, boolean create) throws IOException { 306 if (name != null && name.length() == 0) { 307 throw new IOException("name cannot be empty"); } 309 return Creator.createInstanceDataObject (folder, name, instance, info, create); 310 311 } 312 313 private static InstanceDataObject storeSettings (DataFolder df, String name, Object obj, ModuleInfo mi) 314 throws IOException { 315 FileObject fo = df.getPrimaryFile (); 316 FileObject newFile = fo.getFileObject (name, XML_EXT); 317 String fullname = fo.getPath() + '/' + name + '.' + XML_EXT; 318 InstanceDataObject ido; 319 boolean attachWithSave = false; 320 try { 321 322 if (newFile == null) { 323 System.setProperty("InstanceDataObject.current.file", fo.getPath() + "/" + name + "." + XML_EXT); final ByteArrayOutputStream buf = storeThroughConvertor(obj, new FileObjectContext(fo, name)); 325 System.setProperty("InstanceDataObject.current.file", ""); createdIDOs.add(fullname); 327 newFile = fo.createData (name, XML_EXT); 328 FileLock flock = null; 329 try { 330 flock = newFile.lock(); 331 OutputStream os = newFile.getOutputStream(flock); 332 os.write(buf.toByteArray()); 333 os.close(); 334 } finally { 335 if (flock != null) flock.releaseLock(); 336 } 337 } else attachWithSave = true; 338 339 ido = (InstanceDataObject)DataObject.find (newFile); 340 ido.attachToConvertor(obj, attachWithSave); 342 } finally { 343 createdIDOs.remove(fullname); 344 } 345 return ido; 346 } 347 348 358 public static boolean remove (DataFolder folder, String name, 359 String className) { 360 FileLock lock = null; 361 try { 362 FileObject fileToRemove = findFO (folder, name, className); 363 if (fileToRemove == null) return false; 365 lock = fileToRemove.lock(); 366 fileToRemove.delete(lock); 367 } catch (IOException exc) { 368 return false; 370 } finally { 371 if (lock != null) 372 lock.releaseLock(); 373 } 374 return true; 375 } 376 377 387 public static boolean remove(DataFolder folder, String name, Class <?> clazz) { 388 return remove (folder, name, clazz.getName ()); 389 } 390 391 394 public HelpCtx getHelpCtx () { 395 HelpCtx test = InstanceSupport.findHelp (this); 396 if (test != null) 397 return test; 398 else 399 return HelpCtx.DEFAULT_HELP; 400 } 401 402 412 protected Node createNodeDelegate () { 413 if (getPrimaryFile().hasExt(XML_EXT)) { 414 un = new UpdatableNode(createNodeDelegateImpl()); 415 return un; 416 } else { 417 return createNodeDelegateImpl(); 418 } 419 } 420 421 private UpdatableNode un; 422 423 private final class UpdatableNode extends FilterNode { 424 public UpdatableNode(Node n) { 425 super(n); 426 } 427 public void update() { 428 Children.MUTEX.postWriteRequest(new Runnable () { 429 public void run() { 430 changeOriginal(createNodeDelegateImpl(), true); 431 } 432 }); 433 } 434 } 435 436 437 private Node createNodeDelegateImpl () { 438 try { 439 if (getPrimaryFile().getFileSystem() != Repository.getDefault().getDefaultFileSystem()) { 440 return new DataNode(this, Children.LEAF); 441 } 442 } catch (FileStateInvalidException ex) { 443 err.log(Level.WARNING, null, ex); 444 return new DataNode(this, Children.LEAF); 445 } 446 447 if (getPrimaryFile().hasExt(XML_EXT)) { 448 if (null == getCookieFromEP(InstanceCookie.class)) { 451 return new CookieAdjustingFilter(new UnrecognizedSettingNode()); 452 } 453 Node n = (Node) getCookieFromEP(Node.class); 454 if (n != null) return new CookieAdjustingFilter(n); 455 } 456 457 try { 459 if (instanceOf (Node.class)) { 460 Node n = (Node)instanceCreate (); 461 return new CookieAdjustingFilter(n); 462 } else if (instanceOf (Node.Handle.class)) { 463 Node.Handle h = (Node.Handle) instanceCreate (); 464 return new CookieAdjustingFilter(h.getNode()); 465 } 466 } catch (IOException ex) { 467 err.log(Level.WARNING, null, ex); 468 } catch (ClassNotFoundException ex) { 469 err.log(Level.WARNING, null, ex); 470 } 471 472 return new InstanceNode (this); 473 } 474 475 476 private final class UnrecognizedSettingNode extends AbstractNode { 477 public UnrecognizedSettingNode() { 478 super(Children.LEAF); 479 setName(NbBundle.getMessage(InstanceDataObject.class, "LBL_BrokenSettings")); setIconBaseWithExtension("org/openide/loaders/instanceBroken.gif"); setShortDescription(InstanceDataObject.this.getPrimaryFile().toString()); 482 } 483 484 public boolean canDestroy() { 485 return true; 486 } 487 public boolean canCut() { 488 return false; 489 } 490 public boolean canCopy() { 491 return false; 492 } 493 public boolean canRename() { 494 return false; 495 } 496 public void destroy() throws IOException { 497 InstanceDataObject.this.delete(); 498 } 499 protected SystemAction[] createActions() { 500 return new SystemAction[] {SystemAction.get(DeleteAction.class)}; 501 } 502 503 } 504 505 510 private final class CookieAdjustingFilter extends FilterNode { 511 public CookieAdjustingFilter(Node n) { 512 super(n, null, new ProxyLookup(new Lookup[] { 513 n.getLookup (), 514 Lookups.singleton(InstanceDataObject.this), 515 })); 516 } 517 518 public Node.Handle getHandle() { 521 return getOriginal().getHandle(); 522 } 523 public boolean equals(Object o) { 525 return this == o || getOriginal().equals(o) || (o != null && o.equals(getOriginal())); 526 } 527 public int hashCode() { 528 return getOriginal().hashCode(); 529 } 530 } 531 532 533 private <T> T getCookieFromEP(Class <T> clazz) { 534 return getCookiesLookup().lookup(clazz); 536 } 537 538 void notifyFileChanged(FileEvent fe) { 539 super.notifyFileChanged(fe); 540 if (getPrimaryFile().hasExt(XML_EXT)) { 541 if (!Creator.isFiredFromMe(fe)) { 542 getCookiesLookup(true); 543 } 544 } 545 } 546 547 548 @Override 549 public <T extends Node.Cookie> T getCookie(Class <T> clazz) { 550 T supe = null; 551 if (getPrimaryFile().hasExt(XML_EXT)) { 552 String filename = getPrimaryFile().getPath(); 555 if (createdIDOs.contains(filename)) return null; 556 557 Object res = getCookieFromEP(clazz); 558 supe = res instanceof Node.Cookie ? clazz.cast(res) : null; 559 if (InstanceCookie.class.isAssignableFrom(clazz)) return supe; 560 } 561 if (supe == null) supe = super.getCookie(clazz); 562 return supe; 563 } 564 565 private Lookup.Result cookieResult = null; 566 private Lookup.Result nodeResult = null; 567 private Lookup cookiesLkp = null; 568 private LookupListener cookiesLsnr = null; 569 private LookupListener nodeLsnr = null; 570 571 private Lookup getCookiesLookup() { 572 return getCookiesLookup(false); 573 } 574 575 private Lookup getCookiesLookup(boolean reinit) { 576 synchronized (getLock()) { 577 if (!reinit && cookiesLkp != null) { 578 return cookiesLkp; 579 } 580 } 581 Lookup envLkp = Environment.findForOne(InstanceDataObject.this); 582 583 boolean change = false; 584 synchronized (getLock()) { 585 if (cookiesLkp == null || envLkp == null || !envLkp.getClass().equals(cookiesLkp.getClass())) { 586 cookiesLkp = (envLkp == null) ? Lookup.EMPTY : envLkp; 587 change = true; 588 initCookieResult(); 589 initNodeResult(); 590 } 591 } 592 593 if (nodeResult != null) nodeResult.allItems(); 594 if (cookieResult != null) cookieResult.allItems(); 595 596 if (change) { 597 firePropertyChange(PROP_COOKIE, null, null); 598 } 599 600 return cookiesLkp; 601 } 602 603 private void initNodeResult() { 604 if (nodeResult != null && nodeLsnr != null) { 605 nodeResult.removeLookupListener(nodeLsnr); 606 } 607 608 if (cookiesLkp != null && !cookiesLkp.equals(Lookup.EMPTY)) { 609 nodeResult = cookiesLkp.lookupResult(InstanceCookie.class); 610 nodeLsnr = new LookupListener() { 611 public void resultChanged(LookupEvent lookupEvent) { 612 if (InstanceDataObject.this.un != null) { 613 un.update(); 614 } 615 } 616 }; 617 nodeResult.addLookupListener(nodeLsnr); 618 } 619 } 620 621 private void initCookieResult() { 622 if (cookieResult != null && cookiesLsnr != null) { 623 cookieResult.removeLookupListener(cookiesLsnr); 624 } 625 if (cookiesLkp != null && !cookiesLkp.equals(Lookup.EMPTY)) { 626 cookieResult = cookiesLkp.lookupResult(Node.Cookie.class); 627 cookiesLsnr = new LookupListener() { 628 public void resultChanged(LookupEvent lookupEvent) { 629 firePropertyChange(DataObject.PROP_COOKIE, null, null); 630 } 631 }; 632 cookieResult.addLookupListener(cookiesLsnr); 633 } 634 } 635 636 639 private InstanceCookie.Of delegateIC () { 640 InstanceCookie.Of ic = null; 642 if (getPrimaryFile().hasExt(XML_EXT)) { 643 ic = (InstanceCookie.Of) getCookieFromEP(InstanceCookie.Of.class); 644 } else { 645 ic = ser; 646 } 647 return ic; 648 } 649 650 655 public String instanceName () { 656 InstanceCookie delegateIC = delegateIC (); 657 if (delegateIC == null) return this.getName(); 658 return delegateIC.instanceName (); 659 } 660 661 669 public Class <?> instanceClass () 670 throws IOException, ClassNotFoundException { 671 InstanceCookie delegateIC = delegateIC (); 672 if (delegateIC == null) return this.getClass(); 673 return delegateIC.instanceClass (); 674 } 675 676 680 public boolean instanceOf (Class <?> type) { 681 InstanceCookie.Of delegateIC = delegateIC (); 682 if (delegateIC == null) return type.isAssignableFrom(this.getClass()); 683 return delegateIC.instanceOf (type); 684 } 685 686 691 public Object instanceCreate () 692 throws IOException, ClassNotFoundException { 693 InstanceCookie delegateIC = delegateIC (); 694 if (delegateIC == null) return this; 695 return delegateIC.instanceCreate (); 696 } 697 698 700 final boolean creatorOf (Object inst) { 701 InstanceCookie delegateIC = delegateIC (); 702 if (delegateIC instanceof Ser) { 703 return ((Ser)delegateIC).creatorOf (inst); 704 } 705 return false; 706 } 707 708 709 public String getName () { 710 if (nameCache != null) { 711 return nameCache; 712 } 713 714 String superName = (String ) getPrimaryFile().getAttribute(EA_NAME); 715 if (superName == null) { 716 superName = super.getName(); 717 int bracket = superName.indexOf (OPEN); 718 if (bracket == -1) { 719 superName = unescape(superName); 720 } else { 721 warnAboutBrackets(getPrimaryFile()); 722 superName = unescape(superName.substring(0, bracket)); 723 } 724 } 725 726 this.nameCache = superName; 727 728 return superName; 729 } 730 731 private static final Set<FileObject> warnedAboutBrackets = new WeakSet<FileObject>(); 732 736 private static void warnAboutBrackets(FileObject fo) { 737 if (warnedAboutBrackets.add(fo)) { 738 err.warning("Use of [] in " + fo + " is deprecated."); err.warning("(Please use the string-valued file attribute instanceClass instead.)"); } 741 } 742 743 746 protected FileObject handleRename (String name) throws IOException { 747 FileObject fo = getPrimaryFile(); 748 fo.setAttribute(EA_NAME, name); 749 return fo; 750 } 751 752 754 757 760 static String escape (String text) { 761 boolean spacenasty = text.startsWith(" ") || text.endsWith(" ") || text.indexOf(" ") != -1; int len = text.length (); 763 StringBuffer escaped = new StringBuffer (len); 764 for (int i = 0; i < len; i++) { 765 char c = text.charAt (i); 766 if (c == '/' || c == ':' || c == '\\' || c == OPEN || c == CLOSE || c == '<' || c == '>' || 768 c == '?' || c == '*' || c == '|' || 770 (c == ' ' && spacenasty) || 771 c == '.' || c == '"' || c < '\u0020' || c > '\u007E' || c == '#') { 772 escaped.append ('#'); 774 String hex = Integer.toString(c, 16).toUpperCase(Locale.ENGLISH); 775 if (hex.length () < 4) escaped.append ('0'); 776 if (hex.length () < 3) escaped.append ('0'); 777 if (hex.length () < 2) escaped.append ('0'); 778 escaped.append (hex); 779 } else { 780 escaped.append (c); 781 } 782 } 783 return escaped.toString (); 784 } 785 786 787 static String unescape (String text) { 788 int len = text.length (); 789 StringBuffer unesc = new StringBuffer (len); 790 for (int i = 0; i < len; i++) { 791 char c = text.charAt (i); 792 if (c == '#') { 793 if (i + 4 >= len) { 794 err.warning("trailing garbage in instance name: " + text); break; 796 } 797 try { 798 char[] hex = new char[4]; 799 text.getChars (i + 1, i + 5, hex, 0); 800 unesc.append ((char) Integer.parseInt (new String (hex), 16)); 801 } catch (NumberFormatException nfe) { 802 err.log(Level.WARNING, null, nfe); 803 } 804 i += 4; 805 } else { 806 unesc.append (c); 807 } 808 } 809 return unesc.toString (); 810 } 811 812 private final static int MAX_FILENAME_LENGTH = 50; 815 816 821 static String escapeAndCut (String name) { 822 int maxLen = MAX_FILENAME_LENGTH; 823 824 String ename = escape(name); 825 if (ename.length() <= maxLen) return ename; 826 String hash = Integer.toHexString(ename.hashCode()).toUpperCase(Locale.ENGLISH); 827 maxLen = (maxLen > hash.length()) ? (maxLen-hash.length()) / 2 :1; 828 String start = ename.substring(0, maxLen); 829 if (start.endsWith("#")) { 830 start = start.substring(0, start.length() - 1); 833 } 834 String end = ename.substring(ename.length() - maxLen); 835 836 return start + hash + end; 837 } 838 839 840 final void scheduleSave () { 841 if (isSavingCanceled() || !getPrimaryFile().hasExt(SER_EXT)) return; 843 doFileLock(); 844 ser.getSaveTask().schedule(SAVE_DELAY); 845 } 846 847 private FileLock fileLock; 848 849 850 private FileLock doFileLock() { 851 synchronized (getLock()) { 852 if (fileLock != null) return fileLock; 853 try { 854 fileLock = getPrimaryFile().lock(); 855 } catch (IOException ex) { 856 err.log(Level.WARNING, getPrimaryFile().toString()); 857 err.log(Level.WARNING, null, ex); 858 } 859 return fileLock; 860 } 861 } 862 863 864 private void relaseFileLock() { 865 synchronized (getLock()) { 866 if (fileLock == null) return; 867 fileLock.releaseLock(); 868 fileLock = null; 869 } 870 } 871 874 protected DataObject handleCreateFromTemplate ( 875 DataFolder df, String name 876 ) throws IOException { 877 try { 878 if (getPrimaryFile().hasExt(XML_EXT)) { 879 InstanceCookie ic = (InstanceCookie)this.getCookie(InstanceCookie.class); 880 Object obj = ic.instanceCreate(); 881 882 DataObject d = createSettingsFile(df, name, obj); 883 attachToConvertor(null); 885 return d; 886 } else if ( (!getPrimaryFile().hasExt(INSTANCE)) && 887 Serializable.class.isAssignableFrom( instanceClass()) ) { 888 InstanceCookie ic = (InstanceCookie)this.getCookie(InstanceCookie.class); 889 Object obj = ic.instanceCreate(); 890 891 return DataObject.find( createSerFile( df, name, obj ) ); 892 } 893 } catch (ClassNotFoundException ex) { 894 err.log(Level.WARNING, null, ex); 895 } 896 897 return super.handleCreateFromTemplate(df, name); 898 } 899 900 906 protected DataObject handleCopy(DataFolder df) throws IOException { 907 if (getPrimaryFile ().getFileSystem().isDefault()) { 908 try { 909 if (getPrimaryFile ().hasExt(XML_EXT)) { 910 InstanceCookie ic = (InstanceCookie)getCookie(InstanceCookie.class); 911 if (ic != null) { 912 Object obj = ic.instanceCreate(); 913 InstanceDataObject ido = createSettingsFile( 914 df, getNodeDelegate().getDisplayName(), obj); 915 ido.attachToConvertor(null); 916 return ido; 917 } 918 } else if ( (!getPrimaryFile().hasExt(INSTANCE)) && 919 Serializable.class.isAssignableFrom(instanceClass()) ) { 920 InstanceCookie ic = (InstanceCookie)getCookie(InstanceCookie.class); 921 if (ic != null) { 922 Object obj = ic.instanceCreate(); 923 return DataObject.find(createSerFile( 924 df, getNodeDelegate().getDisplayName(), obj)); 925 } 926 } 927 } catch (ClassNotFoundException ex) { 928 err.log(Level.WARNING, null, ex); 929 } 930 } 931 return super.handleCopy(df); 932 } 933 934 935 private boolean isSavingCanceled() { 936 return savingCanceled; 937 } 938 939 protected void dispose() { 940 if (getPrimaryFile().hasExt(SER_EXT)) { 941 savingCanceled = true; 942 if (ser != null) { 943 RequestProcessor.Task task = ser.getSaveTask(); 944 if (task.getDelay() > 0 || ser.isSaving() && !task.isFinished()) { 945 task.waitFinished(); 946 } 947 } 948 relaseFileLock(); 949 } else if (getPrimaryFile().hasExt(XML_EXT)) { 950 SaveCookie s = (SaveCookie) getCookie(SaveCookie.class); 951 try { 952 if (s != null) s.save(); 953 } catch (IOException ex) { 954 } 956 } 957 super.dispose(); 958 } 959 960 protected void handleDelete() throws IOException { 961 savingCanceled = true; 962 if (getPrimaryFile().hasExt(XML_EXT)) { 963 handleDeleteSettings(); 964 return; 965 } 966 if (ser != null) { 967 RequestProcessor.Task task = ser.getSaveTask(); 968 task.cancel(); 969 if (ser.isSaving() && !task.isFinished()) task.waitFinished(); 970 } 971 relaseFileLock(); 972 super.handleDelete(); 973 } 974 975 private void handleDeleteSettings() throws IOException { 976 SaveCookie s = (SaveCookie) getCookie(SaveCookie.class); 977 try { 978 if (s != null) s.save(); 979 } catch (IOException ex) { 980 } 982 super.handleDelete(); 983 } 984 985 private InstanceDataObject createSettingsFile (DataFolder df, String name, Object obj) 986 throws IOException { 987 boolean isServiceType = false; 988 989 String filename; 990 if (obj instanceof ServiceType) { 992 isServiceType = true; 993 ServiceType sr = (ServiceType) obj; 994 name = name == null? sr.getName(): name; 995 String stName = name; 996 ServiceType.Registry r = (ServiceType.Registry)Lookup.getDefault().lookup(ServiceType.Registry.class); 997 for (int i = 1; r.find(stName) != null; i++) { 998 stName = new StringBuffer (name.length() + 2). 999 append(name).append('_').append(i).toString(); 1000 } 1001 if (!stName.equals(sr.getName())) { 1002 sr = sr.createClone(); 1004 obj = sr; 1005 sr.setName(stName); 1006 } 1007 filename = escapeAndCut(stName); 1008 } else { 1009 filename = (name == null)? getPrimaryFile ().getName (): escapeAndCut(name); 1010 } 1011 1012 filename = FileUtil.findFreeFileName( 1013 df.getPrimaryFile (), filename, getPrimaryFile ().getExt () 1014 ); 1015 1016 InstanceDataObject newFile = storeSettings(df, filename, obj, null); 1017 if (name != null && !isServiceType) { 1018 newFile.getPrimaryFile().setAttribute(EA_NAME, name); 1019 } 1020 return newFile; 1021 } 1022 1023 private FileObject createSerFile( 1024 DataFolder df, String name, Object obj 1025 ) throws IOException { 1026 FileLock lock = null; 1027 OutputStream ostream = null; 1028 FileObject newFile = null; 1029 try { 1030 FileObject fo = df.getPrimaryFile (); 1031 1032 if (name == null) { 1033 name = FileUtil.findFreeFileName( 1034 df.getPrimaryFile (), getPrimaryFile ().getName (), getPrimaryFile ().getExt () 1035 ); 1036 } 1037 1038 newFile = fo.getFileObject (name, SER_EXT); 1039 if (newFile == null) newFile = fo.createData (name, SER_EXT); 1040 1041 lock = newFile.lock (); 1042 ostream = newFile.getOutputStream(lock); 1043 1044 ObjectOutputStream p = new ObjectOutputStream(ostream); 1045 p.writeObject(obj); 1046 p.flush(); 1047 } finally { 1048 if (ostream != null) 1049 ostream.close(); 1050 if (lock != null) 1051 lock.releaseLock (); 1052 } 1053 return newFile; 1054 } 1055 1056 1058 private static final class Ser extends InstanceSupport 1059 implements Runnable { 1060 1061 private Reference<Object > bean = new SoftReference<Object >(null); 1062 1063 private long saveTime; 1064 1065 1066 private ClassLoader customClassLoader; 1067 private InstanceDataObject dobj; 1068 1069 1070 public Ser (InstanceDataObject dobj) { 1071 super (dobj.getPrimaryEntry()); 1072 customClassLoader = null; 1073 this.dobj = dobj; 1074 } 1075 1076 public String instanceName () { 1077 FileObject fo = entry ().getFile (); 1079 if (fo.lastModified ().getTime () <= saveTime) { 1080 Object o = bean.get (); 1081 if (o != null) { 1082 return o.getClass().getName(); 1083 } 1084 } 1085 1086 if (!fo.hasExt (INSTANCE)) { 1087 return super.instanceName (); 1088 } 1089 return getClassName(fo); 1090 } 1091 1092 1093 private static String getClassName(FileObject fo) { 1094 Object attr = fo.getAttribute (EA_INSTANCE_CLASS); 1096 if (attr instanceof String ) { 1097 return Utilities.translate((String ) attr); 1098 } else if (attr != null) { 1099 err.warning( 1100 "instanceClass was a " + attr.getClass().getName()); } 1102 1103 attr = fo.getAttribute (EA_INSTANCE_CREATE); 1104 if (attr != null) { 1105 return attr.getClass().getName(); 1106 } 1107 1108 String name = fo.getName (); 1110 1111 int first = name.indexOf (OPEN) + 1; 1112 if (first != 0) { 1113 warnAboutBrackets(fo); 1114 } 1115 1116 int last = name.indexOf (CLOSE); 1117 if (last < 0) { 1118 last = name.length (); 1119 } 1120 1121 if (first < last) { 1123 name = name.substring (first, last); 1124 } 1125 1126 name = name.replace ('-', '.'); 1127 name = Utilities.translate(name); 1128 1129 return name; 1131 } 1132 1133 1136 public Class instanceClass() throws IOException, ClassNotFoundException { 1137 return super.instanceClass (customClassLoader); 1138 } 1139 1140 1143 public boolean instanceOf (Class type) { 1144 FileObject fo = entry ().getFile (); 1146 if (fo.lastModified ().getTime () <= saveTime) { 1147 Object o = bean.get (); 1148 if (o != null) { 1149 return type.isInstance (o); 1150 } 1151 } 1152 1153 1155 1156 Boolean res = inListOfClasses (type, entry ().getFile ()); 1158 if (res == null) { 1159 return super.instanceOf (type); 1161 } 1162 return res.booleanValue (); 1163 } 1164 1165 1166 public Object instanceCreate () throws IOException, ClassNotFoundException { 1167 FileObject fo = entry ().getFile (); 1168 1169 1170 Object o; 1171 if (fo.lastModified ().getTime () <= saveTime) { 1172 o = bean.get (); 1173 } else { 1174 o = null; 1175 } 1176 1177 if (o != null) { 1178 return o; 1179 } 1180 1181 saveTime = fo.lastModified ().getTime (); 1182 if (saveTime < System.currentTimeMillis ()) { 1183 saveTime = System.currentTimeMillis (); 1184 } 1185 if (fo.hasExt (INSTANCE)) { 1186 o = fo.getAttribute (EA_INSTANCE_CREATE); 1188 } 1189 1190 if (o == null) { 1191 o = super.instanceCreate (); 1193 } 1194 1195 bean = new SoftReference<Object >(o); 1197 return o; 1198 } 1199 1200 1202 final boolean creatorOf (Object inst) { 1203 Reference r = bean; 1204 return r != null && r.get () == inst; 1205 } 1206 1207 1208 public void run () { 1209 try { 1210 saving = true; 1211 runImpl(); 1212 } finally { 1213 dobj.relaseFileLock(); 1214 saving = false; 1215 } 1216 } 1217 1218 1220 private void runImpl () { 1221 Object bean = this.bean.get (); 1222 if (bean == null) { 1223 return; 1225 } 1226 1227 try { 1228 FileLock lock = dobj.doFileLock(); 1229 if (lock == null) return; 1230 ObjectOutputStream oos = new ObjectOutputStream ( 1231 entry ().getFile ().getOutputStream (lock) 1232 ); 1233 try { 1234 oos.writeObject (bean); 1235 saveTime = entry ().getFile ().lastModified ().getTime (); 1237 } finally { 1238 oos.close (); 1239 } 1240 } catch (IOException ex) { 1241 err.log(Level.WARNING, NbBundle.getMessage ( 1242 InstanceDataObject.class, "EXC_CannotSaveBean", instanceName (), entry ().getFile ().getPath() 1244 ), ex); 1245 } 1246 1247 } 1248 1249 1254 private static Boolean inListOfClasses (Class type, FileObject fo) { 1255 Object obj = fo.getAttribute (EA_INSTANCE_OF); 1256 if (obj instanceof String ) { 1257 String typeName = type.getName (); 1258 StringTokenizer tok = new StringTokenizer ((String )obj, "\n\t ,;:"); while (tok.hasMoreTokens ()) { 1260 String t = tok.nextToken ().trim(); 1261 if (typeName.equals (t)) { 1262 return Boolean.TRUE; 1264 } 1265 } 1266 1267 return Boolean.FALSE; 1268 } else if (obj != null) { 1269 err.warning("instanceOf was a " + obj.getClass().getName()); } 1271 return null; 1273 } 1274 1275 final void setCustomClassLoader(ClassLoader cl) { 1276 this.customClassLoader = cl; 1277 } 1278 1279 1280 private RequestProcessor.Task task; 1281 1282 1283 public RequestProcessor.Task getSaveTask() { 1284 if (task == null) { 1285 task = PROCESSOR.create(this); 1286 } 1287 return task; 1288 } 1289 1290 1291 private boolean saving = false; 1292 1293 public boolean isSaving() { 1294 return saving; 1295 } 1296 1297 } 1299 final void setCustomClassLoader(ClassLoader cl) { 1300 if (ser instanceof Ser) 1301 ((Ser) ser).setCustomClassLoader(cl); 1302 } 1303 1304 1308 private static class Creator implements FileSystem.AtomicAction { 1309 private ModuleInfo mi = null; 1310 private DataFolder folder = null; 1311 private Object instance = null; 1312 private String name = null; 1313 private InstanceDataObject result = null; 1314 private boolean create; 1315 1316 private final static Creator me = new Creator (); 1317 1318 1319 private Creator() { 1320 } 1321 1322 public void run () throws IOException { 1323 FileObject fo = folder.getPrimaryFile (); 1324 String filename = name; 1325 if (filename == null) { 1326 filename = instance.getClass().getName().replace ('.', '-'); 1327 filename = FileUtil.findFreeFileName(fo, filename, XML_EXT); 1328 } else { 1329 String escapedFileName = escape(filename); 1330 FileObject newFile = fo.getFileObject (escapedFileName, XML_EXT); 1332 if (newFile == null) { 1333 filename = escapeAndCut(filename); 1334 } else { 1335 filename = escapedFileName; 1336 } 1337 1338 1339 if (create ) { 1340 filename = FileUtil.findFreeFileName(fo, filename, XML_EXT); 1341 } 1342 } 1343 1344 result = storeSettings(folder, filename, instance, mi); 1345 } 1346 1347 1348 public static InstanceDataObject createInstanceDataObject ( 1349 DataFolder folder, String name, Object instance, ModuleInfo mi, 1350 boolean create) throws IOException { 1351 synchronized (me) { 1352 me.mi = mi; 1353 me.folder = folder; 1354 me.instance = instance; 1355 me.name = name; 1356 me.create = create; 1357 1358 DataObjectPool.getPOOL().runAtomicActionSimple (folder.getPrimaryFile(), me); 1359 me.mi = null; 1360 me.folder = null; 1361 me.instance = null; 1362 me.name = null; 1363 InstanceDataObject result = me.result; 1364 me.result = null; 1365 return result; 1366 } 1367 } 1368 1369 public static boolean isFiredFromMe (FileEvent fe) { 1370 return fe.firedFrom(me); 1371 } 1372 } 1373 1374 1375 private static ByteArrayOutputStream storeThroughConvertor(Object inst, FileObjectContext ctx) throws IOException { 1376 FileObject fo = resolveConvertor(inst); 1377 Object convertor = fo.getAttribute("settings.convertor"); if (convertor == null) throw new IOException("missing attribute settings.convertor"); ByteArrayOutputStream b = new ByteArrayOutputStream(1024); 1380 Writer w = new OutputStreamWriter(b, "UTF-8"); convertorWriteMethod(convertor, new WriterProvider(w, ctx), inst); 1382 w.close(); 1383 return b; 1384 } 1385 1386 1387 private static void convertorWriteMethod(Object convertor, Writer w, Object inst) throws IOException { 1388 Throwable e = null; 1389 try { 1390 Method method = convertor.getClass().getMethod( 1391 "write", new Class [] {Writer.class, Object .class}); 1393 method.setAccessible(true); 1394 method.invoke(convertor, new Object [] {w, inst}); 1395 } catch (NoSuchMethodException ex) { 1396 e = ex; 1397 } catch (IllegalAccessException ex) { 1398 e = ex; 1399 } catch (InvocationTargetException ex) { 1400 e = ex.getTargetException(); 1401 if (e instanceof IOException) throw (IOException) e; 1402 } 1403 if (e != null) { 1404 throw (IOException)new IOException("Problem with Convertor.write method. "+e).initCause(e); } 1406 } 1407 1408 1409 private final static String EA_PROVIDER_PATH = "settings.providerPath"; private static final String EA_SUBCLASSES = "settings.subclasses"; 1412 1413 private static FileObject resolveConvertor(Object obj) throws IOException { 1414 String prefix = "xml/memory"; FileSystem sfs = Repository.getDefault().getDefaultFileSystem(); 1416 1417 FileObject memContext = sfs.findResource(prefix); 1418 if (memContext == null) throw new FileNotFoundException("SFS:xml/memory while converting a " + obj.getClass().getName()); 1420 Class clazz = obj.getClass(); 1421 Class c = clazz; 1422 while (c != null) { 1423 String className = c.getName(); 1424 String convertorPath = new StringBuffer (200).append(prefix).append('/'). 1425 append(className.replace('.', '/')).toString(); FileObject fo = sfs.findResource(convertorPath); 1427 if (fo != null) { 1428 String providerPath = (String ) fo.getAttribute(EA_PROVIDER_PATH); 1429 if (providerPath == null) { 1430 c = c.getSuperclass(); 1431 continue; 1432 } 1433 if (c.equals(clazz) || Object .class.equals(c)) { 1434 FileObject ret = sfs.findResource(providerPath); 1435 if (ret == null) { 1436 throw new FileNotFoundException("Invalid settings.providerPath under SFS/xml/memory/ for " + clazz); } else { 1438 return ret; 1439 } 1440 } else { 1441 Object inheritAttribute = fo.getAttribute(EA_SUBCLASSES); 1443 if (inheritAttribute instanceof Boolean ) { 1444 boolean subclasses = ((Boolean )inheritAttribute).booleanValue(); 1445 if (subclasses) { 1446 FileObject ret = sfs.findResource(providerPath); 1447 if (ret == null) { 1448 throw new FileNotFoundException("Invalid settings.providerPath under SFS/xml/memory/ for " + clazz); } else { 1450 return ret; 1451 } 1452 } 1453 } 1454 } 1455 } 1456 c = c.getSuperclass(); 1457 } 1458 throw new FileNotFoundException("None convertor was found under SFS/xml/memory/ for " + clazz); } 1460 1461 private void attachToConvertor(Object obj) throws IOException { 1462 attachToConvertor (obj, false); 1463 } 1464 1465 1466 private void attachToConvertor(Object obj, boolean save) throws IOException { 1467 Object ic = getCookiesLookup().lookup(InstanceCookie.class); 1470 if (ic == null) { 1471 throw new IllegalStateException ( 1472 "Trying to store object " + obj + " which most probably belongs to already disabled module!"); } 1475 convertorSetInstanceMethod(ic, obj, save); 1476 } 1477 1478 1479 private static void convertorSetInstanceMethod(Object convertor, Object inst, boolean save) throws IOException { 1480 Exception e = null; 1481 try { 1482 Method method = convertor.getClass().getMethod( 1483 "setInstance", new Class [] {Object .class, Boolean.TYPE}); 1485 method.setAccessible(true); 1486 method.invoke(convertor, new Object [] {inst, 1487 (save ? Boolean.TRUE : Boolean.FALSE)}); 1488 } catch (NoSuchMethodException ex) { 1489 e = ex; 1490 } catch (IllegalAccessException ex) { 1491 e = ex; 1492 } catch (InvocationTargetException ex) { 1493 e = ex; 1494 if (ex.getTargetException() instanceof IOException) { 1495 throw (IOException) ex.getTargetException(); 1496 } 1497 } 1498 if (e != null) { 1499 Exceptions.attachLocalizedMessage(e, 1500 "Problem with InstanceCookie.setInstance method: " + 1501 convertor.getClass()); err.log(Level.WARNING, null, e); 1503 } 1504 } 1505 1506 1507 private static final List<String > createdIDOs = 1508 Collections.synchronizedList(new ArrayList<String >(1)); 1509 1510 1511 void notifyAttributeChanged(org.openide.filesystems.FileAttributeEvent fae) { 1512 nameCache = null; 1513 super.notifyAttributeChanged(fae); 1514 } 1515 1516 1518 private static final class WriterProvider extends Writer implements Lookup.Provider { 1519 private final Writer orig; 1520 private final FileObjectContext ctx; 1521 private Lookup lookup; 1522 1523 public WriterProvider(Writer w, FileObjectContext ctx) { 1524 this.orig = w; 1525 this.ctx = ctx; 1526 } 1527 1528 public void close() throws IOException { 1529 orig.close(); 1530 } 1531 1532 public void flush() throws IOException { 1533 orig.flush(); 1534 } 1535 1536 public void write(char[] cbuf, int off, int len) throws IOException { 1537 orig.write(cbuf, off, len); 1538 } 1539 1540 public Lookup getLookup() { 1541 if (lookup == null) { 1542 lookup = Lookups.singleton(ctx); 1543 } 1544 return lookup; 1545 } 1546 1547 } 1548 1549 1553 private static final class FileObjectContext extends FileObject { 1554 private static final String UNSUPPORTED = "The Restricted FileObject" + " implementation allowing to get just read-only informations about" + " name and location. It should prevent any manipulation with file" + " or its content."; private final FileObject fo; 1559 private final FileObject parent; 1560 private final String name; 1561 1562 public FileObjectContext(FileObject parent, String name) { 1563 this.parent = parent; 1564 this.name = name; 1565 this.fo = parent.getFileObject(name, XML_EXT); 1566 } 1567 1568 public void addFileChangeListener(FileChangeListener fcl) { 1569 throw new UnsupportedOperationException (UNSUPPORTED); 1570 } 1571 1572 public FileObject createData(String name, String ext) throws IOException { 1573 throw new UnsupportedOperationException (UNSUPPORTED); 1574 } 1575 1576 public FileObject createFolder(String name) throws IOException { 1577 throw new UnsupportedOperationException (UNSUPPORTED); 1578 } 1579 1580 public void delete(FileLock lock) throws IOException { 1581 throw new UnsupportedOperationException (UNSUPPORTED); 1582 } 1583 1584 public Object getAttribute(String attrName) { 1585 return fo == null? null: fo.getAttribute(attrName); 1586 } 1587 1588 public Enumeration<String > getAttributes() { 1589 return fo == null? Enumerations.<String >empty(): fo.getAttributes(); 1590 } 1591 1592 public FileObject[] getChildren() { 1593 return new FileObject[0]; 1594 } 1595 1596 public String getExt() { 1597 return InstanceDataObject.XML_EXT; } 1599 1600 public FileObject getFileObject(String name, String ext) { 1601 return null; 1602 } 1603 1604 public FileSystem getFileSystem() throws FileStateInvalidException { 1605 return parent.getFileSystem(); 1606 } 1607 1608 public InputStream getInputStream() throws FileNotFoundException { 1609 throw new UnsupportedOperationException (UNSUPPORTED); 1610 } 1611 1612 public String getName() { 1613 return name; 1614 } 1615 1616 public OutputStream getOutputStream(FileLock lock) throws IOException { 1617 throw new UnsupportedOperationException (UNSUPPORTED); 1618 } 1619 1620 public FileObject getParent() { 1621 return parent; 1622 } 1623 1624 public long getSize() { 1625 return fo == null? 0: fo.getSize(); 1626 } 1627 1628 public boolean isData() { 1629 return true; 1630 } 1631 1632 public boolean isFolder() { 1633 return false; 1634 } 1635 1636 public boolean isReadOnly() { 1637 return parent.isReadOnly(); 1638 } 1639 1640 public boolean isRoot() { 1641 return false; 1642 } 1643 1644 public boolean isValid() { 1645 return fo == null? false: fo.isValid(); 1646 } 1647 1648 public Date lastModified() { 1649 return fo == null? parent.lastModified(): fo.lastModified(); 1650 } 1651 1652 public FileLock lock() throws IOException { 1653 throw new UnsupportedOperationException (UNSUPPORTED); 1654 } 1655 1656 public void removeFileChangeListener(FileChangeListener fcl) { 1657 throw new UnsupportedOperationException (UNSUPPORTED); 1658 } 1659 1660 public void rename(FileLock lock, String name, String ext) throws IOException { 1661 throw new UnsupportedOperationException (UNSUPPORTED); 1662 } 1663 1664 public void setAttribute(String attrName, Object value) throws IOException { 1665 throw new UnsupportedOperationException (UNSUPPORTED); 1666 } 1667 1668 public void setImportant(boolean b) { 1669 throw new UnsupportedOperationException (UNSUPPORTED); 1670 } 1671 1672 } 1673 1674} 1675 | Popular Tags |