KickJava   Java API By Example, From Geeks To Geeks.

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


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 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeEvent JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.util.*;
26 import java.util.logging.Level JavaDoc;
27 import java.util.logging.Logger JavaDoc;
28
29 import org.openide.*;
30 import org.openide.filesystems.*;
31 import org.openide.cookies.InstanceCookie;
32 import org.openide.util.Task;
33 import org.openide.util.TaskListener;
34 import org.openide.util.RequestProcessor;
35
36 /** Support class for creation of an object from the content
37 * of a {@link DataObject.Container}. It implements
38 * {@link InstanceCookie}, so it
39 * can be used as a cookie for a node or data object.
40 * <P>
41 * When created on a container and started by invoking run method,
42 * it scans its content (in a separate
43 * thread) and creates a list of instances from which the new
44 * instance of this object should be composed. The object
45 * automatically listens to changes of components
46 * in the container, and if some change occurs, it allows the subclass to create
47 * a new object.
48 * </p>
49 *
50 * <p>Subclasses shall override the following methods:</p>
51 *
52 * <ol>
53 * <li>{@link #createInstance(InstanceCookie[])} (required): this method is
54 * called whenever the content has been changed. Its implementation
55 * shall build up the data structures and perform the actions required
56 * by this implementation.</li>
57 * <li>The filter methods {@link #acceptDataObject(DataObject)}, {@link
58 * #acceptCookie(InstanceCookie)},
59 * {@link #acceptFolder(DataFolder)} and
60 * {@link #acceptContainer(DataObject.Container)} (optional): the standard
61 * way is to override one or several of the latter methods. Overriding
62 * {@link #acceptDataObject(DataObject)} more deeply
63 * modifies the default behavior, because the default implementation of
64 * {@link #acceptDataObject(DataObject)} calls the
65 * other 3 filter methods. See the method documentation for details.</li>
66 * <li>The {@link InstanceCookie} methods
67 * {@link #instanceClass()} (optional but recommended)
68 * to inform about the class implemented by the return value of
69 * {@link #instanceCreate()}.</li>
70 * <li>Advanced subclasses may need to override {@link #postCreationTask}
71 * and/or {@link #instanceForCookie}, but it is not common to need these.</li>
72 * </ol>
73 *
74 * @author Jaroslav Tulach
75 */

