KickJava   Java API By Example, From Geeks To Geeks.

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


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.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 /** A data object whose only purpose is to supply <code>InstanceCookie</code>.
39 * The instances are created by default instantiation; the name of the class
40 * to instantiate is stored on disk, typically right in the file name.
41 * <p>This data object is generally used to configure menus and toolbars,
42 * though it could be used in any situation requiring instances to be present in
43 * a folder; for example, anything using {@link FolderInstance}.
44 * <p>Typical instance classes are subclasses of {@link SystemAction} to make
45 * menu items or toolbar buttons; {@link javax.swing.JSeparator} for a menu
46 * separator; or {@link javax.swing.JToolBar.Separator} for a toolbar
47 * separator.
48 * <p>Use {@link #create} and {@link #remove} to make the objects.
49 * Better yet, use an XML filesystem to install them declaratively.
50 * <p>
51 * Instance data object by default recognizes all files with <tt>.instance</tt>
52 * suffix. Such file can have associated optional file attributes:
53 * <dl>
54 * <!-- <dt><tt>instanceClass</tt> <dd><code>String</code> identifing class of created instance
55 * (otherwise class name is derived from file name). -->
56 * <dt><tt>instanceCreate</tt> <dd>instantionalized <code>Object</code> (e.g. created by
57 * <tt>methodvalue</tt> at XML filesystem)
58 * <dt><tt>instanceOf</tt> <dd><code>String</code> that is tokenized at ':', ',', ';' and
59 * whitespace boundaries. Resulting tokens represent class names that created
60 * instance is <code>instanceof</code>. Utilizing it may improve performance.
61 * </dl>
62 * (optional file attributes documented since 3.34).
63 *
64 * @author Ian Formanek
65 */

