1 19 20 package org.openide.loaders; 21 22 23 import java.beans.*; 24 import java.io.*; 25 import java.text.DateFormat ; 26 import java.util.*; 27 import java.util.logging.Level ; 28 import java.util.logging.Logger ; 29 import javax.swing.event.*; 30 import org.openide.filesystems.*; 31 import org.openide.nodes.*; 32 import org.openide.util.*; 33 34 40 public abstract class DataObject extends Object 41 implements Node.Cookie, Serializable, HelpCtx.Provider, Lookup.Provider { 42 43 private static final long serialVersionUID = 3328227388376142699L; 44 45 46 public static final String PROP_TEMPLATE = "template"; 48 49 public static final String PROP_NAME = "name"; 51 52 public static final String PROP_HELP = "helpCtx"; 54 55 public static final String PROP_MODIFIED = "modified"; 57 58 public static final String PROP_COOKIE = Node.PROP_COOKIE; 59 60 61 public static final String PROP_VALID = "valid"; 63 64 public static final String PROP_PRIMARY_FILE = "primaryFile"; 66 public static final String PROP_FILES = "files"; 68 72 static final String EA_ASSIGNED_LOADER = "NetBeansAttrAssignedLoader"; 78 static final String EA_ASSIGNED_LOADER_MODULE = "NetBeansAttrAssignedLoaderModule"; 80 82 private static ModifiedRegistry modified = new ModifiedRegistry(); 83 84 private static Set<DataObject> syncModified = Collections.synchronizedSet(modified); 85 86 87 private boolean modif = false; 88 89 90 private transient Node nodeDelegate; 91 92 93 DataObjectPool.Item item; 94 95 96 private DataLoader loader; 97 98 99 private PropertyChangeSupport changeSupport; 100 private VetoableChangeSupport vetoableChangeSupport; 101 102 105 private static final Object listenersMethodLock = new Object (); 106 107 108 private Object nodeCreationLock = new Object (); 109 110 111 private static Object synchObject = new Object (); 112 113 114 115 static final Logger LOG = Logger.getLogger("org.openide.loaders"); 117 124 public DataObject (FileObject pf, DataLoader loader) throws DataObjectExistsException { 125 this (pf, DataObjectPool.getPOOL().register (pf, loader), loader); 127 } 128 129 136 private DataObject (FileObject pf, DataObjectPool.Item item, DataLoader loader) { 137 this.item = item; 138 this.loader = loader; 139 item.setDataObject (this); 140 } 141 142 154 protected void dispose () { 155 DataObjectPool.Item item = this.item; 156 157 if (item != null) { 158 item.deregister (true); 159 item.setDataObject(null); 160 firePropertyChange (PROP_VALID, Boolean.TRUE, Boolean.FALSE); 161 } 162 } 163 164 172 public void setValid (boolean valid) throws PropertyVetoException { 173 if (!valid && isValid ()) { 174 markInvalid0 (); 175 } 176 } 177 178 181 final void markInvalid0 () throws PropertyVetoException { 182 fireVetoableChange (PROP_VALID, Boolean.TRUE, Boolean.FALSE); 183 dispose (); 184 setModified(false); 185 } 186 187 195 public final boolean isValid () { 196 return item.isValid (); 197 } 198 199 200 201 204 public final DataLoader getLoader () { 205 return loader; 206 } 207 208 211 protected final void markFiles () throws IOException { 212 Iterator en = files ().iterator (); 213 while (en.hasNext ()) { 214 FileObject fo = (FileObject)en.next (); 215 loader.markFile (fo); 216 } 217 } 218 219 226 public Set<FileObject> files() { 227 return java.util.Collections.singleton (getPrimaryFile ()); 228 } 229 230 231 237 public final Node getNodeDelegate () { 238 if (! isValid()) { 239 Exception e = new IllegalStateException ("The data object " + getPrimaryFile() + " is invalid; you may not call getNodeDelegate on it any more; see #17020 and please fix your code"); Logger.getLogger(DataObject.class.getName()).log(Level.WARNING, null, e); 241 } 242 if (nodeDelegate == null) { 243 Children.MUTEX.readAccess (new Runnable () { 247 public void run() { 248 synchronized(nodeCreationLock) { 249 if (nodeDelegate == null) { 250 nodeDelegate = createNodeDelegate(); 251 } 252 } 253 } 254 }); 255 256 if (nodeDelegate == null) { 258 throw new IllegalStateException ("DataObject " + this + " has null node delegate"); } 260 } 261 return nodeDelegate; 262 } 263 264 270 Node getClonedNodeDelegate (DataFilter filter) { 271 return getNodeDelegate ().cloneNode (); 272 } 273 274 277 Node getNodeDelegateOrNull () { 278 return nodeDelegate; 279 } 280 281 298 protected Node createNodeDelegate () { 299 return new DataNode (this, Children.LEAF); 300 } 301 302 307 protected FileLock takePrimaryFileLock () throws IOException { 308 return getPrimaryFile ().lock (); 309 } 310 311 318 static boolean setTemplate (FileObject fo, boolean newTempl) throws IOException { 319 boolean oldTempl = false; 320 321 Object o = fo.getAttribute(DataObject.PROP_TEMPLATE); 322 if ((o instanceof Boolean ) && ((Boolean )o).booleanValue()) 323 oldTempl = true; 324 if (oldTempl == newTempl) 325 return false; 326 327 fo.setAttribute(DataObject.PROP_TEMPLATE, (newTempl ? Boolean.TRUE : null)); 328 329 return true; 330 } 331 332 336 public final void setTemplate (boolean newTempl) throws IOException { 337 if (!setTemplate (getPrimaryFile(), newTempl)) { 338 return; 340 } 341 342 firePropertyChange(DataObject.PROP_TEMPLATE, 343 !newTempl ? Boolean.TRUE : Boolean.FALSE, 344 newTempl ? Boolean.TRUE : Boolean.FALSE); 345 } 346 347 350 public final boolean isTemplate () { 351 Object o = getPrimaryFile().getAttribute(PROP_TEMPLATE); 352 boolean ret = false; 353 if (o instanceof Boolean ) 354 ret = ((Boolean ) o).booleanValue(); 355 return ret; 356 } 357 358 359 362 public abstract boolean isDeleteAllowed (); 363 364 367 public abstract boolean isCopyAllowed (); 368 369 372 public abstract boolean isMoveAllowed (); 373 374 378 public boolean isShadowAllowed () { 379 return true; 380 } 381 382 385 public abstract boolean isRenameAllowed (); 386 387 388 391 public boolean isModified() { 392 return modif; 393 } 394 395 401 public void setModified(boolean modif) { 402 if (this.modif != modif) { 403 this.modif = modif; 404 if (modif) { 405 syncModified.add (this); 406 } else { 407 syncModified.remove (this); 408 } 409 firePropertyChange(DataObject.PROP_MODIFIED, 410 !modif ? Boolean.TRUE : Boolean.FALSE, 411 modif ? Boolean.TRUE : Boolean.FALSE); 412 } 413 } 414 415 418 public abstract HelpCtx getHelpCtx (); 419 420 429 public final FileObject getPrimaryFile () { 430 return item.primaryFile; 431 } 432 433 439 public static DataObject find (FileObject fo) 440 throws DataObjectNotFoundException { 441 if (fo == null) 442 throw new IllegalArgumentException ("Called DataObject.find on null"); 444 try { 445 if (!fo.isValid()) 446 throw new FileStateInvalidException(fo.toString()); 447 448 DataObject obj = DataObjectPool.getPOOL().find (fo); 450 if (obj != null) { 451 return obj; 452 } 453 454 DataLoaderPool p = DataLoaderPool.getDefault(); 456 assert p != null : "No DataLoaderPool found in " + Lookup.getDefault(); 457 obj = p.findDataObject (fo); 458 if (obj != null) { 459 return obj; 460 } 461 462 throw new DataObjectNotFoundException (fo); 463 } catch (DataObjectExistsException ex) { 464 return ex.getDataObject (); 465 } catch (IOException ex) { 466 throw (DataObjectNotFoundException) new DataObjectNotFoundException(fo).initCause(ex); 467 } 468 } 469 470 471 private static Registry REGISTRY_INSTANCE = new Registry(); 472 473 477 public static Registry getRegistry () { 478 return REGISTRY_INSTANCE; 479 } 480 481 485 public String getName () { 486 return getPrimaryFile ().getName (); 487 } 488 489 public String toString () { 490 return super.toString () + '[' + getPrimaryFile () + ']'; 491 } 492 493 497 public final DataFolder getFolder () { 498 FileObject fo = getPrimaryFile ().getParent (); 499 return fo == null ? null : DataFolder.findFolder (fo); 502 } 503 504 511 public final DataObject copy (final DataFolder f) throws IOException { 512 final DataObject[] result = new DataObject[1]; 513 invokeAtomicAction (f.getPrimaryFile (), new FileSystem.AtomicAction () { 514 public void run () throws IOException { 515 result[0] = handleCopy (f); 516 } 517 }, null); 518 fireOperationEvent ( 519 new OperationEvent.Copy (result[0], this), OperationEvent.COPY 520 ); 521 return result[0]; 522 } 523 524 529 protected abstract DataObject handleCopy (DataFolder f) throws IOException; 530 531 535 public final void delete () throws IOException { 536 invokeAtomicAction (getPrimaryFile (), new FileSystem.AtomicAction () { 538 public void run () throws IOException { 539 handleDelete (); 540 item.deregister(false); 541 item.setDataObject(null); 542 } 543 }, synchObject()); 544 firePropertyChange (PROP_VALID, Boolean.TRUE, Boolean.FALSE); 545 fireOperationEvent (new OperationEvent (this), OperationEvent.DELETE); 546 } 547 548 551 protected abstract void handleDelete () throws IOException; 552 553 554 561 public final void rename (String name) throws IOException { 562 if (name != null && name.trim ().length ()==0) { 563 IllegalArgumentException iae = new IllegalArgumentException (this.getName ()); 564 String msg = NbBundle.getMessage (DataObject.class, 565 "MSG_NotValidName", getName ()); Exceptions.attachLocalizedMessage(iae, msg); 567 throw iae; 568 } 569 570 571 class Op implements FileSystem.AtomicAction { 572 FileObject oldPf; 573 FileObject newPf; 574 575 String oldName; 576 String newName; 577 public void run() throws IOException { 578 oldName = getName (); 579 580 if (oldName.equals (newName)) return; 582 oldPf = getPrimaryFile (); 583 newPf = handleRename (newName); 584 if (oldPf != newPf) 585 item.changePrimaryFile (newPf); 586 newName = getName (); 587 } 588 } 589 590 Op op = new Op(); 592 op.newName = name; 593 invokeAtomicAction (getPrimaryFile().getParent(), op, synchObject()); 594 595 if (op.oldName.equals (op.newName)) { 596 return; } 598 599 if (op.oldPf != op.newPf) { 600 firePropertyChange (PROP_PRIMARY_FILE, op.oldPf, op.newPf); 601 } 602 firePropertyChange (PROP_NAME, op.oldName, op.newName); 603 firePropertyChange (PROP_FILES, null, null); 604 605 fireOperationEvent (new OperationEvent.Rename (this, op.oldName), OperationEvent.RENAME); 606 } 607 608 614 protected abstract FileObject handleRename (String name) throws IOException; 615 616 621 public final void move (final DataFolder df) throws IOException { 622 class Op implements FileSystem.AtomicAction { 623 FileObject old; 624 public void run () throws IOException { 625 if ((getFolder () == null)) return; if (df.equals (getFolder ())) return; 628 old = getPrimaryFile (); 630 FileObject mf = handleMove (df); 631 item.changePrimaryFile (mf); 632 } 633 } 634 Op op = new Op(); 635 636 invokeAtomicAction (df.getPrimaryFile(), op, synchObject()); 637 638 firePropertyChange (PROP_PRIMARY_FILE, op.old, getPrimaryFile ()); 639 fireOperationEvent ( 640 new OperationEvent.Move (this, op.old), OperationEvent.MOVE 641 ); 642 } 643 644 650 protected abstract FileObject handleMove (DataFolder df) throws IOException; 651 652 660 protected DataShadow handleCreateShadow (DataFolder f) throws IOException { 661 return DataShadow.create (f, this); 662 } 663 664 670 public final DataShadow createShadow (final DataFolder f) throws IOException { 671 final DataShadow[] result = new DataShadow[1]; 672 673 invokeAtomicAction (f.getPrimaryFile (), new FileSystem.AtomicAction () { 674 public void run () throws IOException { 675 result[0] = handleCreateShadow (f); 676 } 677 }, null); 678 fireOperationEvent ( 679 new OperationEvent.Copy (result[0], this), OperationEvent.SHADOW 680 ); 681 return result[0]; 682 } 683 684 691 public final DataObject createFromTemplate (DataFolder f) 692 throws IOException { 693 return createFromTemplate (f, null); 694 } 695 696 705 public final DataObject createFromTemplate ( 706 final DataFolder f, final String name 707 ) throws IOException { 708 return createFromTemplate(f, name, Collections.<String ,Object >emptyMap()); 709 } 710 711 724 public final DataObject createFromTemplate( 725 final DataFolder f, final String name, final Map<String ,? extends Object > parameters 726 ) throws IOException { 727 CreateAction create = new CreateAction(this, f, name, parameters); 728 invokeAtomicAction (f.getPrimaryFile (), create, null); 729 fireOperationEvent ( 730 new OperationEvent.Copy (create.result, this), OperationEvent.TEMPL 731 ); 732 return create.result; 733 } 734 735 746 protected abstract DataObject handleCreateFromTemplate ( 747 DataFolder df, String name 748 ) throws IOException; 749 750 751 755 private static void fireOperationEvent (OperationEvent ev, int type) { 756 DataLoaderPool.getDefault().fireOperationEvent (ev, type); 757 } 758 759 763 Object synchObject() { 764 return synchObject; 765 } 766 767 769 private void invokeAtomicAction (FileObject target, final FileSystem.AtomicAction action, final Object lockTheSession) throws IOException { 770 FileSystem.AtomicAction toRun; 771 772 if (lockTheSession != null) { 773 class WrapRun implements FileSystem.AtomicAction { 774 public void run() throws IOException { 775 synchronized (lockTheSession) { 776 action.run(); 777 } 778 } 779 } 780 toRun = new WrapRun(); 781 } else { 782 toRun = action; 783 } 784 785 if (Boolean.getBoolean ("netbeans.dataobject.insecure.operation")) { 786 DataObjectPool.getPOOL ().runAtomicActionSimple (target, toRun); 787 return; 788 } 789 790 791 if (this instanceof DataFolder) { 792 DataObjectPool.getPOOL ().runAtomicActionSimple (target, toRun); 794 } else { 795 DataObjectPool.getPOOL ().runAtomicAction (target, toRun); 797 } 798 } 799 800 801 805 808 public void addPropertyChangeListener (PropertyChangeListener l) { 809 synchronized (listenersMethodLock) { 810 if (changeSupport == null) 811 changeSupport = new PropertyChangeSupport(this); 812 } 813 changeSupport.addPropertyChangeListener(l); 814 } 815 816 819 public void removePropertyChangeListener (PropertyChangeListener l) { 820 if (changeSupport != null) 821 changeSupport.removePropertyChangeListener(l); 822 } 823 824 831 protected final void firePropertyChange (String name, Object oldValue, Object newValue) { 832 if (changeSupport != null) 833 changeSupport.firePropertyChange(name, oldValue, newValue); 834 } 835 836 840 844 public void addVetoableChangeListener (VetoableChangeListener l) { 845 synchronized (listenersMethodLock) { 846 if (vetoableChangeSupport == null) 847 vetoableChangeSupport = new VetoableChangeSupport(this); 848 } 849 vetoableChangeSupport.addVetoableChangeListener(l); 850 } 851 852 856 public void removeVetoableChangeListener (VetoableChangeListener l) { 857 if (vetoableChangeSupport != null) 858 vetoableChangeSupport.removeVetoableChangeListener(l); 859 } 860 861 868 protected final void fireVetoableChange (String name, Object oldValue, Object newValue) 869 throws PropertyVetoException 870 { 871 if (vetoableChangeSupport != null) 872 vetoableChangeSupport.fireVetoableChange(name, oldValue, newValue); 873 } 874 875 879 895 public <T extends Node.Cookie> T getCookie(Class <T> c) { 896 if (c.isInstance (this)) { 897 return c.cast(this); 898 } 899 return null; 900 } 901 902 917 public Lookup getLookup() { 918 return getNodeDelegate().getLookup(); 919 } 920 921 934 protected <T extends Node.Cookie> T getCookie(DataShadow shadow, Class <T> clazz) { 935 return getCookie (clazz); 936 } 937 938 942 945 public Object writeReplace () { 946 return new Replace (this); 947 } 948 949 950 952 private static final class Replace extends Object implements Serializable { 953 954 private FileObject fo; 955 956 private transient DataObject obj; 957 958 private static final long serialVersionUID =-627843044348243058L; 959 962 public Replace (DataObject obj) { 963 this.obj = obj; 964 this.fo = obj.getPrimaryFile (); 965 } 966 967 public Object readResolve () { 968 return obj; 969 } 970 971 972 private void readObject (ObjectInputStream ois) 973 throws IOException, ClassNotFoundException { 974 ois.defaultReadObject (); 975 if (fo == null) { 976 throw new java.io.FileNotFoundException (); 977 } 978 obj = DataObject.find(fo); 980 } 981 } 982 983 985 static String getString (String name) { 986 return NbBundle.getMessage (DataObject.class, name); 987 } 988 989 993 public static interface Container extends Node.Cookie { 994 995 public static final String PROP_CHILDREN = "children"; 997 999 public DataObject[] getChildren (); 1000 1001 1004 public void addPropertyChangeListener (PropertyChangeListener l); 1005 1006 1009 public void removePropertyChangeListener (PropertyChangeListener l); 1010 } 1011 1012 1016 public static final class Registry extends Object { 1017 1018 1019 private Registry () { 1020 } 1021 1022 1025 public void addChangeListener (final ChangeListener chl) { 1026 modified.addChangeListener(chl); 1027 } 1028 1029 1032 public void removeChangeListener (final ChangeListener chl) { 1033 modified.removeChangeListener(chl); 1034 } 1035 1036 1039 public Set<DataObject> getModifiedSet() { 1040 return Collections.unmodifiableSet(syncModified); 1041 } 1042 1043 1046 public DataObject[] getModified () { 1047 return syncModified.toArray(new DataObject[0]); 1048 } 1049 } 1050 1051 private static final class ModifiedRegistry extends HashSet<DataObject> { 1052 static final long serialVersionUID =-2861723614638919680L; 1053 1054 1055 private Set<ChangeListener> listeners; 1056 1057 ModifiedRegistry() {} 1058 1059 1062 public final synchronized void addChangeListener (final ChangeListener chl) { 1063 if (listeners == null) listeners = new HashSet<ChangeListener>(5); 1064 listeners.add(chl); 1065 } 1066 1067 1070 public final synchronized void removeChangeListener (final ChangeListener chl) { 1071 if (listeners == null) return; 1072 listeners.remove(chl); 1073 } 1074 1075 1077 @Override 1078 public boolean add (DataObject o) { 1079 boolean result = super.add(o); 1080 if (result) fireChangeEvent(new ChangeEvent(this)); 1081 return result; 1082 } 1083 1084 @Override 1085 public boolean remove (Object o) { 1086 boolean result = super.remove(o); 1087 if (result) fireChangeEvent(new ChangeEvent(this)); 1088 return result; 1089 } 1090 1091 1094 protected final void fireChangeEvent (ChangeEvent che) { 1095 if (listeners == null) return; 1096 Set<ChangeListener> clones; 1097 synchronized (this) { 1098 clones = new HashSet<ChangeListener>(listeners); 1099 } 1100 for (ChangeListener l : clones) { 1102 l.stateChanged(che); 1103 } 1104 } 1105 1106 } 1108 1111 void recognizedByFolder() { 1112 } 1113 1114 void notifyFileRenamed(FileRenameEvent fe) { 1118 if (fe.getFile ().equals (getPrimaryFile ())) { 1119 firePropertyChange(PROP_NAME, fe.getName(), getName()); 1120 } 1121 } 1122 1123 void notifyFileDeleted(FileEvent fe) { 1124 } 1125 1126 void notifyFileChanged(FileEvent fe) { 1127 } 1128 1129 void notifyFileDataCreated(FileEvent fe) { 1130 } 1131 1132 void notifyAttributeChanged(FileAttributeEvent fae) { 1133 if (! EA_ASSIGNED_LOADER.equals(fae.getName())) { 1134 return; 1136 } 1137 FileObject f = fae.getFile(); 1138 if (f != null) { 1139 String attrFromFO = (String )f.getAttribute(EA_ASSIGNED_LOADER); 1140 if (attrFromFO == null || (! attrFromFO.equals(getLoader().getClass().getName()))) { 1141 Set<FileObject> single = new HashSet<FileObject>(); single.add(f); 1143 if (!DataObjectPool.getPOOL().revalidate(single).isEmpty()) { 1144 LOG.info("It was not possible to invalidate data object: " + this); } else { 1146 FolderList.changedDataSystem (f.getParent()); 1149 } 1150 } 1151 } 1152 } 1153 static final class CreateAction implements FileSystem.AtomicAction { 1154 public DataObject result; 1155 private String name; 1156 private DataFolder f; 1157 private DataObject orig; 1158 private Map<String , ? extends Object > param; 1159 1160 private static ThreadLocal <CreateAction> CURRENT = new ThreadLocal <CreateAction>(); 1161 1162 public CreateAction(DataObject orig, DataFolder f, String name, Map<String , ? extends Object > param) { 1163 this.orig = orig; 1164 this.f = f; 1165 this.name = name; 1166 this.param = param; 1167 } 1168 1169 public void run () throws IOException { 1170 CreateAction prev = CURRENT.get(); 1171 try { 1172 CURRENT.set(this); 1173 result = orig.handleCreateFromTemplate(f, name); 1174 } finally { 1175 CURRENT.set(prev); 1176 } 1177 } 1178 1179 public static Map<String ,Object > findParameters(String name) { 1180 CreateAction c = CURRENT.get(); 1181 if (c == null) { 1182 return Collections.emptyMap(); 1183 } 1184 HashMap<String ,Object > all = new HashMap<String ,Object >(); 1185 for (CreateFromTemplateAttributesProvider provider : Lookup.getDefault().lookupAll(CreateFromTemplateAttributesProvider.class)) { 1186 Map<String ,? extends Object > map = provider.attributesFor(c.orig, c.f, c.name); 1187 if (map != null) { 1188 for (Map.Entry<String ,? extends Object > e : map.entrySet()) { 1189 all.put(e.getKey(), e.getValue()); 1190 } 1191 } 1192 } 1193 if (c.param != null) { 1194 for (Map.Entry<String ,? extends Object > e : c.param.entrySet()) { 1195 all.put(e.getKey(), e.getValue()); 1196 } 1197 } 1198 1199 if (!all.containsKey("name") && name != null) { all.put("name", name); } 1202 if (!all.containsKey("user")) { all.put("user", System.getProperty("user.name")); } 1205 Date d = new Date(); 1206 if (!all.containsKey("date")) { all.put("date", DateFormat.getDateInstance().format(d)); } 1209 if (!all.containsKey("time")) { all.put("time", DateFormat.getTimeInstance().format(d)); } 1212 1213 return Collections.unmodifiableMap(all); 1214 } 1215 1216 } } 1218 | Popular Tags |