76 public abstract class FolderInstance extends Task implements InstanceCookie { // XXX add generic type params?
77

78     /* -------------------------------------------------------------------- */
79     /* -- Constants ------------------------------------------------------- */
80     /* -------------------------------------------------------------------- */
81     
82     /** a queue to run requests in */
83     private static final RequestProcessor PROCESSOR = new RequestProcessor (
84       "Folder Instance Processor" // NOI18N
85
);
86     
87     /** static variable to hold current value for callbacks to tasks
88      * Also used to synchronize access to map field on.
89      */

90     private static final ThreadLocal JavaDoc<Object JavaDoc> CURRENT = new ThreadLocal JavaDoc<Object JavaDoc> ();
91
92     /** The last finished folder instance in this thread. Works together
93      * with CURRENT, because sometimes more than one FolderInstance.instanceCreate
94      * can be called on the same thread.
95      */

96     private static final ThreadLocal JavaDoc<Object JavaDoc> LAST_CURRENT = new ThreadLocal JavaDoc<Object JavaDoc> ();
97
98     /* -------------------------------------------------------------------- */
99     /* -- Instance attributes --------------------------------------------- */
100     /* -------------------------------------------------------------------- */
101
102     /** Folder to work with. Non null only if a constructor with DataFolder
103      * is used to construct this object.
104      */

105     protected DataFolder folder;
106     
107     /** container to work with */
108     private DataObject.Container container;
109
110     /** map of primary file to their cookies (FileObject, HoldInstance) */
111     private HashMap<FileObject, HoldInstance> map = new HashMap<FileObject, HoldInstance> (17);
112
113     /** Array of tasks that we have to check before we are ok. These are the tasks
114      * associated with children of the current folder.
115      */

116     private Task[] waitFor;
117
118     /** object for this cookie. Either the right instance of object or
119     * an instance of IOException or ClassNotFoundException. By default
120     * it is assigned to some private object in this class to signal that
121     * it is uninitialized.
122     */

123     private Object JavaDoc object = CURRENT;
124
125     /** Listener and runner for this object */
126     private Listener JavaDoc listener;
127     
128     /** error manager for this instance */
129     private Logger JavaDoc err;
130
131     /** Task that computes the children list of the folder */
132     private Task recognizingTask;
133     
134     /** A task that gets objects from InstanceCookie's and calls createInstance.
135      * Started immediately after the <code>recognizingTask</code> is finished.
136      */

137     private Task creationTask;
138     
139     /* -------------------------------------------------------------------- */
140     /* -- Constructor(s) -------------------------------------------------- */
141     /* -------------------------------------------------------------------- */
142
143     /** Create new folder instance.
144      * @param df data folder to create instances from
145     */

146     public FolderInstance (DataFolder df) {
147         this ((DataObject.Container)df);
148     }
149     
150     /** A new object that listens on changes in a container.
151      * @param container the object to associate with
152      * @since 1.11
153      */

154     public FolderInstance (DataObject.Container container) {
155         this (container, null);
156     }
157     
158     /** Constructs everything.
159      * @param container container
160      * @param logName the name to use for logging purposes
161      */

162     private FolderInstance (DataObject.Container container, String JavaDoc logName) {
163         if (container instanceof DataFolder) {
164             folder = (DataFolder)container;
165             if (logName == null) {
166                 logName = folder.getPrimaryFile().getPath().replace('/','.');
167             }
168             container = FolderList.find (folder.getPrimaryFile (), true);
169         }
170         
171         listener = new Listener JavaDoc ();
172         
173         if (logName == null) {
174             logName = "org.openide.loaders.FolderInstance"; // NOI18N
175
} else {
176             logName = "org.openide.loaders.FolderInstance" + '.' + logName; // NOI18N
177
}
178
179         err = Logger.getLogger(logName);
180
181         this.container = container;
182         container.addPropertyChangeListener (
183             org.openide.util.WeakListeners.propertyChange (listener, container)
184         );
185         
186         if (err.isLoggable(Level.FINE)) {
187             err.fine("new " + this); // NOI18N
188
}
189     }
190     
191     /* -------------------------------------------------------------------- */
192     /* -- Implementation of org.openide.Cookies.InstanceCookie ------------ */
193     /* -------------------------------------------------------------------- */
194
195     /** The name of the class that we create.
196     * @return the name
197     */

198     public String JavaDoc instanceName () {
199         try {
200             return instanceClass ().getName ();
201         } catch (java.io.IOException JavaDoc ex) {
202             return "java.lang.Object"; // NOI18N
203
} catch (ClassNotFoundException JavaDoc ex) {
204             return "java.lang.Object"; // NOI18N
205
}
206     }
207
208     /** Returns the root class of all objects.
209     * Supposed to be overriden in subclasses.
210     *
211     * @return Object.class
212     * @exception IOException an I/O error occured
213     * @exception ClassNotFoundException the class has not been found
214     */

215     public Class JavaDoc<?> instanceClass ()
216     throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
217         Object JavaDoc object = this.object;
218         if (object != null) {
219             if (object instanceof java.io.IOException JavaDoc) {
220                 throw (java.io.IOException JavaDoc)object;
221             }
222             if (object instanceof ClassNotFoundException JavaDoc) {
223                 throw (ClassNotFoundException JavaDoc)object;
224             }
225             return object.getClass ();
226         }
227
228         return Object JavaDoc.class;
229     }
230
231     /** Creates instance.
232     * @return an object to work with
233     * @exception IOException an I/O error occured
234     * @exception ClassNotFoundException the class has not been found
235     */

236     public Object JavaDoc instanceCreate ()
237     throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
238         Object JavaDoc object = CURRENT.get ();
239         
240         if (object == null || LAST_CURRENT.get () != this) {
241             err.fine("do into waitFinished"); // NOI18N
242
waitFinished ();
243
244             object = FolderInstance.this.object;
245         }
246
247         if (err.isLoggable(Level.FINE)) {
248             err.fine("instanceCreate: " + object); // NOI18N
249
}
250
251         if (object instanceof java.io.IOException JavaDoc) {
252             throw (java.io.IOException JavaDoc)object;
253         }
254         if (object instanceof ClassNotFoundException JavaDoc) {
255             throw (ClassNotFoundException JavaDoc)object;
256         }
257         
258         if (object == CURRENT) {
259             // uninitialized
260
throw new IOException JavaDoc ("Cyclic reference. Somebody is trying to get value from FolderInstance (" + getClass ().getName () + ") from the same thread that is processing the instance"); // NOI18N
261
}
262         
263         return object;
264     }
265     
266     /* -------------------------------------------------------------------- */
267     /* -- Wait ------------------------------------------------------------ */
268     /* -------------------------------------------------------------------- */
269
270     /** Wait for instance initialization to finish.
271     */