66 public class InstanceDataObject extends MultiDataObject implements InstanceCookie.Of {
67     /** generated Serialized Version UID */
68     static final long serialVersionUID = -6134784731744777123L;
69
70     private static final String JavaDoc EA_INSTANCE_CLASS = "instanceClass"; // NOI18N
71
private static final String JavaDoc EA_INSTANCE_CREATE = "instanceCreate"; // NOI18N
72
private static final String JavaDoc EA_INSTANCE_OF = "instanceOf"; // NOI18N
73
/** data object name cached in the attribute to prevent instance creation when
74      its InstanceNode is displayed. */

75     static final String JavaDoc EA_NAME = "name"; // NOI18N
76
/** if an instance is modified, what is the delay before it is saved? */
77     private static final int SAVE_DELAY = 2000;
78
79     // XXX #27494 Please changes to these two fields apply also into
80
// core/naming/src/org/netbeans/core/naming/Utils class.
81
/** opening symbol */
82     private static final char OPEN = '[';
83     /** closing symbol */
84     private static final char CLOSE = ']';
85
86     /** File extension for instance data objects. */
87     public static final String JavaDoc INSTANCE = "instance"; // NOI18N
88

89     /** File extension for serialized files. */
90     static final String JavaDoc SER_EXT = "ser"; // NOI18N
91
/** File extension for xml settings. */
92     static final String JavaDoc XML_EXT = "settings"; //NOI18N
93

94     /** optional property file key for icon resource */
95     private static final String JavaDoc ICON_NAME = "icon"; // NOI18N
96

97     /** the object that handles instance cookie manipulation */
98     private Ser ser;
99
100     /** saving task status */
101     private boolean savingCanceled = false;
102     private String JavaDoc nameCache;
103
104
105     private static final RequestProcessor PROCESSOR = new RequestProcessor ("Instance processor"); // NOI18N
106
private static final Logger err = Logger.getLogger("org.openide.loaders.InstanceDataObject"); // NOI18N
107

108     /** Create a new instance.
109     * Do not use this to make instances; use {@link #create}.
110     * @param pf primary file object for this data object
111     * @param loader the loader
112     * @throws DataObjectExistsException if it already exists
113     */

114     public InstanceDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException {
115         super (pf, loader);
116
117         if (pf.hasExt (SER_EXT)) { // NOI18N
118
// only if we have ser extension the Ser (InstanceCookie)
119
// should be accessible
120
ser = new Ser (this);
121             getCookieSet ().add (ser);
122             // I.e. never create one.
123
} else if (!pf.hasExt(XML_EXT)) {
124             // otherwise we implement just the InstanceCookie directly
125
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             // ok, ignore no editor support added
134
}
135     }
136
137     /** used for synchronization instead of the IDO object */
138     private final Object JavaDoc IDO_LOCK = new Object JavaDoc();
139     /** Get the lock of the IDO object. Do not synchronize on the IDO object
140      * directly to prevent deadlocks.
141      */

142     private Object JavaDoc getLock() {
143         return IDO_LOCK;
144     }
145
146     /** Finds file object of specified name in a given folder.
147     * @param folder the folder where search
148     * @param name the name to give to the object (can be <code>null</code> if no special name besides the class name is needed)
149     * @param className the name of the class the new object should provide an instance of
150     * @return the found file object or null if it does not exist
151     */

152     private static FileObject findFO (DataFolder folder, String JavaDoc name, String JavaDoc className) {
153         FileObject fo = folder.getPrimaryFile ();
154         String JavaDoc 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     /** get data object name from specified file object */
174     private static String JavaDoc getName(FileObject fo) {
175         String JavaDoc superName = (String JavaDoc) 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     /** Finds instance of specified name in a given folder.
189     * @param folder the folder to create the instance data object in
190     * @param name the name to give to the object (can be <code>null</code> if no special name besides the class name is needed)
191     * @param className the name of the class the new object should provide an instance of
192     * @return the found instance data object or null if it does not exist
193     */

194     public static InstanceDataObject find (DataFolder folder, String JavaDoc name, String JavaDoc 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     /** Finds instance of specified name in a given folder.
206     * @param folder the folder to create the instance data object in
207     * @param name the name to give to the object (can be <code>null</code> if no special name besides the class name is needed)
208     * @param clazz the class to create instance for (see class header for details)
209     * @return the found instance data object or null if it does not exist
210     */

211     public static InstanceDataObject find(DataFolder folder, String JavaDoc name, Class JavaDoc<?> clazz) {
212         return find (folder, name, clazz.getName ());
213     }
214
215     /** Create a new <code>InstanceDataObject</code> in a given folder. If object with specified name already exists, it is returned.
216     * You should specify the name if there is a chance another file of the same
217     * instance class already exists in the folder; or just to provide a more
218     * descriptive name, which will appear in the Explorer for example.
219     * <p><strong>Note:</strong> use of XML layers to install instances is generally preferred.
220     * @param folder the folder to create the instance data object in
221     * @param name the name to give to the object (can be <code>null</code> if no special name besides the class name is needed)
222     * but name cannot be empty
223     * @param className the name of the class the new object should provide an instance of (see class header for details)
224     * @return the newly created or existing instance data object
225     * @exception IOException if the file cannot be created
226     */

227     public static InstanceDataObject create (DataFolder folder, final String JavaDoc name, final String JavaDoc className) throws IOException {
228         final FileObject fo = folder.getPrimaryFile ();
229         if (name != null && name.length() == 0) {
230             throw new IOException("name cannot be empty"); // NOI18N
231
}
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 JavaDoc 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     /** Create a new <code>InstanceDataObject</code> in a given folder. If object with specified name already exists, it is returned.
255     * You should specify the name if there is a chance another file of the same
256     * instance class already exists in the folder; or just to provide a more
257     * descriptive name, which will appear in the Explorer for example.
258     * <p><strong>Note:</strong> use of XML layers to install instances is generally preferred.
259     * @param folder the folder to create the instance data object in
260     * @param name the name to give to the object (can be <code>null</code> if no special name besides the class name is needed)
261     * @param clazz the class to create instance for (see class header for details)
262     * @return the newly created or existing instance data object
263     * @exception IOException if the file cannot be created
264     */

265     public static InstanceDataObject create(DataFolder folder, String JavaDoc name, Class JavaDoc<?> clazz) throws IOException {
266         return create (folder, name, clazz.getName ());
267     }
268
269     /** Create a new <code>InstanceDataObject</code> containing settings
270     * in a given folder. If object with specified name already exists, it is returned.
271     * If the module info is <code>null</code> then the origin module info
272     * of an instance class is tried to find out.
273     * <p><strong>Note:</strong> use of XML layers to install instances is generally preferred.
274     * @param folder the folder to create the instance data object in
275     * @param name the name to give to the object (can be <code>null</code> if no special name besides the class name is needed)
276     * but name cannot be empty
277     * @param instance the serializable instance
278     * @param info the module info describing the settings provenance (can be <code>null</code>)
279     * @return the newly created or existing instance data object
280     * @exception IOException if the file cannot be created
281     * @since 1.28
282     */

283     public static InstanceDataObject create (DataFolder folder, String JavaDoc name,
284         Object JavaDoc instance, ModuleInfo info) throws IOException {
285         return create(folder, name, instance, info, false);
286     }
287
288     /** Create a new <code>InstanceDataObject</code> containing settings
289     * in a given folder.
290     * If the module info is <code>null</code> then the origin module info
291     * of an instance class is tried to find out.
292     * <p><strong>Note:</strong> use of XML layers to install instances is generally preferred.
293     * @param folder the folder to create the instance data object in
294     * @param name the name to give to the object (can be <code>null</code> if no special name besides the class name is needed)
295     * but name cannot be empty
296     * @param instance the serializable instance
297     * @param info the module info describing the settings provenance (can be <code>null</code>)
298     * @param create <code>true</code> - always create new file; <code>false</code>
299     * - store to existing file if exist
300     * @return the newly created or existing instance data object
301     * @exception IOException if the file cannot be created
302     * @since 2.9
303     */

304     public static InstanceDataObject create (DataFolder folder, String JavaDoc name,
305         Object JavaDoc instance, ModuleInfo info, boolean create) throws IOException {
306         if (name != null && name.length() == 0) {
307             throw new IOException("name cannot be empty"); // NOI18N
308
}
309         return Creator.createInstanceDataObject (folder, name, instance, info, create);
310
311     }
312
313     private static InstanceDataObject storeSettings (DataFolder df, String JavaDoc name, Object JavaDoc obj, ModuleInfo mi)
314     throws IOException {
315         FileObject fo = df.getPrimaryFile ();
316         FileObject newFile = fo.getFileObject (name, XML_EXT);
317         String JavaDoc 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); // NOI18N
324
final ByteArrayOutputStream buf = storeThroughConvertor(obj, new FileObjectContext(fo, name));
325                 System.setProperty("InstanceDataObject.current.file", ""); // NOI18N
326
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             // attachToConvertor will store the object
341
ido.attachToConvertor(obj, attachWithSave);
342         } finally {
343             createdIDOs.remove(fullname);
344         }
345         return ido;
346     }
347
348     /** Remove an existing instance data object.
349     * If you have the exact file name, just call {@link DataObject#delete};
350     * this method lets you delete an instance you do not have an exact record
351     * of the file name for, based on the same information used to create it.
352     * <p><strong>Note:</strong> use of XML layers to install instances is generally preferred.
353     * @param folder the folder to remove the file from
354     * @param name the name of the instance (can be <code>null</code>)
355     * @param className the name of class the object referred to (see class header for details)
356     * @return <code>true</code> if the instance was succesfully removed, <code>false</code> if not
357     */

358     public static boolean remove (DataFolder folder, String JavaDoc name,
359                                   String JavaDoc className) {
360         FileLock lock = null;
361         try {
362             FileObject fileToRemove = findFO (folder, name, className);
363             if (fileToRemove == null) // file not found
364
return false;
365             lock = fileToRemove.lock();
366             fileToRemove.delete(lock);
367         } catch (IOException exc) {
368             // something is bad, instance wasn't removed
369
return false;
370         } finally {
371             if (lock != null)
372                 lock.releaseLock();
373         }
374         return true;
375     }
376
377     /** Remove an existing instance data object.
378     * If you have the exact file name, just call {@link DataObject#delete};
379     * this method lets you delete an instance you do not have an exact record
380     * of the file name for, based on the same information used to create it.
381     * <p><strong>Note:</strong> use of XML layers to install instances is generally preferred.
382     * @param folder the folder to remove the file from
383     * @param name the name of the instance (can be <code>null</code>)
384     * @param clazz the class the object referred to (see class header for details)
385     * @return <code>true</code> if the instance was succesfully removed, <code>false</code> if not
386     */

387     public static boolean remove(DataFolder folder, String JavaDoc name, Class JavaDoc<?> clazz) {
388         return remove (folder, name, clazz.getName ());
389     }
390
391     /* Help context for this object.
392     * @return help context
393     */

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     /* Provides node that should represent this data object. When a node for representation
403     * in a parent is requested by a call to getNode (parent) it is the exact copy of this node
404     * with only parent changed. This implementation creates instance
405     * <CODE>DataNode</CODE>.
406     * <P>
407     * This method is called only once.
408     *
409     * @return the node representation for this data object
410     * @see DataNode
411     */

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     /** allows to swap original node */
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 JavaDoc() {
429                     public void run() {
430                         changeOriginal(createNodeDelegateImpl(), true);
431                     }
432                 });
433         }
434     }
435
436     /** create node delegate */
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 lookup does not contain any InstanceCookie then the object
449
// is considered as unregognized
450
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         // Instances of Node or Node.Handle should be used as is.
458
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 JavaDoc ex) {
469             err.log(Level.WARNING, null, ex);
470         }
471
472         return new InstanceNode (this);
473     }
474
475     /** Node presents IDO as unregonized setting object which can be just deleted. */
476     private final class UnrecognizedSettingNode extends AbstractNode {
477         public UnrecognizedSettingNode() {
478             super(Children.LEAF);
479             setName(NbBundle.getMessage(InstanceDataObject.class, "LBL_BrokenSettings")); //NOI18N
480
setIconBaseWithExtension("org/openide/loaders/instanceBroken.gif"); //NOI18N
481
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     /** A filter which ensures that when some-random-class-impl-Node.instance
506      * is created, its node delegate gives itself as the cookie for DataObject,
507      * and not some other unrelated data object. E.g. Services/.../TemplatesNode.instance
508      * vs. Templates/ and similar. See DataNodeTest unit test.
509      */

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         // If this node is used as the root of a new Explorer window etc.,
519
// just save the real underlying node; no need to make it a CAF later.
520
public Node.Handle getHandle() {
521             return getOriginal().getHandle();
522         }
523         // #17920: Index cookie works only when equality works
524
public boolean equals(Object JavaDoc 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     /** delegate .getCookie to Environment.Provider */
533     private <T> T getCookieFromEP(Class JavaDoc<T> clazz) {
534         //updateLookup(false);
535
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     /* Serve up editor cookies where requested. */
548     @Override JavaDoc
549     public <T extends Node.Cookie> T getCookie(Class JavaDoc<T> clazz) {
550         T supe = null;
551         if (getPrimaryFile().hasExt(XML_EXT)) {
552             // #24683 fix: do not return any cookie until the .settings file is written
553
// successfully; PROP_COOKIE is fired when cookies are available.
554
String JavaDoc filename = getPrimaryFile().getPath();
555             if (createdIDOs.contains(filename)) return null;
556
557             Object JavaDoc 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     /** Finds delegate instance cookie/if provided in cookie set.
637     * @return instance cookie or null
638     */

639     private InstanceCookie.Of delegateIC () {
640         //return ser;
641
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     /* The name of the bean for this file or null if the class name is not encoded
651     * in the file name and rather the CLASS_NAME property from the file content should be used.
652     *
653     * @return the name for the instance or null if the class name is not defined in the name
654     */

655     public String JavaDoc instanceName () {
656         InstanceCookie delegateIC = delegateIC ();
657         if (delegateIC == null) return this.getName();
658         return delegateIC.instanceName ();
659     }
660
661     /* The class of the instance represented by this cookie.
662     * Can be used to test whether the instance is of valid
663     * class before it is created.
664     *
665     * @return the class of the instance
666     * @exception IOException an I/O error occured
667     * @exception ClassNotFoundException the class has not been found
668     */

669     public Class JavaDoc<?> instanceClass ()
670     throws IOException, ClassNotFoundException JavaDoc {
671         InstanceCookie delegateIC = delegateIC ();
672         if (delegateIC == null) return this.getClass();
673         return delegateIC.instanceClass ();
674     }
675
676     /** Query if this instance can create object of given type.
677     * @param type the type to create
678     * @return true or false
679     */

680     public boolean instanceOf (Class JavaDoc<?> type) {
681         InstanceCookie.Of delegateIC = delegateIC ();
682         if (delegateIC == null) return type.isAssignableFrom(this.getClass());
683         return delegateIC.instanceOf (type);
684     }
685
686     /*
687     * @return an object to work with
688     * @exception IOException an I/O error occured
689     * @exception ClassNotFoundException the class has not been found
690     */

691     public Object JavaDoc instanceCreate ()
692     throws IOException, ClassNotFoundException JavaDoc {
693         InstanceCookie delegateIC = delegateIC ();
694         if (delegateIC == null) return this;
695         return delegateIC.instanceCreate ();
696     }
697     
698     /** Checks whether the instance was created by this object.
699      */

700     final boolean creatorOf (Object JavaDoc inst) {
701         InstanceCookie delegateIC = delegateIC ();
702         if (delegateIC instanceof Ser) {
703             return ((Ser)delegateIC).creatorOf (inst);
704         }
705         return false;
706     }
707
708     /* Overriden to return only first part till the bracket */
709     public String JavaDoc getName () {
710         if (nameCache != null) {
711             return nameCache;
712         }
713         
714         String JavaDoc superName = (String JavaDoc) 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     /** Make sure people stop using this syntax eventually.
733      * It is better to use the file attribute, not least because some VMs
734      * do not much like [] in file names (OpenVMS had problems at one point, e.g.).
735      */

736     private static void warnAboutBrackets(FileObject fo) {
737         if (warnedAboutBrackets.add(fo)) {
738             err.warning("Use of [] in " + fo + " is deprecated."); // NOI18N
739
err.warning("(Please use the string-valued file attribute instanceClass instead.)"); // NOI18N
740
}
741     }
742
743     // [PENDING] probably setName also needs to be overridden!
744
/* Renames all entries and changes their files to new ones.
745      */

746     protected FileObject handleRename (String JavaDoc name) throws IOException {
747         FileObject fo = getPrimaryFile();
748         fo.setAttribute(EA_NAME, name);
749         return fo;
750     }
751
752     // SEE ALSO org.netbeans.core.windows.util.WindowUtils FOR COPIED IMPL OF escape/unescape:
753

754     // XXX #27494 Please changes to this method apply also into
755
// core/naming/src/org/netbeans/core/naming/Utils class.
756

757     /** Hex-escapes anything potentially nasty in some text.
758      * Package-private for the benefit of the test suite.
759      */

760     static String JavaDoc escape (String JavaDoc text) {
761         boolean spacenasty = text.startsWith(" ") || text.endsWith(" ") || text.indexOf(" ") != -1; // NOI18N
762
int len = text.length ();
763         StringBuffer JavaDoc escaped = new StringBuffer JavaDoc (len);
764         for (int i = 0; i < len; i++) {
765             char c = text.charAt (i);
766             // For some reason Windoze throws IOException if angle brackets in filename...
767
if (c == '/' || c == ':' || c == '\\' || c == OPEN || c == CLOSE || c == '<' || c == '>' ||
768                     // ...and also for some other chars (#16479):
769
c == '?' || c == '*' || c == '|' ||
770                     (c == ' ' && spacenasty) ||
771                     c == '.' || c == '"' || c < '\u0020' || c > '\u007E' || c == '#') {
772                 // Hex escape.
773
escaped.append ('#');
774                 String JavaDoc 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     /** Removes hex escapes and regenerates displayable Unicode. */
787     static String JavaDoc unescape (String JavaDoc text) {
788         int len = text.length ();
789         StringBuffer JavaDoc unesc = new StringBuffer JavaDoc (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); // NOI18N
795
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 JavaDoc (hex), 16));
801                 } catch (NumberFormatException JavaDoc 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     // XXX #27494 Please changes to this field apply also into
813
// core/naming/src/org/netbeans/core/naming/Utils class.
814
private final static int MAX_FILENAME_LENGTH = 50;
815
816     // XXX #27494 Please changes to this method apply also into
817
// core/naming/src/org/netbeans/core/naming/Utils class.
818
/** escape a filename and map it to the name with max length MAX_FILENAME_LENGTH
819      * @see issue #17186
820      */

821     static String JavaDoc escapeAndCut (String JavaDoc name) {
822         int maxLen = MAX_FILENAME_LENGTH;
823
824         String JavaDoc ename = escape(name);
825         if (ename.length() <= maxLen) return ename;
826         String JavaDoc hash = Integer.toHexString(ename.hashCode()).toUpperCase(Locale.ENGLISH);
827         maxLen = (maxLen > hash.length()) ? (maxLen-hash.length()) / 2 :1;
828         String JavaDoc start = ename.substring(0, maxLen);
829         if (start.endsWith("#")) {
830             //Strip ending # so that next hexadecimal (hash) sequence is not treated as escape sequence
831
//by unescape.
832
start = start.substring(0, start.length() - 1);
833         }
834         String JavaDoc end = ename.substring(ename.length() - maxLen);
835
836         return start + hash + end;
837     }
838
839     /** schedule task to save the instance */
840     final void scheduleSave () {
841         // just for .ser files
842
if (isSavingCanceled() || !getPrimaryFile().hasExt(SER_EXT)) return;
843         doFileLock();
844         ser.getSaveTask().schedule(SAVE_DELAY);
845     }
846
847     private FileLock fileLock;
848
849     /** try to lock the primary file; may return <code>null</code> */
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     /** release the file lock if any was taken */
864     private void relaseFileLock() {
865         synchronized (getLock()) {
866             if (fileLock == null) return;
867             fileLock.releaseLock();
868             fileLock = null;
869         }
870     }
871     /* Creates new object from template.
872     * @exception IOException
873     */

874     protected DataObject handleCreateFromTemplate (
875         DataFolder df, String JavaDoc name
876     ) throws IOException {
877         try {
878             if (getPrimaryFile().hasExt(XML_EXT)) {
879                 InstanceCookie ic = (InstanceCookie)this.getCookie(InstanceCookie.class);
880                 Object JavaDoc obj = ic.instanceCreate();
881
882                 DataObject d = createSettingsFile(df, name, obj);
883                 // reset template instance to null
884
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 JavaDoc obj = ic.instanceCreate();
890
891                 return DataObject.find( createSerFile( df, name, obj ) );
892             }
893         } catch (ClassNotFoundException JavaDoc ex) {
894             err.log(Level.WARNING, null, ex);
895         }
896
897         return super.handleCreateFromTemplate(df, name);
898     }
899
900     /* Copy a service sanely. For settings and serializable beans, special
901      * methods are used to write out the resulting files, and the name to
902      * use is taken from the *display name* of the current file, as this is
903      * what the user is accustomed to seeing (for ServiceType's especially).
904      * @see <a HREF="http://www.netbeans.org/issues/show_bug.cgi?id=16278">Issue #16278</a>
905      */

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 JavaDoc 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 JavaDoc obj = ic.instanceCreate();
923                         return DataObject.find(createSerFile(
924                             df, getNodeDelegate().getDisplayName(), obj));
925                     }
926                 }
927             } catch (ClassNotFoundException JavaDoc ex) {
928                 err.log(Level.WARNING, null, ex);
929             }
930         }
931         return super.handleCopy(df);
932     }
933
934     /** Is the saving task already canceled? If yes do not schedule it again. */
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                 //ignore
955
}
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             // ignore
981
}
982         super.handleDelete();
983     }
984
985     private InstanceDataObject createSettingsFile (DataFolder df, String JavaDoc name, Object JavaDoc obj)
986     throws IOException {
987         boolean isServiceType = false;
988
989         String JavaDoc filename;
990         // find name for new service type
991
if (obj instanceof ServiceType) {
992             isServiceType = true;
993             ServiceType sr = (ServiceType) obj;
994             name = name == null? sr.getName(): name;
995             String JavaDoc 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 JavaDoc(name.length() + 2).
999                     append(name).append('_').append(i).toString();
1000            }
1001            if (!stName.equals(sr.getName())) {
1002                // Do not modify the original!
1003
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 JavaDoc name, Object JavaDoc 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    /** Support for serialized objects.
1057     */

1058    private static final class Ser extends InstanceSupport
1059    implements Runnable JavaDoc {
1060        /** the reference to the bean, so it is created just once when used */
1061        private Reference<Object JavaDoc> bean = new SoftReference<Object JavaDoc>(null);
1062        /** last time the bean was read from a file */
1063        private long saveTime;
1064
1065        /** Custom class loader */
1066        private ClassLoader JavaDoc customClassLoader;
1067        private InstanceDataObject dobj;
1068
1069        /** @param dobj IDO containing the serialized instance */
1070        public Ser (InstanceDataObject dobj) {
1071            super (dobj.getPrimaryEntry());
1072            customClassLoader = null;
1073            this.dobj = dobj;
1074        }
1075
1076        public String JavaDoc instanceName () {
1077            // try the life object if any
1078
FileObject fo = entry ().getFile ();
1079            if (fo.lastModified ().getTime () <= saveTime) {
1080                Object JavaDoc 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        /** get class name from specified file object*/
1093        private static String JavaDoc getClassName(FileObject fo) {
1094            // first of all try "instanceClass" property of the primary file
1095
Object JavaDoc attr = fo.getAttribute (EA_INSTANCE_CLASS);
1096            if (attr instanceof String JavaDoc) {
1097                return Utilities.translate((String JavaDoc) attr);
1098            } else if (attr != null) {
1099                err.warning(
1100                    "instanceClass was a " + attr.getClass().getName()); // NOI18N
1101
}
1102
1103            attr = fo.getAttribute (EA_INSTANCE_CREATE);
1104            if (attr != null) {
1105                return attr.getClass().getName();
1106            }
1107
1108            // otherwise extract the name from the filename
1109
String JavaDoc 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            // take only a part of the string
1122
if (first < last) {
1123                name = name.substring (first, last);
1124            }
1125
1126            name = name.replace ('-', '.');
1127            name = Utilities.translate(name);
1128
1129            //System.out.println ("Original: " + getPrimaryFile ().getName () + " new one: " + name); // NOI18N
1130
return name;
1131        }
1132
1133        /** Uses cache to remember list of classes to them this object is
1134        * assignable.
1135        */

1136        public Class JavaDoc instanceClass() throws IOException, ClassNotFoundException JavaDoc {
1137            return super.instanceClass (customClassLoader);
1138        }
1139
1140        /** Uses the cache to answer this question without loading the class itself, if the
1141        * cache exists.
1142        */

1143        public boolean instanceOf (Class JavaDoc type) {
1144            // try the life object if any
1145
FileObject fo = entry ().getFile ();
1146            if (fo.lastModified ().getTime () <= saveTime) {
1147                Object JavaDoc o = bean.get ();
1148                if (o != null) {
1149                    return type.isInstance (o);
1150                }
1151            }
1152
1153            // else do checking of classes
1154

1155
1156            // null means no cache exists
1157
Boolean JavaDoc res = inListOfClasses (type, entry ().getFile ());
1158            if (res == null) {
1159                // uses instanceClass and then assignableFrom
1160
return super.instanceOf (type);
1161            }
1162            return res.booleanValue ();
1163        }
1164
1165
1166        public Object JavaDoc instanceCreate () throws IOException, ClassNotFoundException JavaDoc {
1167            FileObject fo = entry ().getFile ();
1168
1169
1170            Object JavaDoc 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                // try to ask for instance creation attribute
1187
o = fo.getAttribute (EA_INSTANCE_CREATE);
1188            }
1189
1190            if (o == null) {
1191                // try super method
1192
o = super.instanceCreate ();
1193            }
1194
1195            // remember the created value
1196
bean = new SoftReference<Object JavaDoc>(o);
1197            return o;
1198        }
1199
1200        /** Checks whether the instance was created by this object.
1201         */

1202        final boolean creatorOf (Object JavaDoc 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        /** Saves the bean to disk.
1219         */

1220        private void runImpl () {
1221            Object JavaDoc bean = this.bean.get ();
1222            if (bean == null) {
1223                // nothing to save
1224
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                    // avoid bean reloading
1236
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", // NOI18N
1243
instanceName (), entry ().getFile ().getPath()
1244                ), ex);
1245            }
1246
1247        }
1248
1249        /** Check whether a given class is in list of all classes assigned to fo.
1250        * @param type type to test
1251        * @param fo file object to check
1252        * @return true if the class is in the list of objects
1253        */

1254        private static Boolean JavaDoc inListOfClasses (Class JavaDoc type, FileObject fo) {
1255            Object JavaDoc obj = fo.getAttribute (EA_INSTANCE_OF);
1256            if (obj instanceof String JavaDoc) {
1257                String JavaDoc typeName = type.getName ();
1258                StringTokenizer tok = new StringTokenizer ((String JavaDoc)obj, "\n\t ,;:"); // NOI18N
1259
while (tok.hasMoreTokens ()) {
1260                    String JavaDoc t = tok.nextToken ().trim();
1261                    if (typeName.equals (t)) {
1262                        // we know this class is in the list of otherclasses
1263
return Boolean.TRUE;
1264                    }
1265                }
1266
1267                return Boolean.FALSE;
1268            } else if (obj != null) {
1269                err.warning("instanceOf was a " + obj.getClass().getName()); // NOI18N
1270
}
1271            // means no cache exists
1272
return null;
1273        }
1274
1275        final void setCustomClassLoader(ClassLoader JavaDoc cl) {
1276            this.customClassLoader = cl;
1277        }
1278
1279        /** save task */
1280        private RequestProcessor.Task task;
1281
1282        /** return the instance save task */
1283        public RequestProcessor.Task getSaveTask() {
1284            if (task == null) {
1285                task = PROCESSOR.create(this);
1286            }
1287            return task;
1288        }
1289
1290        /** save task is running */
1291        private boolean saving = false;
1292
1293        public boolean isSaving() {
1294            return saving;
1295        }
1296
1297    } // end of Ser
1298

1299    final void setCustomClassLoader(ClassLoader JavaDoc cl) {
1300        if (ser instanceof Ser)
1301            ((Ser) ser).setCustomClassLoader(cl);
1302    }
1303
1304    /** Support for creating instances allowing identify the origin of file events
1305     * fired as a consequence of this creating.
1306     * Not thread safe.
1307     */

1308    private static class Creator implements FileSystem.AtomicAction {
1309        private ModuleInfo mi = null;
1310        private DataFolder folder = null;
1311        private Object JavaDoc instance = null;
1312        private String JavaDoc 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 JavaDoc filename = name;
1325            if (filename == null) {
1326                filename = instance.getClass().getName().replace ('.', '-');
1327                filename = FileUtil.findFreeFileName(fo, filename, XML_EXT);
1328            } else {
1329                String JavaDoc escapedFileName = escape(filename);
1330                // do not cut if such file already exist
1331
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 /*|| (newFile == null && Utilities.isWindows()) */) {
1340                    filename = FileUtil.findFreeFileName(fo, filename, XML_EXT);
1341                }
1342            }
1343
1344            result = storeSettings(folder, filename, instance, mi);
1345        }
1346
1347        /** see InstanceDataObject.create */
1348        public static InstanceDataObject createInstanceDataObject (
1349        DataFolder folder, String JavaDoc name, Object JavaDoc 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        /** is file event originated by this Creator? */
1369        public static boolean isFiredFromMe (FileEvent fe) {
1370            return fe.firedFrom(me);
1371        }
1372    }
1373
1374    /** store object to strem using convertor */
1375    private static ByteArrayOutputStream storeThroughConvertor(Object JavaDoc inst, FileObjectContext ctx) throws IOException {
1376        FileObject fo = resolveConvertor(inst);
1377        Object JavaDoc convertor = fo.getAttribute("settings.convertor"); // NOI18N
1378
if (convertor == null) throw new IOException("missing attribute settings.convertor"); // NOI18N
1379
ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
1380        Writer w = new OutputStreamWriter(b, "UTF-8"); // NOI18N
1381
convertorWriteMethod(convertor, new WriterProvider(w, ctx), inst);
1382        w.close();
1383        return b;
1384    }
1385
1386    /** reflection for void write (java.io.Writer w, Objectinst) method */
1387    private static void convertorWriteMethod(Object JavaDoc convertor, Writer w, Object JavaDoc inst) throws IOException {
1388        Throwable JavaDoc e = null;
1389        try {
1390            Method method = convertor.getClass().getMethod(
1391                "write", // NOI18N
1392
new Class JavaDoc[] {Writer.class, Object JavaDoc.class});
1393            method.setAccessible(true);
1394            method.invoke(convertor, new Object JavaDoc[] {w, inst});
1395        } catch (NoSuchMethodException JavaDoc ex) {
1396            e = ex;
1397        } catch (IllegalAccessException JavaDoc 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); // NOI18N
1405
}
1406    }
1407
1408    /** path where to find convertor/provider definition */
1409    private final static String JavaDoc EA_PROVIDER_PATH = "settings.providerPath"; // NOI18N
1410
private static final String JavaDoc EA_SUBCLASSES = "settings.subclasses"; // NOI18N
1411

1412    /** look up appropriate convertor according to obj */
1413    private static FileObject resolveConvertor(Object JavaDoc obj) throws IOException {
1414        String JavaDoc prefix = "xml/memory"; //NOI18N
1415
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()); //NOI18N
1419

1420        Class JavaDoc clazz = obj.getClass();
1421        Class JavaDoc c = clazz;
1422        while (c != null) {
1423            String JavaDoc className = c.getName();
1424            String JavaDoc convertorPath = new StringBuffer JavaDoc(200).append(prefix).append('/').
1425                    append(className.replace('.', '/')).toString(); // NOI18N
1426
FileObject fo = sfs.findResource(convertorPath);
1427            if (fo != null) {
1428                String JavaDoc providerPath = (String JavaDoc) fo.getAttribute(EA_PROVIDER_PATH);
1429                if (providerPath == null) {
1430                    c = c.getSuperclass();
1431                    continue;
1432                }
1433                if (c.equals(clazz) || Object JavaDoc.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); // NOI18N
1437
} else {
1438                        return ret;
1439                    }
1440                } else {
1441                    // check the special subclasses attribute
1442
Object JavaDoc inheritAttribute = fo.getAttribute(EA_SUBCLASSES);
1443                    if (inheritAttribute instanceof Boolean JavaDoc) {
1444                        boolean subclasses = ((Boolean JavaDoc)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); // NOI18N
1449
} 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); //NOI18N
1459
}
1460
1461    private void attachToConvertor(Object JavaDoc obj) throws IOException {
1462        attachToConvertor (obj, false);
1463    }
1464
1465    /** propagate instance to convertor; obj can be null */
1466    private void attachToConvertor(Object JavaDoc obj, boolean save) throws IOException {
1467        // InstanceCookie subclass has to implement
1468
// void setInstance(Object inst)
1469
Object JavaDoc ic = getCookiesLookup().lookup(InstanceCookie.class);
1470        if (ic == null) {
1471            throw new IllegalStateException JavaDoc(
1472                "Trying to store object " + obj // NOI18N
1473
+ " which most probably belongs to already disabled module!");// NOI18N
1474
}
1475        convertorSetInstanceMethod(ic, obj, save);
1476    }
1477
1478    /** reflection for void setInstance(Object inst) */
1479    private static void convertorSetInstanceMethod(Object JavaDoc convertor, Object JavaDoc inst, boolean save) throws IOException {
1480        Exception JavaDoc e = null;
1481        try {
1482            Method method = convertor.getClass().getMethod(
1483                "setInstance", // NOI18N
1484
new Class JavaDoc[] {Object JavaDoc.class, Boolean.TYPE});
1485            method.setAccessible(true);
1486            method.invoke(convertor, new Object JavaDoc[] {inst,
1487            (save ? Boolean.TRUE : Boolean.FALSE)});
1488        } catch (NoSuchMethodException JavaDoc ex) {
1489            e = ex;
1490        } catch (IllegalAccessException JavaDoc 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()); // NOI18N
1502
err.log(Level.WARNING, null, e);
1503        }
1504    }
1505
1506    /** filenames list of just created files; sync purpose */
1507    private static final List<String JavaDoc> createdIDOs =
1508        Collections.synchronizedList(new ArrayList<String JavaDoc>(1));
1509
1510    /** Clear name cache */
1511    void notifyAttributeChanged(org.openide.filesystems.FileAttributeEvent fae) {
1512        nameCache = null;
1513        super.notifyAttributeChanged(fae);
1514    }
1515
1516    /** helper allowing a Writer to provide context via Lookup.Provider
1517     */

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    /** The Restricted FileObject implementation allowing to get just
1550     * read-only informations about name and location. It should prevent
1551     * any manipulation with file or its content.
1552     */

