KickJava   Java API By Example, From Geeks To Geeks.

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


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.Reference JavaDoc;
25 import java.lang.ref.WeakReference JavaDoc;
26 import java.util.*;
27 import java.util.logging.*;
28 import org.openide.cookies.InstanceCookie;
29 import org.openide.filesystems.FileObject;
30 import org.openide.util.*;
31 import org.openide.util.lookup.*;
32
33 /** Implements a lookup, that scans a content of a folder for its
34  * data objects and asks them for instance cookie, the created objects
35  * are then used to for the content of the lookup.
36  * <p>Any instances which are in fact instances of <code>Lookup</code>
37  * will be proxied to, permitting one file to generate many instances
38  * in the lookup system easily.
39  * @author Jaroslav Tulach
40  * @since 1.11
41  */

42 public class FolderLookup extends FolderInstance {
43     
44     /** Lock for initiliazation of lookup. */
45     private static final Object JavaDoc LOCK = new Object JavaDoc ();
46     
47     /** Lookup to delegate to. */
48     private ProxyLkp lookup;
49     
50     /** The root name of the lookup. */
51     private String JavaDoc rootName;
52
53     /** Indicates whether this FolderLookup is at the root of (folder)tree which
54      * we are interested in and the one which collects all items from the tree. */

55     private final boolean isRoot;
56
57     
58     /** Constructs the FolderLookup for given container. A default ID prefix is
59      * used for identification of located items.
60      *
61      * @param df container (or folder) to work on
62      */

63     public FolderLookup (DataObject.Container df) {
64         this (df, "FL["); // NOI18N
65
}
66     
67     /** Constructs the FolderLookup for given container.
68      * @param df container (or folder) to work on
69      * @param prefix the prefix to use
70      */

71     public FolderLookup (DataObject.Container df, String JavaDoc prefix) {
72         this(df, prefix, true);
73     }
74
75     /** Constructs the FolderLookup for given container.
76      * @param df container (or folder) to work on
77      * @param prefix the prefix to use
78      * @param isRoot indicates whether this instance is the at the root of tree
79      * in which we perform the lookup -> only this instance has lookup
80      * which collects all items from the tree */

81     private FolderLookup(DataObject.Container df, String JavaDoc prefix, boolean isRoot) {
82         super(df);
83         
84         this.rootName = prefix;
85         this.isRoot = isRoot;
86     }
87     
88
89     /** The correct class that this folder recognizes.
90      * @return Proxy.Lkp class. */

91     public final Class JavaDoc<?> instanceClass () {
92         return ProxyLkp.class;
93     }
94     
95     /**
96      * Getter for the lookup that should be used.
97      * <p>Serializable since 3.27.
98      * @return a lookup
99      */

100     public final Lookup getLookup () {
101         boolean inited = false;
102         synchronized(LOCK) {
103             if(lookup == null) {
104                 lookup = new ProxyLkp(this);
105                 inited = true;
106             }
107         }
108
109         if(inited) {
110             checkRecreate();
111         }
112                 
113         return lookup;
114     }
115     
116     /** Updates the content of the lookup.
117      * @param cookies updated array of instance cookies for the folder
118      * @return object to represent these cookies
119      *
120      * @exception IOException an I/O error occured
121      * @exception ClassNotFoundException a class has not been found
122      */

123     protected final Object JavaDoc createInstance(InstanceCookie[] cookies)
124     throws IOException, ClassNotFoundException JavaDoc {
125         FolderLookupData flData = new FolderLookupData();
126
127         // If we are root, preserve place for abstract lookup which collects items.
128
// see ProxyLkp.update method.
129
if(isRoot) {
130             flData.lookups.add(null);
131         }
132         
133         for (int i = 0; i < cookies.length; i++) {
134             try {
135                 // It's either result from underlying lookup or some another lookup or it is ICItem.
136
Object JavaDoc obj = cookies[i].instanceCreate ();
137
138                 if(obj instanceof FolderLookupData) {
139                     // It's from underlying 'sub'-lookup.
140
flData.items.addAll(((FolderLookupData)obj).items);
141                     flData.lookups.addAll(((FolderLookupData)obj).lookups);
142                 } else if(obj instanceof Lookup) {
143                     flData.lookups.add((Lookup)obj);
144                 } else {
145                     // Has to be ICItem.
146
flData.items.add((ICItem)obj);
147                 }
148             } catch(IOException ex) {
149                 exception(ex);
150             } catch(ClassNotFoundException JavaDoc ex) {
151                 exception(ex);
152             }
153         }
154
155         // If this is not the root lookup just return items+lookups
156
// which will be collected by root lookup.
157
if(!isRoot) {
158             return flData;
159         }
160         
161         // We are root FolderLookup. Now collect all items from underlying world.
162

163         // Initializes lookup.
164
getLookup();
165
166         lookup.update(flData.items, flData.lookups);
167         
168         return lookup;
169     }
170     
171     /** Overrides superclass method. It returns instance
172      * for <code>DataObject</code>&<code>InstanceCookie</code> 'pair'.
173      * If the instance is of <code>FolderLookup.Lkp</code> class it is created otherwise
174      * new <code>Lkp.ICItem</code> created and returned.
175      *
176      * @param dobj the data object that is the source of the cookie
177      * @param cookie the instance cookie to read the instance from
178      * @exception IOException when there I/O error
179      * @exception ClassNotFoundException if the class cannot be found */

180     protected Object JavaDoc instanceForCookie(DataObject dobj, InstanceCookie cookie)
181     throws IOException, ClassNotFoundException JavaDoc {
182         boolean isLookup;
183         
184         if(cookie instanceof InstanceCookie.Of) {
185             isLookup = ((InstanceCookie.Of)cookie).instanceOf(Lookup.class);
186         } else {
187             isLookup = Lookup.class.isAssignableFrom(cookie.instanceClass ());
188         }
189
190         if(isLookup) {
191             // Is underlying FolderLookup create it.
192
return cookie.instanceCreate();
193         } else {
194             return new ICItem(dobj, rootName, cookie);
195         }
196     }
197     
198     /** Folder is recognized as underlying <code>FolderLookup</code> which passes
199      * its items to parent <code>FolderLookup</code>.
200      * @param df the folder found
201      * @return new FolderLookup
202      */

203     protected InstanceCookie acceptFolder (DataFolder df) {
204         return new FolderLookup(df, objectName(rootName, df), false);
205     }
206     
207     /** Container is recognized as underlying <code>FolderLookup</code> which passes
208      * its items to parent <code>FolderLookup</code>.
209      * @param df the container found
210      * @return new FolderLookup
211      */

212     protected InstanceCookie acceptContainer (DataObject.Container df) {
213         return new FolderLookup(
214             df,
215             rootName == null ? "<container>" : rootName + "<container>", // NOI18N
216
false
217         );
218     }
219     
220
221     /** Starts the creation of the object in the Folder recognizer thread.
222      * Doing all the lookup stuff in one thread should prevent deadlocks,
223      * but because we call unknown data loaders, they obviously must be
224      * implemented in correct way.
225      *
226      * @param run runable to start
227      * @return <code>null</code>, because the runnable is started immediatelly
228      */

229     protected final Task postCreationTask (Runnable JavaDoc run) {
230         run.run ();
231         return null;
232     }
233     
234     /** Concatenates name of folder with name of object. Helper method.
235      * @param folderName name of folder or null
236      * @param obj object to concatenate
237      * @return new name
238      */

239     private static String JavaDoc objectName (String JavaDoc name, DataObject obj) {
240         if (name == null) {
241             return obj.getName ();
242         } else {
243             return name + '/' + obj.getName ();
244         }
245     }
246     
247     /** Notifies the exception. Helper method. */
248     private static void exception (Exception JavaDoc e) {
249         Logger.getLogger(FolderLookup.class.getName()).log(Level.WARNING, null, e);
250     }
251     private static void exception(Exception JavaDoc e, FileObject fo) {
252         Exceptions.attachMessage(e, "Bad file: " + fo); // NOI18N
253
exception(e);
254     }
255
256     
257     /** <code>ProxyLookup</code> delegate so we can change the lookups on fly. */
258     static final class ProxyLkp extends ProxyLookup implements Serializable {
259         
260         private static final long serialVersionUID = 1L;
261
262         /** <code>FolderLookup</code> we are associated with. */
263         private transient FolderLookup fl;
264         
265         /** Content to control the abstract lookup. */
266         private transient AbstractLookup.Content content;
267         
268         private transient boolean readFromStream;
269
270         /** Constructs lookup which holds all items+lookups from underlying world.
271          * @param folder <code>FolderLookup</code> to associate to */

272         public ProxyLkp(FolderLookup folder) {
273             this(folder, new AbstractLookup.Content());
274         }
275
276         /** Constructs lookup. */
277         private ProxyLkp(FolderLookup folder, AbstractLookup.Content content) {
278             super(new Lookup[] {new AbstractLookup(content)});
279             
280             this.fl = folder;
281             this.content = content;
282         }
283         
284         public String JavaDoc toString() {
285             return "FolderLookup.lookup[\"" + fl.rootName + "\"]";
286         }
287         
288         private void writeObject (ObjectOutputStream oos) throws IOException {
289             Lookup[] ls = getLookups();
290             for (int i = 0; i < ls.length; i++) {
291                 oos.writeObject(ls[i]);
292             }
293             oos.writeObject(null);
294             oos.writeObject (fl.folder);
295             oos.writeObject (fl.rootName);
296             oos.writeObject (content);
297         }
298         
299         private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException JavaDoc {
300             List<Lookup> ls = new ArrayList<Lookup>();
301             Lookup l;
302             while ((l = (Lookup)ois.readObject()) != null) {
303                 ls.add(l);
304             }
305             Lookup[] arr = ls.toArray(new Lookup[ls.size()]);
306             DataFolder df = (DataFolder)ois.readObject ();
307             String JavaDoc root = (String JavaDoc)ois.readObject ();
308             
309             fl = new FolderLookup (df, root, true);
310             fl.lookup = this;
311             
312             content = (AbstractLookup.Content)ois.readObject ();
313             
314             setLookups (arr);
315
316             readFromStream = true;
317             org.openide.util.RequestProcessor.getDefault ().post (fl, 0, Thread.MIN_PRIORITY);
318         }
319         
320         
321         /** Updates internal data.
322          * @param items Items to assign to all pairs
323          * @param lookups delegates to delegate to (first item is null)
324          */

325         public void update(Collection<ICItem> items, List<Lookup> lookups) {
326             readFromStream = false;
327             
328             // remember the instance lookup
329
Lookup pairs = getLookups ()[0];
330
331             // changes the its content
332
content.setPairs (items);
333             if (fl.err().isLoggable(Level.FINE)) fl.err ().fine("Changed pairs: " + items); // NOI18N
334

335             lookups.set(0, pairs);
336
337             Lookup[] arr = (Lookup[])lookups.toArray (new Lookup[lookups.size ()]);
338             setLookups (arr);
339             if (fl.err().isLoggable(Level.FINE)) fl.err ().fine("Changed lookups: " + lookups); // NOI18N
340
}
341         
342         /** Waits before the processing of changes is finished. */
343         protected void beforeLookup (Template template) {
344             if (readFromStream) {
345                 // ok
346
return;
347             }
348             
349             // do not wait in folder recognizer, but in all other cases
350
if (
351                 !FolderList.isFolderRecognizerThread() &&
352                 ICItem.DANGEROUS.get() == null
353             ) {
354                 if (!DataObjectPool.isConstructorAllowed()) {
355                     fl.waitFinished();
356                 } else {
357                     try {
358                         // try a bit but prevent deadlock from CanYouQueryFolderLookupFromHandleFindTest
359
while (!fl.waitFinished(10000)) {
360                             long blocked = DataObjectPool.getPOOL().timeInWaitNotified();
361                             if (blocked > 10000L) {
362                                 // folder recognizer thread is waiting for more then
363
// 10s in waitFinished, which signals that there
364
// is a very high possibility of a deadlock
365
fl.err().log(Level.INFO, "Preventing deadlock #65543: Do not call FolderLookup from inside DataObject operations!", new Exception JavaDoc("Thread dump")); // NOI18N
366
return;
367                             }
368                         }
369                     } catch (InterruptedException JavaDoc ex) {
370                         fl.err().log(Level.WARNING, null, ex);
371                     }
372                 }
373             }
374         }
375         
376         /** Mostly for testing purposes, to allow the tests to wait
377          * for the scan to finished after deserialization.
378          */

379         public void waitFinished () {
380             fl.waitFinished ();
381         }
382
383     } // End of ProxyLkp class.
384

385     
386     /** Item that delegates to <code>InstanceCookie</code>. Item which
387      * the internal lookup data structure is made from. */

388     private static final class ICItem extends AbstractLookup.Pair {
389         static final long serialVersionUID = 10L;
390         
391         static final ThreadLocal JavaDoc<ICItem> DANGEROUS = new ThreadLocal JavaDoc<ICItem> ();
392
393         /** error manager for ICItem */
394         private static final Logger ERR = Logger.getLogger(ICItem.class.getName());
395
396         /** when deserialized only primary file is stored */
397         private FileObject fo;
398         
399         private transient InstanceCookie ic;
400         /** source data object */
401         private transient DataObject obj;
402         /** reference to created object */
403         private transient Reference JavaDoc<Object JavaDoc> ref;
404         /** root folder */
405         private String JavaDoc rootName;
406
407         /** Constructs new item. */
408         public ICItem (DataObject obj, String JavaDoc rootName, InstanceCookie ic) {
409             this.ic = ic;
410             this.obj = obj;
411             this.rootName = rootName;
412             this.fo = obj.getPrimaryFile();
413             
414             if (ERR.isLoggable(Level.FINE)) ERR.fine("New ICItem: " + obj); // NOI18N
415
}
416         
417         /** Initializes the item
418          */

419         public void init () {
420             if (ic != null) return;
421
422             ICItem prev = DANGEROUS.get ();
423             try {
424                 DANGEROUS.set (this);
425                 if (obj == null) {
426                     try {
427                         obj = DataObject.find(fo);
428                     } catch (DataObjectNotFoundException donfe) {
429                         ic = new BrokenInstance("No DataObject for " + fo.getPath(), donfe); // NOI18N
430
return;
431                     }
432                 }
433
434                 ic = (InstanceCookie)obj.getCookie (InstanceCookie.class);
435                 if (ic == null) {
436                     ic = new BrokenInstance("No cookie for " + fo.getPath(), null); // NOI18N
437
}
438             } finally {
439                 DANGEROUS.set (prev);
440             }
441         }
442             
443         /**
444          * Fake instance cookie.
445          * Used in case a file had an instance in a previous session but now does not
446          * (or the data object could not even be created correctly).
447          */

448         private static final class BrokenInstance implements InstanceCookie.Of {
449             private final String JavaDoc message;
450             private final Exception JavaDoc ex;
451             public BrokenInstance(String JavaDoc message, Exception JavaDoc ex) {
452                 this.message = message;
453                 this.ex = ex;
454             }
455             public String JavaDoc instanceName() {
456                 return "java.lang.Object"; // NOI18N
457
}
458             private ClassNotFoundException JavaDoc die() {
459                 if (ex != null) {
460                     return new ClassNotFoundException JavaDoc(message, ex);
461                 } else {
462                     return new ClassNotFoundException JavaDoc(message);
463                 }
464             }
465             public Class JavaDoc instanceClass() throws IOException, ClassNotFoundException JavaDoc {
466                 throw die();
467             }
468             public Object JavaDoc instanceCreate() throws IOException, ClassNotFoundException JavaDoc {
469                 throw die();
470             }
471             public boolean instanceOf(Class JavaDoc type) {
472                 return false;
473             }
474         }
475
476
477         /** The class of the result item.
478          * @return the class of the item
479          */

480         protected boolean instanceOf (Class JavaDoc clazz) {
481             init ();
482             
483             if (ERR.isLoggable(Level.FINE)) ERR.fine("instanceOf: " + clazz.getName() + " obj: " + obj); // NOI18N
484

485             if (ic instanceof InstanceCookie.Of) {
486                 // special handling for special cookies
487
InstanceCookie.Of of = (InstanceCookie.Of)ic;
488                 boolean res = of.instanceOf (clazz);
489                 if (ERR.isLoggable(Level.FINE)) ERR.fine(" of: " + res); // NOI18N
490
return res;
491             }
492
493             // handling of normal instance cookies
494
try {
495                 boolean res = clazz.isAssignableFrom (ic.instanceClass ());
496                 if (ERR.isLoggable(Level.FINE)) ERR.fine(" plain: " + res); // NOI18N
497
return res;
498             } catch (ClassNotFoundException JavaDoc ex) {
499                 exception(ex, fo);
500             } catch (IOException ex) {
501                 exception(ex, fo);
502             }
503             return false;
504         }
505
506         /** The class of the result item.
507          * @return the instance of the object or null if it cannot be created
508          */

509         public Object JavaDoc getInstance() {
510             init ();
511             
512             try {
513                 Object JavaDoc obj = ic.instanceCreate();
514                 if (ERR.isLoggable(Level.FINE)) ERR.fine(" getInstance: " + obj + " for " + this.obj); // NOI18N
515
ref = new WeakReference JavaDoc<Object JavaDoc> (obj);
516                 return obj;
517             } catch (ClassNotFoundException JavaDoc ex) {
518                 exception(ex, fo);
519             } catch (IOException ex) {
520                 exception(ex, fo);
521             }
522             return null;
523         }
524
525         /** Hash code is the <code>InstanceCookie</code>'s code. */
526         public int hashCode () {
527             init ();
528             
529             return System.identityHashCode (ic);
530         }
531
532         /** Two items are equal if they point to the same cookie. */
533         public boolean equals (Object JavaDoc obj) {
534             if (obj instanceof ICItem) {
535                 ICItem i = (ICItem)obj;
536                 i.init ();
537                 init ();
538                 return ic == i.ic;
539             }
540             return false;
541         }
542
543         /** An identity of the item.
544          * @return string representing the item, that can be used for
545          * persistance purposes to locate the same item next time */

546         public String JavaDoc getId() {
547             init ();
548
549             if (obj == null) {
550                 // Deser problems.
551
return "<broken: " + fo.getPath() + ">"; // NOI18N
552
}
553             
554             return objectName(rootName, obj);
555         }
556
557         /** Display name is extracted from name of the objects node. */
558         public String JavaDoc getDisplayName () {
559             init ();
560             
561             if (obj == null) {
562                 // Deser problems.
563
return "<broken: " + fo.getPath() + ">"; // NOI18N
564
}
565             
566             return obj.getNodeDelegate ().getDisplayName ();
567         }
568
569         /** Method that can test whether an instance of a class has been created
570          * by this item.
571          *
572          * @param obj the instance
573          * @return if the item has already create an instance and it is the same
574          * as obj.
575          */

576         protected boolean creatorOf(Object JavaDoc obj) {
577             Reference JavaDoc w = ref;
578             if (w != null && w.get () == obj) {
579                 return true;
580             }
581             if (this.obj instanceof InstanceDataObject) {
582                 return ((InstanceDataObject)this.obj).creatorOf (obj);
583             }
584             return false;
585         }
586
587         /** The class of this item.
588          * @return the correct class
589          */

590         public Class JavaDoc getType() {
591             init ();
592             
593             try {
594                 return ic.instanceClass ();
595             } catch (IOException ex) {
596                 // ok, no class available
597
} catch (ClassNotFoundException JavaDoc ex) {
598                 // ok, no class available
599
}
600             return Object JavaDoc.class;
601         }
602
603     } // End of ICItem class.
604

605     
606     /** Data structure which holds <code>ICItem</code>'s and <code>Lookup</code>'s got
607      * from current folder and underlying sub-folders making it possible to
608      * pass to parent folder together. */

609     private static class FolderLookupData {
610
611         /** Collection of <code>ICItem</code>'s found in current
612          * folder and its sub-folders. */

613         private Collection<ICItem> items;
614         
615         /** List of <code>Lookup</code>'s found in current folder
616          * and its sub-folders. */

617         private List<Lookup> lookups;
618         
619         
620         /** Constructs data structure with inited fields. */
621         public FolderLookupData() {
622             items = new ArrayList<ICItem>(30);
623             lookups = new ArrayList<Lookup>(5);
624         }
625         
626     } // End of FolderLookupData class.
627
}
628
Popular Tags