272     public final void instanceFinished () {
273         waitFinished ();
274     }
275     
276     /* -------------------------------------------------------------------- */
277     /* -- Extends org.openide.util.Task ----------------------------------- */
278     /* -------------------------------------------------------------------- */
279     
280     /** Overrides the instance finished to deal with
281      * internal state correctly.
282      */

283     public void waitFinished () {
284         boolean isLog = err.isLoggable(Level.FINE);
285         for (;;) {
286             err.fine("waitProcessingFinished on container"); // NOI18N
287
waitProcessingFinished (container);
288
289             Task originalRecognizing = checkRecognizingStarted ();
290             if (isLog) {
291                 err.fine("checkRecognizingStarted: " + originalRecognizing); // NOI18N
292
}
293             originalRecognizing.waitFinished ();
294
295             Task t = creationTask;
296             if (isLog) {
297                 err.fine("creationTask: " + creationTask); // NOI18N
298
}
299             if (t != null) {
300                 t.waitFinished ();
301             }
302
303
304             Task[] toWait = waitFor;
305             if (isLog) {
306                 err.fine("toWait: " + toWait); // NOI18N
307
}
308             if (toWait != null) {
309                 for (int i = 0; i < toWait.length; i++) {
310                     if (isLog) {
311                         err.fine(" wait[" + i + "]: " + toWait[i]); // NOI18N
312
}
313                     toWait[i].waitFinished ();
314                 }
315             }
316
317             // loop if there was yet another task started to compute the
318
// children list
319
if (originalRecognizing == checkRecognizingStarted ()) {
320                 if (isLog) {
321                     err.fine("breaking the wait loop"); // NOI18N
322
}
323                 break;
324             }
325             
326             //
327
// otherwise go on an try it once more
328
//
329
}
330     }
331
332     /** Synchronously starts the creation of the instance. */
333     public void run () {
334         recreate ();
335         instanceFinished ();
336     }
337     
338     /* -------------------------------------------------------------------- */
339     /* -- Filter methods (protected, may be overridden by sub-classes) ---- */
340     /* -------------------------------------------------------------------- */
341
342     /** Allows subclasses to decide whether they want to work with the specified
343     * <code>DataObject</code> or not.
344     *
345     * <p>The default implementation roughly performs the following steps:</p>
346     *
347     * <ol>
348     * <li>if <code>dob</code> has an <code>InstanceCookie</code>
349     * {@link #acceptCookie(InstanceCookie)} is called on that cookie</li>
350     * <li>if <code>dob</code> has a <code>DataFolder</code> cookie,
351     * {@link #acceptFolder(DataFolder)} is called on that folder</li>
352     * <li>if <code>dob</code> has a <code>DataObject.Container</code> cookie,
353     * {@link #acceptContainer(DataObject.Container)} is called on that
354     * container</li>
355     * </ol>
356     *
357     * <p>The first of the aforementioned steps which returns a non-<code>null</code>
358     * cookie and does not throw an exception determines the return value. If
359     * none of the steps succeeds, <code>null</code> is returned.</p>
360     *
361     * @param dob a <code>DataObject</code> to test
362     * @return the cookie for the <code>DataObject</code> or <code>null</code>
363     * if it should not be used
364     */

