KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > loaders > DataObject


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.loaders;
21
22
23 import java.beans.*;
24 import java.io.*;
25 import java.text.DateFormat JavaDoc;
26 import java.util.*;
27 import java.util.logging.Level JavaDoc;
28 import java.util.logging.Logger JavaDoc;
29 import javax.swing.event.*;
30 import org.openide.filesystems.*;
31 import org.openide.nodes.*;
32 import org.openide.util.*;
33
34 /** Object that represents one or more file objects, with added behavior
35 * accessible though {@link #getLookup} lookup pattern. Since version 6.0
36 * this class implements {@link Lookup.Provider}.
37 *
38 * @author Jaroslav Tulach, Petr Hamernik, Jan Jancura, Ian Formanek
39 */

40 public abstract class DataObject extends Object JavaDoc
41 implements Node.Cookie, Serializable, HelpCtx.Provider, Lookup.Provider {
42     /** generated Serialized Version UID */
43     private static final long serialVersionUID = 3328227388376142699L;
44
45     /** Name of the template property. */
46     public static final String JavaDoc PROP_TEMPLATE = "template"; // NOI18N
47

48     /** Name of the name property. */
49     public static final String JavaDoc PROP_NAME = "name"; // NOI18N
50

51     /** Name of the help context property. */
52     public static final String JavaDoc PROP_HELP = "helpCtx"; // NOI18N
53

54     /** Name of the modified property. */
55     public static final String JavaDoc PROP_MODIFIED = "modified"; // NOI18N
56

57     /** Name of the property used during notification of changes in the set of cookies attached to this object. */
58     public static final String JavaDoc PROP_COOKIE = Node.PROP_COOKIE;
59
60     /** Name of valid property. Allows listening to deletion or disposal of the data object. */
61     public static final String JavaDoc PROP_VALID = "valid"; // NOI18N
62

63     /** Name of primary file property. Primary file is changed when the object is moved */
64     public static final String JavaDoc PROP_PRIMARY_FILE = "primaryFile"; // NOI18N
65
/** Name of files property. Allows listening to set of files handled by this object. */
66     public static final String JavaDoc PROP_FILES = "files"; // NOI18N
67

68     /** Extended attribute for holding the class of the loader that should
69     * be used to recognize a file object before the normal processing takes
70     * place.
71     */

72     static final String JavaDoc EA_ASSIGNED_LOADER = "NetBeansAttrAssignedLoader"; // NOI18N
73
/** Extended attribute which may be used in addition to EA_ASSIGNED_LOADER
74      * which indicates the code name base of the module that installed that preferred
75      * loader. If the indicated module is not installed, ignore the loader request.
76      * See #13816.
77      */

78     static final String JavaDoc EA_ASSIGNED_LOADER_MODULE = "NetBeansAttrAssignedLoaderModule"; // NOI18N
79

80     /** all modified data objects contains DataObjects.
81     * ! Use syncModified for modifications instead !*/

82     private static ModifiedRegistry modified = new ModifiedRegistry();
83     /** sync modified data (for modification operations) */
84     private static Set<DataObject> syncModified = Collections.synchronizedSet(modified);
85
86     /** Modified flag */
87     private boolean modif = false;
88
89     /** the node delegate for this data object */
90     private transient Node nodeDelegate;
91
92     /** item with info about this data object */
93     DataObjectPool.Item item;
94
95     /** the loader for this data object */
96     private DataLoader loader;
97
98     /** property change listener support */
99     private PropertyChangeSupport changeSupport;
100     private VetoableChangeSupport vetoableChangeSupport;
101
102     /** The synchronization lock used only for methods creating listeners
103      * objects. It is static and shared among all DataObjects.
104      */

105     private static final Object JavaDoc listenersMethodLock = new Object JavaDoc();
106     
107     /** Lock used for ensuring there will be just one node delegate */
108     private Object JavaDoc nodeCreationLock = new Object JavaDoc();
109     
110     /** Lock for copy/move/rename/etc. operations */
111     private static Object JavaDoc synchObject = new Object JavaDoc ();
112
113
114     /** default logger for whole package */
115     static final Logger JavaDoc LOG = Logger.getLogger("org.openide.loaders"); // NOI18N
116

117     /** Create a new data object.
118      *
119      * @param pf primary file object for this data object
120      * @param loader loader that created the data object
121      * @exception DataObjectExistsException if there is already a data object
122      * for this primary file
123      */

124     public DataObject (FileObject pf, DataLoader loader) throws DataObjectExistsException {
125         // By registering we'll also get notifications about file changes.
126
this (pf, DataObjectPool.getPOOL().register (pf, loader), loader);
127     }
128
129     /** Private constructor. At this time the constructor receives
130     * the primary file and pool item where it should register itself.
131     *
132     * @param pf primary file
133     * @param item the item to register into
134     * @param loader loader that created the data object
135     */

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     // This method first unregisters the object, then calls method unreferenced.
143
// After that it asks the parent folder to regenerate its list of children,
144
// so different object is usually created for primary file of this object.
145
/** Allows subclasses to discard the object. When an object is discarded,
146     * it is released from the list of objects registered in the system.
147     * Then the contents of the parent folder (if it still exists) are rescanned, which
148     * may result in the creation of a new data object for the primary file.
149     * <P>
150     * The normal use of this method is to change the type of a data object.
151     * Because this would usually only be invoked from
152     * the original data object, it is protected.
153     */

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     /** Setter that allows to destroy this data object. Because such
165     * operation can be dangerous and not always possible (if the data object
166     * is opened in editor) it can be vetoed. Either by this data object
167     * or by any vetoable listener attached to this object (like editor support)
168     *
169     * @param valid should be false
170     * @exception PropertyVetoException if the invalidation has been vetoed
171     */

172     public void setValid (boolean valid) throws PropertyVetoException {
173         if (!valid && isValid ()) {
174             markInvalid0 ();
175         }
176     }
177         
178     /** Tries to mark the object invalid. Called from setValid or from
179      * MultiDataObject.notifyDeleted
180      */

181     final void markInvalid0 () throws PropertyVetoException {
182         fireVetoableChange (PROP_VALID, Boolean.TRUE, Boolean.FALSE);
183         dispose ();
184         setModified(false);
185     }
186
187     /** Test whether the data object is still valid and usable.
188     * <P>
189     * The object can become invalid when it is deleted, its files are deleted, or
190     * {@link #dispose} is called.
191     * <P>
192     * When the validity of the object changes a property change event is fired, so
193     * anyone can listen and be notified when the object is deleted/disposed.
194     */

195     public final boolean isValid () {
196         return item.isValid ();
197     }
198
199
200
201     /** Get the loader that created this data object.
202     * @return the data loader
203     */

204     public final DataLoader getLoader () {
205         return loader;
206     }
207
208     /** Mark all contained files as belonging to this loader.
209      * If the files are rescanned (e.g. after a disposal), the current data loader will be given preference.
210     */

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     /** Get all contained files.
220      * These file objects should ideally have had the {@linkplain FileObject#setImportant important flag} set appropriately.
221     * <P>
222     * The default implementation returns a set consisting only of the primary file.
223     *
224     * @return set of files
225     */

226     public Set<FileObject> files() {
227         return java.util.Collections.singleton (getPrimaryFile ());
228     }
229
230
231     /** Get the node delegate. Either {@link #createNodeDelegate creates it} (if it does not
232     * already exist) or
233     * returns a previously created instance of it.
234     * @return the node delegate (without parent) for this data object
235     * @see <a HREF="doc-files/api.html#delegate">Datasystems API - Node Delegates</a>
236     */

237     public final Node getNodeDelegate () {
238         if (! isValid()) {
239             Exception JavaDoc e = new IllegalStateException JavaDoc("The data object " + getPrimaryFile() + " is invalid; you may not call getNodeDelegate on it any more; see #17020 and please fix your code"); // NOI18N
240
Logger.getLogger(DataObject.class.getName()).log(Level.WARNING, null, e);
241         }
242         if (nodeDelegate == null) {
243             // synchronize on something private, so only one delegate can be created
244
// do not synchronize on this, because we could deadlock with
245
// subclasses could synchronize too.
246
Children.MUTEX.readAccess (new Runnable JavaDoc() {
247                 public void run() {
248                     synchronized(nodeCreationLock) {
249                         if (nodeDelegate == null) {
250                             nodeDelegate = createNodeDelegate();
251                         }
252                     }
253                 }
254             });
255
256             // JST: debuging code
257
if (nodeDelegate == null) {
258                 throw new IllegalStateException JavaDoc("DataObject " + this + " has null node delegate"); // NOI18N
259
}
260         }
261         return nodeDelegate;
262     }
263     
264     /** This method allows DataFolder to filter its nodes.
265     *
266     * @param filter filter for subdata objects
267     * @return the node delegate (without parent) the node is new instance
268     * of node and can be inserted to any place in the hierarchy
269     */

270     Node getClonedNodeDelegate (DataFilter filter) {
271         return getNodeDelegate ().cloneNode ();
272     }
273
274     /** Access method for node delagate.
275      * @return node delegate or null
276      */

277     Node getNodeDelegateOrNull () {
278         return nodeDelegate;
279     }
280
281     /** Provides node that should represent this data object.
282     * <p>The default implementation creates an instance of {@link DataNode}.
283     * Most subclasses will override this method to provide a <code>DataNode</code>
284     * (usually subclassed).
285     * <P>
286     * This method is called only once per data object.
287     * <p>It is strongly recommended that the resulting node will, when asked for
288     * the cookie <samp>DataObject.class</samp>, return this same data object.
289     * <p>It is also recommended that the node:
290     * <ol>
291     * <li>Base its name on {@link #getName}.
292     * <li>Base its display name additionally on {@link DataNode#getShowFileExtensions}.
293     * <li>Tune its display name and icon according to {@link org.openide.filesystems.FileSystem.Status}.
294     * </ol>
295     * @return the node delegate (without parent) for this data object
296     * @see <a HREF="doc-files/api.html#create-delegate">Datasystems API - Creating a node delegate</a>
297     */

298     protected Node createNodeDelegate () {
299         return new DataNode (this, Children.LEAF);
300     }
301
302     /** Obtains lock for primary file.
303     *
304     * @return the lock
305     * @exception IOException if taking the lock fails
306     */

307     protected FileLock takePrimaryFileLock () throws IOException {
308         return getPrimaryFile ().lock ();
309     }
310
311     /** Package private method to assign template attribute to a file.
312     * Used also from FileEntry.
313     *
314     * @param fo the file
315     * @param newTempl is template or not
316     * @return true if the value change/false otherwise
317     */

318     static boolean setTemplate (FileObject fo, boolean newTempl) throws IOException {
319         boolean oldTempl = false;
320
321         Object JavaDoc o = fo.getAttribute(DataObject.PROP_TEMPLATE);
322         if ((o instanceof Boolean JavaDoc) && ((Boolean JavaDoc)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     /** Set the template status of this data object.
333     * @param newTempl <code>true</code> if the object should be a template
334     * @exception IOException if setting the template state fails
335     */

336     public final void setTemplate (boolean newTempl) throws IOException {
337         if (!setTemplate (getPrimaryFile(), newTempl)) {
338             // no change in state
339
return;
340         }
341
342         firePropertyChange(DataObject.PROP_TEMPLATE,
343                            !newTempl ? Boolean.TRUE : Boolean.FALSE,
344                            newTempl ? Boolean.TRUE : Boolean.FALSE);
345     }
346
347     /** Get the template status of this data object.
348     * @return <code>true</code> if it is a template
349     */

350     public final boolean isTemplate () {
351         Object JavaDoc o = getPrimaryFile().getAttribute(PROP_TEMPLATE);
352         boolean ret = false;
353         if (o instanceof Boolean JavaDoc)
354             ret = ((Boolean JavaDoc) o).booleanValue();
355         return ret;
356     }
357
358
359     /** Test whether the object may be deleted.
360     * @return <code>true</code> if it may
361     */

362     public abstract boolean isDeleteAllowed ();
363
364     /** Test whether the object may be copied.
365     * @return <code>true</code> if it may
366     */

367     public abstract boolean isCopyAllowed ();
368
369     /** Test whether the object may be moved.
370     * @return <code>true</code> if it may
371     */

372     public abstract boolean isMoveAllowed ();
373
374     /** Test whether the object may create shadows.
375      * <p>The default implementation returns <code>true</code>.
376     * @return <code>true</code> if it may
377     */

378     public boolean isShadowAllowed () {
379         return true;
380     }
381
382     /** Test whether the object may be renamed.
383     * @return <code>true</code> if it may
384     */

385     public abstract boolean isRenameAllowed ();
386
387
388     /** Test whether the object is modified.
389     * @return <code>true</code> if it is modified
390     */

391     public boolean isModified() {
392         return modif;
393     }
394
395     /** Set whether the object is considered modified.
396      * Also fires a change event.
397     * If the new value is <code>true</code>, the data object is added into a {@link #getRegistry registry} of opened data objects.
398     * If the new value is <code>false</code>,
399     * the data object is removed from the registry.
400     */

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     /** Get help context for this object.
416     * @return the help context
417     */

418     public abstract HelpCtx getHelpCtx ();
419
420     /** Get the primary file for this data object.
421      * For example,
422     * Java source uses <code>*.java</code> and <code>*.class</code> files but the primary one is
423     * always <code>*.java</code>. Please note that two data objects are {@link #equals equivalent} if
424     * they use the same primary file.
425     * <p><em>Warning:</em> do not call {@link Node#getHandle} or {@link DefaultHandle#createHandle} in this method.
426     *
427     * @return the primary file
428     */

429     public final FileObject getPrimaryFile () {
430         return item.primaryFile;
431     }
432
433     /** Finds the data object for a specified file object.
434     * @param fo file object
435     * @return the data object for that file
436     * @exception DataObjectNotFoundException if the file does not have a
437     * data object
438     */

439     public static DataObject find (FileObject fo)
440     throws DataObjectNotFoundException {
441         if (fo == null)
442             throw new IllegalArgumentException JavaDoc("Called DataObject.find on null"); // NOI18N
443

444         try {
445             if (!fo.isValid())
446                 throw new FileStateInvalidException(fo.toString());
447             
448             // try to scan directly the pool (holds only primary files)
449
DataObject obj = DataObjectPool.getPOOL().find (fo);
450             if (obj != null) {
451                 return obj;
452             }
453
454             // try to use the loaders machinery
455
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     /** the only instance */
471     private static Registry REGISTRY_INSTANCE = new Registry();
472     
473     /** Get the registry containing all modified objects.
474     *
475     * @return the registry
476     */

477     public static Registry getRegistry () {
478         return REGISTRY_INSTANCE;
479     }
480
481     /** Get the name of the data object.
482     * <p>The default implementation uses the name of the primary file.
483     * @return the name
484     */

485     public String JavaDoc getName () {
486         return getPrimaryFile ().getName ();
487     }
488
489     public String JavaDoc toString () {
490         return super.toString () + '[' + getPrimaryFile () + ']';
491     }
492
493     /** Get the folder this data object is stored in.
494     * @return the folder; <CODE>null</CODE> if the primary file
495     * is the {@link FileObject#isRoot root} of its filesystem
496     */

497     public final DataFolder getFolder () {
498         FileObject fo = getPrimaryFile ().getParent ();
499         // could throw IllegalArgumentException but only if fo is not folder
500
// => then there is a bug in filesystem implementation
501
return fo == null ? null : DataFolder.findFolder (fo);
502     }
503
504     /** Copy this object to a folder. The copy of the object is required to
505     * be deletable and movable.
506     * <p>An event is fired, and atomicity is implemented.
507     * @param f the folder to copy the object to
508     * @exception IOException if something went wrong
509     * @return the new object
510     */

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     /** Copy this object to a folder (implemented by subclasses).
525     * @param f target folder
526     * @return the new data object
527     * @exception IOException if an error occures
528     */

529     protected abstract DataObject handleCopy (DataFolder f) throws IOException;
530
531     /** Delete this object.
532      * <p>Events are fired and atomicity is implemented.
533     * @exception IOException if an error occures
534     */

535     public final void delete () throws IOException {
536         // the object is ready to be closed
537
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     /** Delete this object (implemented by subclasses).
549     * @exception IOException if an error occures
550     */

551     protected abstract void handleDelete () throws IOException;
552
553
554     /** Rename this object.
555      * <p>Events are fired and atomicity is implemented.
556     *
557     * @param name the new name
558     *
559     * @exception IOException if an error occurs
560     */

561     public final void rename (String JavaDoc name) throws IOException {
562         if (name != null && name.trim ().length ()==0) {
563             IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc (this.getName ());
564             String JavaDoc msg = NbBundle.getMessage (DataObject.class,
565                                   "MSG_NotValidName", getName ()); // NOI18N
566
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 JavaDoc oldName;
576             String JavaDoc newName;
577             public void run() throws IOException {
578                 oldName = getName ();
579
580                 if (oldName.equals (newName)) return; // the new name is the same as the old one
581

582                 oldPf = getPrimaryFile ();
583                 newPf = handleRename (newName);
584                 if (oldPf != newPf)
585                     item.changePrimaryFile (newPf);
586                  newName = getName ();
587             }
588         }
589         
590         // executes atomic action with renaming
591
Op op = new Op();
592         op.newName = name;
593         invokeAtomicAction (getPrimaryFile().getParent(), op, synchObject());
594
595         if (op.oldName.equals (op.newName)) {
596             return; // the new name is the same as the old one
597
}
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     /** Rename this object (implemented in subclasses).
609     *
610     * @param name name to rename the object to
611     * @return new primary file of the object
612     * @exception IOException if an error occures
613     */

614     protected abstract FileObject handleRename (String JavaDoc name) throws IOException;
615
616     /** Move this object to another folder.
617      * <p>An event is fired and atomicity is implemented.
618     * @param df folder to move object to
619     * @exception IOException if an error occurs
620     */

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; // cannot move filesystem root
626
if (df.equals (getFolder ())) return; // if the destination folder is the same as the current one ==>> do nothing
627

628                 // executes atomic action for moving
629
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     /** Move this object to another folder (implemented in subclasses).
645     *
646     * @param df target data folder
647     * @return new primary file of the object
648     * @exception IOException if an error occures
649     */

650     protected abstract FileObject handleMove (DataFolder df) throws IOException;
651
652     /** Creates shadow for this object in specified folder (overridable in subclasses).
653      * <p>The default
654     * implementation creates a reference data shadow and pastes it into
655     * the specified folder.
656     *
657     * @param f the folder to create a shortcut in
658     * @return the shadow
659     */

660     protected DataShadow handleCreateShadow (DataFolder f) throws IOException {
661         return DataShadow.create (f, this);
662     }
663
664     /** Creates shadow for this object in specified folder.
665      * <p>An event is fired and atomicity is implemented.
666     *
667     * @param f the folder to create shortcut in
668     * @return the shadow
669     */

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     /** Create a new object from template (with a name depending on the template).
685     *
686     * @param f folder to create object in
687     * @return new data object based on this one
688     * @exception IOException if an error occured
689     * @see #createFromTemplate(DataFolder,String)
690     */

691     public final DataObject createFromTemplate (DataFolder f)
692     throws IOException {
693         return createFromTemplate (f, null);
694     }
695
696     /** Create a new object from template.
697     * Asks {@link #handleCreateFromTemplate}.
698     *
699     * @param f folder to create object in
700     * @param name name of object that should be created, or <CODE>null</CODE> if the
701     * name should be same as that of the template (or otherwise mechanically generated)
702     * @return the new data object
703     * @exception IOException if an error occured
704     */

705     public final DataObject createFromTemplate (
706         final DataFolder f, final String JavaDoc name
707     ) throws IOException {
708         return createFromTemplate(f, name, Collections.<String JavaDoc,Object JavaDoc>emptyMap());
709     }
710     
711     /** More generic way how to instantiate a {@link DataObject}. One can
712     * not only specify its name, but also pass a map of parameters that
713     * can influence the copying of the stream.
714     *
715     * @param f folder to create object in
716     * @param name name of object that should be created, or <CODE>null</CODE> if the
717     * name should be same as that of the template (or otherwise mechanically generated)
718     * @param parameters map of named objects that are going to be used when
719     * creating the new object
720     * @return the new data object
721     * @exception IOException if an error occured
722     * @since 6.1
723     */

724     public final DataObject createFromTemplate(
725         final DataFolder f, final String JavaDoc name, final Map<String JavaDoc,? extends Object JavaDoc> 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     /** Create a new data object from template (implemented in subclasses).
736      * This method should
737     * copy the content of the template to the destination folder and assign a new name
738     * to the new object.
739     *
740     * @param df data folder to create object in
741     * @param name name to give to the new object (or <CODE>null</CODE>
742     * if the name should be chosen according to the template)
743     * @return the new data object
744     * @exception IOException if an error occured
745     */

746     protected abstract DataObject handleCreateFromTemplate (
747         DataFolder df, String JavaDoc name
748     ) throws IOException;
749
750
751     /** Fires operation event to data loader pool.
752     * @param ev the event
753     * @param type OperationEvent.XXXX constant
754     */

755     private static void fireOperationEvent (OperationEvent ev, int type) {
756         DataLoaderPool.getDefault().fireOperationEvent (ev, type);
757     }
758
759     /** Provide object used for synchronization.
760      * @return <CODE>this</CODE> in DataObject implementation. Other DataObjects
761      * (MultiDataObject) can rewrite this method and return own synch object.
762      */

763     Object JavaDoc synchObject() {
764         return synchObject;
765     }
766     
767     /** Invokes atomic action.
768      */

769     private void invokeAtomicAction (FileObject target, final FileSystem.AtomicAction action, final Object JavaDoc 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             // action is slow
793
DataObjectPool.getPOOL ().runAtomicActionSimple (target, toRun);
794         } else {
795             // it is quick, make it block DataObject recognition
796
DataObjectPool.getPOOL ().runAtomicAction (target, toRun);
797         }
798     }
799      
800     
801     //
802
// Property change support
803
//
804

805     /** Add a property change listener.
806      * @param l the listener to add
807     */

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     /** Remove a property change listener.
817      * @param l the listener to remove
818     */

819     public void removePropertyChangeListener (PropertyChangeListener l) {
820         if (changeSupport != null)
821             changeSupport.removePropertyChangeListener(l);
822     }
823
824     /** Fires property change notification to all listeners registered via
825     * {@link #addPropertyChangeListener}.
826     *
827     * @param name of property
828     * @param oldValue old value
829     * @param newValue new value
830     */

831     protected final void firePropertyChange (String JavaDoc name, Object JavaDoc oldValue, Object JavaDoc newValue) {
832         if (changeSupport != null)
833             changeSupport.firePropertyChange(name, oldValue, newValue);
834     }
835
836     //
837
// Property change support
838
//
839

840     /** Add a listener to vetoable changes.
841      * @param l the listener to add
842      * @see #PROP_VALID
843     */

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     /** Add a listener to vetoable changes.
853      * @param l the listener to remove
854      * @see #PROP_VALID
855     */

856     public void removeVetoableChangeListener (VetoableChangeListener l) {
857         if (vetoableChangeSupport != null)
858             vetoableChangeSupport.removeVetoableChangeListener(l);
859     }
860
861     /** Fires vetoable change notification.
862     *
863     * @param name of property
864     * @param oldValue old value
865     * @param newValue new value
866     * @exception PropertyVetoException if the change has been vetoed
867     */

868     protected final void fireVetoableChange (String JavaDoc name, Object JavaDoc oldValue, Object JavaDoc newValue)
869         throws PropertyVetoException
870     {
871         if (vetoableChangeSupport != null)
872             vetoableChangeSupport.fireVetoableChange(name, oldValue, newValue);
873     }
874
875     //
876
// Cookie
877
//
878

879     /** Obtain a cookie from the data object.
880     * May be overridden by subclasses to extend the behaviour of
881     * data objects.
882     * <P>
883     * The default implementation tests if this object is of the requested class and
884     * if so, returns it.
885     * <p>
886     * <b>Warning:</b> the {@link #getCookie} method and {@link #getLookup}
887     * method are ment to be interchangable - e.g. if you override one of them
888     * be sure to override also the other and try as much as possible to
889     * keep the same content in each of them. The default implementation tries
890     * to do that as much as possible.
891     *
892     * @param c class of requested cookie
893     * @return a cookie or <code>null</code> if such cookies are not supported
894     */

895     public <T extends Node.Cookie> T getCookie(Class JavaDoc<T> c) {
896         if (c.isInstance (this)) {
897             return c.cast(this);
898         }
899         return null;
900     }
901     
902     /** Represents a context of the data object. This method is a more
903      * general replacement for {@link #getCookie} and should preferably
904      * be used instead of the old method. The default implementation
905      * inside a data object
906      * returns the <code>getNodeDelegate().getLookup()</code>.
907      * <p>
908      * <b>Warning:</b> the {@link #getCookie} method and {@link #getLookup}
909      * method are ment to be interchangable - e.g. if you override one of them
910      * be sure to override also the other and try as much as possible to
911      * keep the same content in each of them. The default implementation tries
912      * to do that as much as possible.
913      *
914      * @return lookup representing this data object and its content
915      * @since 6.0
916      */

917     public Lookup getLookup() {
918         return getNodeDelegate().getLookup();
919     }
920     
921     /** When a request for a cookie is done on a DataShadow of this DataObject
922      * this methods gets called (by default) so the DataObject knows which
923      * DataShadow is asking and extract some information from the shadow itself.
924      * <P>
925      * Subclasses can override this method with better logic, but the default
926      * implementation just delegates to <code>getCookie (Class)</code>.
927      *
928      * @param clazz class to search for
929      * @param shadow the shadow for which is asking
930      * @return the cookie or <code>null</code>
931      *
932      * @since 1.16
933      */

934     protected <T extends Node.Cookie> T getCookie(DataShadow shadow, Class JavaDoc<T> clazz) {
935         return getCookie (clazz);
936     }
937
938     // =======================
939
// Serialization methods
940
//
941

942     /** The Serialization replacement for this object stores the primary file instead.
943      * @return a replacement
944     */

945     public Object JavaDoc writeReplace () {
946         return new Replace (this);
947     }
948
949
950     /** The default replace for the data object
951     */

952     private static final class Replace extends Object JavaDoc implements Serializable {
953         /** the primary file */
954         private FileObject fo;
955         /** the object to return */
956         private transient DataObject obj;
957
958         private static final long serialVersionUID =-627843044348243058L;
959         /** Constructor.
960         * @param obj the object to use
961         */

962         public Replace (DataObject obj) {
963             this.obj = obj;
964             this.fo = obj.getPrimaryFile ();
965         }
966
967         public Object JavaDoc readResolve () {
968             return obj;
969         }
970
971         /** Read method */
972         private void readObject (ObjectInputStream ois)
973         throws IOException, ClassNotFoundException JavaDoc {
974             ois.defaultReadObject ();
975             if (fo == null) {
976                 throw new java.io.FileNotFoundException JavaDoc ();
977             }
978             // DataObjectNotFoundException extends IOException:
979
obj = DataObject.find(fo);
980         }
981     }
982
983     /** Getter for a text from resource bundle.
984     */

985     static String JavaDoc getString (String JavaDoc name) {
986         return NbBundle.getMessage (DataObject.class, name);
987     }
988     
989     /** Interface for objects that can contain other data objects.
990      * For example DataFolder and DataShadow implement this interface
991      * to allow others to access the contained objects in uniform maner
992      */

993     public static interface Container extends Node.Cookie {
994         /** Name of property that holds children of this container. */
995         public static final String JavaDoc PROP_CHILDREN = "children"; // NOI18N
996

997         /** @return the array of contained objects
998          */

999         public DataObject[] getChildren ();
1000        
1001        /** Adds a listener.
1002         * @param l the listener
1003         */

1004        public void addPropertyChangeListener (PropertyChangeListener l);
1005        
1006        /** Removes property change listener.
1007         * @param l the listener
1008         */

1009        public void removePropertyChangeListener (PropertyChangeListener l);
1010    }
1011
1012    /** Registry of modified data objects.
1013     * The registry permits attaching of a change listener
1014    * to be informed when the count of modified objects changes.
1015    */

1016    public static final class Registry extends Object JavaDoc {
1017
1018        /** Private constructor */
1019        private Registry () {
1020        }
1021
1022        /** Add new listener to changes in the set of modified objects.
1023        * @param chl listener to add
1024        */

1025        public void addChangeListener (final ChangeListener chl) {
1026            modified.addChangeListener(chl);
1027        }
1028
1029        /** Remove a listener to changes in the set of modified objects.
1030        * @param chl listener to remove
1031        */

1032        public void removeChangeListener (final ChangeListener chl) {
1033            modified.removeChangeListener(chl);
1034        }
1035
1036        /** Get a set of modified data objects.
1037        * @return an unmodifiable set of data objects
1038        */

1039        public Set<DataObject> getModifiedSet() {
1040            return Collections.unmodifiableSet(syncModified);
1041        }
1042
1043        /** Get modified objects.
1044        * @return array of objects
1045        */

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        /** Set of listeners listening to changes to the set of modified objs */
1055        private Set<ChangeListener> listeners;
1056
1057        ModifiedRegistry() {}
1058
1059        /** Adds new listener.
1060        * @param chl new listener
1061        */

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        /** Removes listener from the listener list.
1068        * @param chl listener to remove
1069        */

1070        public final synchronized void removeChangeListener (final ChangeListener chl) {
1071            if (listeners == null) return;
1072            listeners.remove(chl);
1073        }
1074
1075        /***** overriding of methods which change content in order to notify
1076        * listeners about the content change */

1077        @Override JavaDoc
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 JavaDoc
1085        public boolean remove (Object JavaDoc o) {
1086            boolean result = super.remove(o);
1087            if (result) fireChangeEvent(new ChangeEvent(this));
1088            return result;
1089        }
1090
1091        /** Fires change event to all listeners.
1092        * @param che change event
1093        */

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            // fire on cloned list to prevent from modifications when firing
1101
for (ChangeListener l : clones) {
1102                l.stateChanged(che);
1103            }
1104        }
1105
1106    } // end of ModifiedRegistry inner class
1107

1108    /** A.N. - profiling shows that MultiLoader.checkFiles() is called too often
1109    * This method is part of the fix - empty for DataObject.
1110    */

1111    void recognizedByFolder() {
1112    }
1113    
1114    // This methods are called by DataObjectPool whenever the primary file
1115
// gets changed. The Pool listens on the whole FS thus reducing
1116
// the number of individual listeners created/registered.
1117
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            // We are interested only in assigned loader
1135
return;
1136        }
1137        FileObject f = fae.getFile();
1138        if (f != null) {
1139            String JavaDoc attrFromFO = (String JavaDoc)f.getAttribute(EA_ASSIGNED_LOADER);
1140            if (attrFromFO == null || (! attrFromFO.equals(getLoader().getClass().getName()))) {
1141                Set<FileObject> single = new HashSet<FileObject>(); // Collections.singleton is r/o, this must be writable
1142
single.add(f);
1143                if (!DataObjectPool.getPOOL().revalidate(single).isEmpty()) {
1144                    LOG.info("It was not possible to invalidate data object: " + this); // NOI18N
1145
} else {
1146                    // we need to refresh parent folder if it is there
1147
// this should be covered by DataLoaderPoolTest.testChangeIsAlsoReflectedInNodes
1148
FolderList.changedDataSystem (f.getParent());
1149                }
1150            }
1151        }
1152    }
1153    static final class CreateAction implements FileSystem.AtomicAction {
1154        public DataObject result;
1155        private String JavaDoc name;
1156        private DataFolder f;
1157        private DataObject orig;
1158        private Map<String JavaDoc, ? extends Object JavaDoc> param;
1159        
1160        private static ThreadLocal JavaDoc<CreateAction> CURRENT = new ThreadLocal JavaDoc<CreateAction>();
1161        
1162        public CreateAction(DataObject orig, DataFolder f, String JavaDoc name, Map<String JavaDoc, ? extends Object JavaDoc> 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 JavaDoc,Object JavaDoc> findParameters(String JavaDoc name) {
1180            CreateAction c = CURRENT.get();
1181            if (c == null) {
1182                return Collections.emptyMap();
1183            }
1184            HashMap<String JavaDoc,Object JavaDoc> all = new HashMap<String JavaDoc,Object JavaDoc>();
1185            for (CreateFromTemplateAttributesProvider provider : Lookup.getDefault().lookupAll(CreateFromTemplateAttributesProvider.class)) {
1186                Map<String JavaDoc,? extends Object JavaDoc> map = provider.attributesFor(c.orig, c.f, c.name);
1187                if (map != null) {
1188                    for (Map.Entry<String JavaDoc,? extends Object JavaDoc> e : map.entrySet()) {
1189                        all.put(e.getKey(), e.getValue());
1190                    }
1191                }
1192            }
1193            if (c.param != null) {
1194                for (Map.Entry<String JavaDoc,? extends Object JavaDoc> e : c.param.entrySet()) {
1195                    all.put(e.getKey(), e.getValue());
1196                }
1197            }
1198            
1199            if (!all.containsKey("name") && name != null) { // NOI18N
1200
all.put("name", name); // NOI18N
1201
}
1202            if (!all.containsKey("user")) { // NOI18N
1203
all.put("user", System.getProperty("user.name")); // NOI18N
1204
}
1205            Date d = new Date();
1206            if (!all.containsKey("date")) { // NOI18N
1207
all.put("date", DateFormat.getDateInstance().format(d)); // NOI18N
1208
}
1209            if (!all.containsKey("time")) { // NOI18N
1210
all.put("time", DateFormat.getTimeInstance().format(d)); // NOI18N
1211
}
1212            
1213            return Collections.unmodifiableMap(all);
1214        }
1215        
1216    } // end of CreateAction
1217
}
1218
Popular Tags