1553    private static final class FileObjectContext extends FileObject {
1554        private static final String JavaDoc UNSUPPORTED = "The Restricted FileObject" + //NOI18N
1555
" implementation allowing to get just read-only informations about" + //NOI18N
1556
" name and location. It should prevent any manipulation with file" + //NOI18N
1557
" or its content."; //NOI18N
1558
private final FileObject fo;
1559        private final FileObject parent;
1560        private final String JavaDoc name;
1561
1562        public FileObjectContext(FileObject parent, String JavaDoc 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 JavaDoc(UNSUPPORTED);
1570        }
1571
1572        public FileObject createData(String JavaDoc name, String JavaDoc ext) throws IOException {
1573            throw new UnsupportedOperationException JavaDoc(UNSUPPORTED);
1574        }
1575
1576        public FileObject createFolder(String JavaDoc name) throws IOException {
1577            throw new UnsupportedOperationException JavaDoc(UNSUPPORTED);
1578        }
1579
1580        public void delete(FileLock lock) throws IOException {
1581            throw new UnsupportedOperationException JavaDoc(UNSUPPORTED);
1582        }
1583
1584        public Object JavaDoc getAttribute(String JavaDoc attrName) {
1585            return fo == null? null: fo.getAttribute(attrName);
1586        }
1587
1588        public Enumeration<String JavaDoc> getAttributes() {
1589            return fo == null? Enumerations.<String JavaDoc>empty(): fo.getAttributes();
1590        }
1591
1592        public FileObject[] getChildren() {
1593            return new FileObject[0];
1594        }
1595
1596        public String JavaDoc getExt() {
1597            return InstanceDataObject.XML_EXT; //NOI18N
1598
}
1599
1600        public FileObject getFileObject(String JavaDoc name, String JavaDoc 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 JavaDoc(UNSUPPORTED);
1610        }
1611
1612        public String JavaDoc getName() {
1613            return name;
1614        }
1615
1616        public OutputStream getOutputStream(FileLock lock) throws IOException {
1617            throw new UnsupportedOperationException JavaDoc(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 JavaDoc(UNSUPPORTED);
1654        }
1655
1656        public void removeFileChangeListener(FileChangeListener fcl) {
1657            throw new UnsupportedOperationException JavaDoc(UNSUPPORTED);
1658        }
1659
1660        public void rename(FileLock lock, String JavaDoc name, String JavaDoc ext) throws IOException {
1661            throw new UnsupportedOperationException JavaDoc(UNSUPPORTED);
1662        }
1663
1664        public void setAttribute(String JavaDoc attrName, Object JavaDoc value) throws IOException {
1665            throw new UnsupportedOperationException JavaDoc(UNSUPPORTED);
1666        }
1667
1668        public void setImportant(boolean b) {
1669            throw new UnsupportedOperationException JavaDoc(UNSUPPORTED);
1670        }
1671
1672    }
1673
1674}
1675
Popular Tags