365     protected InstanceCookie acceptDataObject(DataObject dob) {
366         int acceptType = -1;
367         
368         InstanceCookie cookie;
369         //Order of checking reversed first check cookie and then folder
370
// test if we accept the instance
371
cookie = (InstanceCookie)dob.getCookie (InstanceCookie.class);
372         try {
373             cookie = cookie == null ? null : acceptCookie (cookie);
374             acceptType = 1;
375         } catch (IOException JavaDoc ex) {
376             // an error during a call to acceptCookie
377
err.log(Level.WARNING, null, ex);
378             cookie = null;
379         } catch (ClassNotFoundException JavaDoc ex) {
380             // an error during a call to acceptCookie
381
err.log(Level.WARNING, null, ex);
382             cookie = null;
383         }
384         
385         if (cookie == null) {
386             DataFolder folder = (DataFolder) dob.getCookie (DataFolder.class);
387             if (folder != null) {
388                 HoldInstance previous = map.get (folder.getPrimaryFile ());
389                 if (previous != null && previous.cookie != null) {
390                     // the old cookie will be returned if the folder is already registered
391
cookie = previous;
392                     acceptType = 2;
393                 } else {
394                     cookie = acceptFolder (folder);
395                     acceptType = 3;
396                 }
397             }
398         }
399         
400         if (cookie == null) {
401             // try also the container
402
DataObject.Container c = (DataObject.Container)dob.getCookie (DataObject.Container.class);
403             if (c != null) {
404                 cookie = acceptContainer (c);
405                 acceptType = 4;
406             }
407         }
408
409         if (err.isLoggable(Level.FINE)) {
410             err.fine("acceptDataObject: " + dob + " cookie: " + cookie + " acceptType: " + acceptType); // NOI18N
411
}
412
413         return cookie;
414     }
415     
416     /** Allows subclasses to decide whether they want to work with
417     * the specified <code>InstanceCookie</code> or not.
418     * <p>The default implementation simply
419     * returns the same cookie, but subclasses may
420     * decide to return <code>null</code> or a different cookie.
421     * </p>
422     * <p>Compare {@link #acceptDataObject(DataObject)} to learn when this method
423     * is called.</p>
424     *
425     * @param cookie the instance cookie to test
426     * @return the cookie to use or <code>null</code> if this cookie should not
427     * be used
428     * @exception IOException if an I/O error occurred calling a cookie method
429     * @exception ClassNotFoundException if a class is not found in a call to a cookie method
430     */

431     protected InstanceCookie acceptCookie (InstanceCookie cookie)
432     throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
433         return cookie;
434     }
435
436     /** Allows subclasses to decide how they want to work with a
437     * provided folder.
438     *
439     * <p>The default implementation simply calls {@link #acceptContainer(DataObject.Container)}.</p>
440     *
441      * <p>A common override of this method is to return a new
442      * <code>FolderInstance</code> based on the subfolder, permitting
443      * recursion.</p>
444      *
445     * <p>Compare {@link #acceptDataObject(DataObject)} to learn when this method
446     * is called.</p>
447     *
448     * @param df data folder to create cookie for
449     * @return the cookie for this folder or <code>null</code> if this folder should not
450     * be used
451     */

452     protected InstanceCookie acceptFolder (DataFolder df) {
453         return acceptContainer (df);
454     }
455     
456     /** Allows subclasses to decide how they want to work with an object
457      * that implements a DataObject.Container.
458      *
459      * <p>By default this returns <code>null</code> to indicated that subfolders
460      * (as well as {@link DataShadow}s, etc.) should be ignored.</p>
461      *
462      * <p>A common override of this method is to return a new
463      * <code>FolderInstance</code> based on the subfolder, permitting
464      * recursion.</p>
465      *
466      * <p>Compare {@link #acceptDataObject(DataObject)} to learn when this method
467      * is called.</p>
468      *
469      * @param container the container to accept or not
470      * @return cookie for this container or <code>null</code> if this object should
471      * be ignored
472      *
473      * @since 1.11
474      */

475     protected InstanceCookie acceptContainer (DataObject.Container container) {
476         return null;
477     }
478     
479     /* ----------------------------------------------------------------------------- */
480     /* -- Instances creation method (protected, must be overridden by sub-classes) - */
481     /* ----------------------------------------------------------------------------- */
482
483     /** Notifies subclasses that the set of cookies for this folder
484     * has changed.
485     * A new object representing the folder should
486     * be created (or the old one updated).
487     * Called both upon initialization of the class, and change of its cookies.
488     *
489     * <p>It may be poor style for this method to have side-effects. A
490     * common way to use <code>FolderInstance</code> is to have this
491     * method set some global state which is then used as the resulting
492     * instance. Better is to treat the <code>FolderInstance</code> as
493     * pure SPI and assign it to a variable of type
494     * <code>InstanceCookie</code>. Then use the {@link
495     * #instanceCreate} method to get the final result. However in some
496     * cases there is a singleton live object which must be updated
497     * in-place, and it only makes sense to do so here (in which case
498     * the <code>InstanceCookie</code> methods are unused).</p>
499     *
500     * @param cookies updated array of instance cookies for the folder
501     * @return object to represent these cookies
502     *
503     * @exception IOException an I/O error occured
504     * @exception ClassNotFoundException a class has not been found
505     */

506     protected abstract Object JavaDoc createInstance (InstanceCookie[] cookies)
507     throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc;
508     
509     /* ----------------------------------------------------------------------------- */
510     /* -- Instances creation method (protected, may be overridden by sub-classes) - */
511     /* ----------------------------------------------------------------------------- */
512
513     /** Method that is called when a the folder instance really wants to
514     * create an object from provided cookie.
515     * It allows subclasses to overwrite the default behaviour (which is
516     * to call {@link InstanceCookie#instanceCreate}).
517     *
518     * @param obj the data object that is the source of the cookie
519     * @param cookie the instance cookie to read the instance from
520     * @exception IOException when there I/O error
521     * @exception ClassNotFoundException if the class cannot be found
522     */

523     protected Object JavaDoc instanceForCookie (DataObject obj, InstanceCookie cookie)
524     throws IOException JavaDoc, ClassNotFoundException JavaDoc {
525         return cookie.instanceCreate ();
526     }
527     
528     /* ----------------------------------------------------------------------------- */
529     /* -- Recreation --------------------------------------------------------------- */
530     /* ----------------------------------------------------------------------------- */
531
532     /** Starts recreation of the instance in special thread.
533     */

534     public synchronized void recreate () {
535         // this method should be synchronized so the recognizingTask is created
536
// together with notification that we are running.
537
// Fix of #16136 => sometimes it happened that the thread started in
538
// the recognizer task was finished sooner then notifyRunning called.
539
// In such case notifyFinished could be called before notifyRunning
540
// and everything was completely broken
541
err.fine("recreate");
542         recognizingTask = computeChildrenList (container, listener);
543         if (err.isLoggable(Level.FINE)) {
544             err.fine(" recognizing task is now " + recognizingTask);
545         }
546         notifyRunning ();
547     }
548
549     /** Checks whether recreation of this instance is running already
550      * and in that case does nothing, otherwise calls
551      * {@link #recreate() recreate} method.
552      * This prevents from redundant recreation tasks of this instance caused by
553      * first creation of underlying items which are also of {@link org.openide.util.Task Task}
554      * type (e.g. sub-FolderInstances, sub-FolderLookups etc.). */

555     final void checkRecreate() {
556         if(isFinished()) {
557             recreate();
558         }
559     }
560
561     /** Checks whether recreation has already started and starts it if if was
562      * was not yet started during the live of this <code>FolderInstance</code>.
563      * @return the latest started task for children computation */

564     private final synchronized Task checkRecognizingStarted () {
565         if(recognizingTask == null) {
566             recreate();
567         }
568         
569         return recognizingTask;
570     }
571
572     /* ----------------------------------------------------------------------------- */
573     /* -- Static helper methods (abstract away the differences between different --- */
574     /* ------------------------- DataObject.Container types) ----------------------- */
575     /* ----------------------------------------------------------------------------- */
576     
577     /** Waits until the task to compute the children of the currents folder is
578      * finished. This methods provides a unified interface which allows to
579      * treat <code>FolderList</code>s and general <code>DataObject.Container</code>s
580      * is a uniform way.
581      */

582     private static void waitProcessingFinished (
583         DataObject.Container c
584     ) {
585         if (c instanceof FolderList) {
586             ((FolderList)c).waitProcessingFinished ();
587         }
588     }
589     
590     /** Starts and returns the task to compute the children of the current
591      * folder. This methods provides a unified interface which allows to
592      * treat <code>FolderList</code>s and general <code>DataObject.Container</code>s
593      * is a uniform way.
594      *
595      * <p>The task returned uses the {@link #listener} to process the children.</p>
596      */

597     private static Task computeChildrenList (
598         final DataObject.Container container, final FolderListListener listener
599     ) {
600         if (container instanceof FolderList) {
601             FolderList list = (FolderList)container;
602             return list.computeChildrenList (listener);
603         }
604         
605         // otherwise we have to simulate the listener by container methods
606
// itself
607
return PROCESSOR.post (new Runnable JavaDoc () {
608             public void run () {
609                 DataObject[] arr = container.getChildren ();
610                 ArrayList<DataObject> list = new ArrayList<DataObject> (arr.length);
611                 for (int i = 0; i < arr.length; i++) {
612                     listener.process (arr[i], list);
613                 }
614                 listener.finished (list);
615             }
616         });
617     }
618     
619     /* ----------------------------------------------------------------------------- */
620     /* -- Processing --------------------------------------------------------------- */
621     /* ----------------------------------------------------------------------------- */
622
623     /** A method that starts <code>creationTask</code>, the task which really
624      * creates the instances from given objects. The task is started by a
625      * call to {@link #postCreationTask(Runnable)}.
626     *
627     * @param arr collection of DataObjects
628     */

629     final void processObjects (final Collection<DataObject> arr) {
630         creationTask = postCreationTask (new Runnable JavaDoc () {
631             public void run () {
632                 defaultProcessObjects (arr);
633             }
634         });
635     }
636
637     /** Default processing of objects.
638     * @param arr array of objects to process
639     */

640     private final void defaultProcessObjects (Collection<DataObject> arr) {
641         err.fine("defaultProcessObjects");
642         HashSet<FileObject> toRemove;
643         ArrayList<HoldInstance> cookies = new ArrayList<HoldInstance> ();
644
645         // synchronized for safe access to map field
646
synchronized (CURRENT) {
647             toRemove = new HashSet<FileObject> (map.keySet ());
648         }
649             
650         for (DataObject obj: arr) {
651             if (! obj.isValid()) {
652                 // #12960: skip over it, probably invalidated while we were
653
// waiting for this task to be run...
654
continue;
655             }
656
657             // testing
658
InstanceCookie cookie = acceptDataObject(obj);
659             if (cookie != null) {
660                 // cookie accepted
661
FileObject fo = obj.getPrimaryFile ();
662                 
663                 boolean attachListener = true;
664                 HoldInstance prevCookie = null;
665                 if (toRemove.remove (fo)) {
666                     // if the fo is in the map than try to find its cookie
667
prevCookie = map.get (fo);
668                     if (prevCookie != null && (prevCookie.cookie == null || !prevCookie.cookie.equals (cookie))) {
669                         prevCookie = null;
670                         // #49199 - do not add second listener
671
attachListener = false;
672                     }
673                 }
674                 
675                 if (prevCookie == null) {
676                     // such cookie is not there yet
677
HoldInstance hold;
678                     
679                     if (cookie instanceof HoldInstance) {
680                         hold = (HoldInstance)cookie;
681                     } else {
682                         hold = new HoldInstance(obj, cookie);
683                     }
684                     
685                     // synchronized for safe access to map field
686
synchronized (CURRENT) {
687                         map.put (fo, hold);
688                     }
689                 
690                     // register for changes of PROP_COOKIE property
691
if (attachListener) {
692                         obj.addPropertyChangeListener (
693                             org.openide.util.WeakListeners.propertyChange (listener, obj)
694                         );
695                     }
696                     
697                     cookies.add (hold);
698                 } else {
699                     // old cookie, already there => only add it to the list of cookies
700
cookies.add (prevCookie);
701                 }
702             } else {
703                 // empty instance placeholder
704
synchronized (CURRENT) {
705                     FileObject fo = obj.getPrimaryFile ();
706                     toRemove.remove (fo);
707                     
708                     HoldInstance hold = map.get (fo);
709                     if (hold != null && hold.cookie == null) {
710                         // already registered do not do any changes
711
continue;
712                     }
713                     
714                     // not yet registered, add new
715

716                     hold = new HoldInstance (obj, null);
717                     
718                     map.put (fo, hold);
719                 }
720                 
721                 // register for changes of PROP_COOKIE property
722
obj.addPropertyChangeListener (
723                     org.openide.util.WeakListeners.propertyChange (listener, obj)
724                 );
725                 
726             }
727             
728         }
729
730         // synchronized for safe access to map field
731
synchronized (CURRENT) {
732             // now remove the cookies that are no longer in the folder
733
map.keySet ().removeAll (toRemove);
734         }
735
736         // create the list of cookies
737
HoldInstance[] all = new HoldInstance[cookies.size ()];
738         cookies.toArray (all);
739         
740         updateWaitFor (all);
741
742         Object JavaDoc result = null;
743         try {
744             result = createInstance (all);
745         } catch (IOException JavaDoc ex) {
746             result = ex;
747         } catch (ClassNotFoundException JavaDoc ex) {
748             result = ex;
749         } finally {
750             if (err.isLoggable(Level.FINE)) {
751                 err.fine("notifying finished"); // NOI18N
752
for (int log = 0; log < all.length; log++) {
753                     err.fine(" #" + log + ": " + all[log]); // NOI18N
754
}
755             }
756             object = result;
757             
758             Object JavaDoc prevResult = CURRENT.get ();
759             CURRENT.set (result);
760             Object JavaDoc prevLast = LAST_CURRENT.get ();
761             LAST_CURRENT.set (this);
762             
763             try {
764                 notifyFinished ();
765             } finally {
766                 CURRENT.set (prevResult);
767                 LAST_CURRENT.set (prevLast);
768             }
769         }
770     }
771     
772     /** Recomputes the list of tasks we should wait for (i.e. the tasks associated
773      * with the children of the folder).
774      */

775     private void updateWaitFor (HoldInstance[] arr) {
776         ArrayList<Task> out = new ArrayList<Task> (arr.length);
777         for (int i = 0; i < arr.length; i++) {
778             Task t = arr[i].getTask ();
779             if (t != null) {
780                 out.add (t);
781             }
782         }
783         waitFor = out.toArray (new Task[out.size ()]);
784     }
785     
786     /* ----------------------------------------------------------------------------- */
787     /* -- Processing: Start the creation task (protected, may be overridden) ------- */
788     /* ----------------------------------------------------------------------------- */
789     
790     /** Invokes the creation of objects in a "safe" thread. This method is
791     * for expert subclasses that want to control the thread that the
792     * instance is created in.
793     *
794     * <p>The default implementation invokes the creation logic in the
795     * request processor in non-blocking mode (no other tasks will
796     * block on this).</p>
797     *
798     * @param run runnable to run
799     * @return task to control the execution of the runnable or null if
800     * the runnable is run immediatelly
801     * @since 1.5
802     */

803     protected Task postCreationTask (Runnable JavaDoc run) {
804         return PROCESSOR.post (run);
805     }
806     
807     /* ----------------------------------------------------------------------------- */
808     /* -- Getters ------------------------------------------------------------------ */
809     /* ----------------------------------------------------------------------------- */
810     
811     /** Access to error manager for FolderLookup.
812      */

813     final Logger JavaDoc err () {
814         return err;
815     }
816     
817     public String JavaDoc toString () {
818         return getClass ().getName () + "@" + Integer.toHexString (System.identityHashCode (this)) + "(" + this.container + ")"; // NOI18N
819
}
820     
821     /* -------------------------------------------------------------------- */
822     /* -- Inner class Listener -------------------------------------------- */
823     /* -------------------------------------------------------------------- */
824
825     /** Listener on change of folder's children and a starter for the task.
826      *
827      * <p>Each instance of {@link FolderInstance} has one instance of this class
828      * associated with it. The latter serves for three purposes:</p>
829      *
830      * <ol>
831      * <li>to listen for property changes of the {@link DataObject.Container}
832      * this {@link FolderInstance} was created for (by implementing
833      * {@link java.beans.PropertyChangeListener})</li>
834      * <li>to listen for changes of the cookies of the children of this folder</li>
835      * <li>to process the results of the computation of a child list
836      * (by implementing {@link FolderListListener#finished(java.util.List)})
837      * </li>
838      * </ol>
839     */

840     private class Listener implements PropertyChangeListener JavaDoc, FolderListListener {
841         
842         Listener() {}
843
844         /** Recreates the {@link FolderInstance} if the children list of
845          * its container was changed.
846          *
847          * <p>Additionally ...</p>
848          */

849         public void propertyChange (PropertyChangeEvent JavaDoc ev) {
850             Object JavaDoc s = ev.getSource ();
851             if (s == container) {
852                 if (DataObject.Container.PROP_CHILDREN.equals(ev.getPropertyName ())) {
853                     err.fine("PROP_CHILDREN");
854
855                     recreate();
856                 }
857                 return;
858             }
859
860             if (DataObject.PROP_NAME.equals(ev.getPropertyName())) {
861                 if (s instanceof DataObject) {
862                     err.fine("PROP_NAME");
863                     recreate();
864                 }
865             }
866             
867             // change of cookie in one of children of the container
868

869             if (DataObject.PROP_COOKIE.equals(ev.getPropertyName ())) {
870                 if (s instanceof DataObject) {
871                     DataObject source = (DataObject)s;
872                     err.fine("PROP_COOKIE: " + source); // NOI18N
873

874                     InstanceCookie ic = acceptDataObject (source);
875                     
876                     HoldInstance hi;
877                     FileObject fo = source.getPrimaryFile();
878                     synchronized (CURRENT) {
879                         hi = map.get(fo);
880                     }
881                 
882                     if (hi != null) {
883                         err.fine("previous instance: " + hi + " new instance " + ic); // NOI18N
884
/* Recreate if the new instance cookie is null or differs
885                          * from the previous one.
886                          * When the default implementation of acceptDataObject is
887                          * used ic == hi is the case if source is a folder.
888                          * [XXX] Why not:
889                          * if ((ic == null && hi.cookie != null) || (ic != hi && !ic.equals (hi.cookie))) {
890                          */

891                         if (ic == null || (ic != hi && !ic.equals (hi.cookie))) {
892                             hi = new HoldInstance(source, ic);
893
894                             // synchronized for safe access to map field
895
synchronized (CURRENT) {
896                                 map.put (fo, hi);
897                             }
898                             recreate ();
899                         }
900                     }
901                 }
902             }
903         }
904
905         /** Callback for object processing after all children are computed.
906          * This implementation starts a new task for the creation of the
907          * child objects.
908          * @param arr list of DataObjects
909          */

910         public void finished(java.util.List JavaDoc<DataObject> arr) {
911             processObjects (arr);
912         }
913
914         /** Default implementation without filtering.
915          * @param obj the object recognized
916          * @param arr array where the implementation should add the
917          * object
918          */

919         public void process(DataObject obj, java.util.List JavaDoc<DataObject> arr) {
920             arr.add (obj);
921         }
922
923     }
924     
925     /* -------------------------------------------------------------------- */
926     /* -- Inner class HoldInstance ---------------------------------------- */
927     /* -------------------------------------------------------------------- */
928
929     /** A instance cookie that holds the result of first
930     * invocation of the provided cookie.
931     *
932     */

933     private class HoldInstance extends Object JavaDoc
934     implements InstanceCookie.Of, TaskListener {
935         /** the data object -> source of this instance */
936         private final DataObject source;
937         /** the cookie to delegate to */
938         protected final InstanceCookie cookie;
939
940         public HoldInstance (DataObject source, InstanceCookie cookie) {
941             this.cookie = cookie;
942             this.source = source;
943
944             if (cookie instanceof Task) {
945                 // for example FolderInstance ;-) attach itself for changes
946
// in the cookie
947
Task t = (Task)cookie;
948                 t.addTaskListener (
949                     (TaskListener)org.openide.util.WeakListeners.create (TaskListener.class, this, t)
950                 );
951             }
952         }
953
954         /** Full name of the data folder's primary file separated by dots.
955         * @return the name
956         */

957         public String JavaDoc instanceName () {
958             return cookie.instanceName ();
959         }
960
961         /** Query to find out if the object created by this cookie is
962          * instance of given type. The same code as:
963          * <pre>
964          * Class actualClass = instanceClass ();
965          * result = type.isAsignableFrom (actualClass);
966          * </pre>
967          * But this can prevent the class <code>actualClass</code> to be
968          * loaded into the <em>JavaVM</em>.
969          *
970          * @param type the class type we want to check
971          * @return true if this cookie can produce object of given type
972          */

973         public boolean instanceOf(Class JavaDoc<?> type) {
974             if (cookie instanceof InstanceCookie.Of) {
975                 InstanceCookie.Of of = (InstanceCookie.Of)cookie;
976                 return of.instanceOf (type);
977             }
978             // delegate
979
try {
980                 Class JavaDoc<?> clazz = cookie.instanceClass ();
981                 return type.isAssignableFrom (clazz);
982             } catch (IOException JavaDoc ex) {
983                 return false;
984             } catch (ClassNotFoundException JavaDoc ex) {
985                 return false;
986             }
987         }
988
989         /** Returns the root class of all objects.
990         * Supposed to be overriden in subclasses.
991         *
992         * @return Object.class
993         * @exception IOException an I/O error occured
994         * @exception ClassNotFoundException the class has not been found
995         */

996         public Class JavaDoc instanceClass ()
997         throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
998             return cookie.instanceClass ();
999         }
1000
1001        /**
1002        * @return an object to work with
1003        * @exception IOException an I/O error occured
1004        * @exception ClassNotFoundException the class has not been found
1005        */

1006        public Object JavaDoc instanceCreate ()
1007        throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
1008            return instanceForCookie (source, cookie);
1009        }
1010
1011        /** Called when a task finishes running.
1012         * @param task the finished task
1013         */

1014        public void taskFinished(Task task) {
1015            checkRecreate();
1016        }
1017        
1018        /** Waits till the instance is ready.
1019         */

1020        public Task getTask () {
1021            if (cookie instanceof Task) {
1022                // for example FolderInstance ;-) attach itself for changes
1023
// in the cookie
1024
return (Task)cookie;
1025            } else {
1026                return null;
1027            }
1028        }
1029    } // end of HoldInstance
1030

1031}
1032
Popular Tags