KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > javacore > classpath > MergedClassPathImplementation


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.netbeans.modules.javacore.classpath;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.beans.PropertyChangeSupport JavaDoc;
25 import java.lang.ref.Reference JavaDoc;
26 import java.lang.ref.WeakReference JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Arrays JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.HashSet JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.Set JavaDoc;
38 import java.util.WeakHashMap JavaDoc;
39 import java.util.logging.Logger JavaDoc;
40 import javax.swing.event.ChangeEvent JavaDoc;
41 import javax.swing.event.ChangeListener JavaDoc;
42 import org.netbeans.api.java.classpath.ClassPath;
43 import org.netbeans.api.java.classpath.GlobalPathRegistry;
44 import org.netbeans.api.java.classpath.GlobalPathRegistryEvent;
45 import org.netbeans.api.java.classpath.GlobalPathRegistryListener;
46 import org.netbeans.api.java.queries.SourceForBinaryQuery;
47 import org.netbeans.spi.java.classpath.ClassPathImplementation;
48 import org.netbeans.spi.java.classpath.PathResourceImplementation;
49 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
50 import org.openide.ErrorManager;
51 import org.openide.filesystems.FileObject;
52 import org.openide.filesystems.FileStateInvalidException;
53 import org.openide.filesystems.URLMapper;
54 import org.openide.util.Utilities;
55 import org.openide.util.WeakListeners;
56
57 public class MergedClassPathImplementation implements ClassPathImplementation {
58
59     public static final String JavaDoc PROP_UNRESOLVED_ROOTS = "unresolvedRoots"; //NOI18N
60

61     private long eventCounter;
62     private Set JavaDoc roots;
63     private PropertyChangeSupport JavaDoc support;
64     private GlobalPathRegistry reg;
65     private final ArrayList JavaDoc/*<PathResourceImplementation>*/ cachedResources;
66     private List JavaDoc unresolvedRoots;
67     private List JavaDoc missingRoots;
68     private ClassPathMap resourceMap;
69     private GlobalPathRegistryListener gprListener;
70     private PropertyChangeListener JavaDoc pcListener = new PropertyChangeListener JavaDoc() {
71         public void propertyChange(PropertyChangeEvent JavaDoc event) {
72             assert event != null : "event == null"; //NOI18N
73
long id;
74             synchronized (MergedClassPathImplementation.this) {
75                 roots = null;
76                 id = (++eventCounter);
77             }
78             if (ClassPath.PROP_ENTRIES.equals(event.getPropertyName())) {
79                 MergedClassPathImplementation.this.updateEntries((ClassPath)event.getSource(),id);
80             }
81         }
82     };
83     
84     private WeakHashMap JavaDoc/*<ClassPath, WeakReference<SFBQListener>>*/ sfbResultListeners = new WeakHashMap JavaDoc(100);
85     
86     private static MergedClassPathImplementation instance;
87
88     private MergedClassPathImplementation () {
89         this.support = new PropertyChangeSupport JavaDoc(this);
90         this.cachedResources = new ArrayList JavaDoc ();
91         this.missingRoots = new ArrayList JavaDoc();
92         this.reg = GlobalPathRegistry.getDefault();
93         this.gprListener = new GlobalPathRegistryListener() {
94             public void pathsAdded(GlobalPathRegistryEvent event) {
95                 assert event != null : "event == null"; // NOI18N
96
synchronized (MergedClassPathImplementation.this) {
97                     roots = null;
98                 }
99                 MergedClassPathImplementation.this.updateEntries(event);
100                 MergedClassPathImplementation.this.firePropertyChange (PROP_UNRESOLVED_ROOTS);
101             }
102
103             public void pathsRemoved(GlobalPathRegistryEvent event) {
104             }
105         };
106         this.reg.addGlobalPathRegistryListener ((GlobalPathRegistryListener)
107                 WeakListeners.create(GlobalPathRegistryListener.class,this.gprListener,this.reg));
108         // XXX Shouldn't we also check for original contents of GPR?
109
assert this.reg != null : "GloabalPathRegistry.getDefault()==null"; //NOI18N
110
}
111
112
113     public synchronized List JavaDoc/*<PathResourceImplementation>*/ getResources() {
114         return Collections.unmodifiableList((List JavaDoc) this.cachedResources.clone());
115     }
116
117     public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
118         assert listener != null : "gprListener == null"; //NOI18N
119
this.support.addPropertyChangeListener(listener);
120     }
121
122     public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
123         assert listener != null : "gprListener == null"; //NOI18N
124
this.support.removePropertyChangeListener(listener);
125     }
126
127
128     public void addClassPaths (ClassPath[] cps) {
129         assert cps != null : "addClassPath called with null"; //NOI18N
130
for (int i = 0; i < cps.length; i++) {
131             if (cps[i]!=null) {
132                 this.addClassPath(cps[i]);
133             }
134         }
135         synchronized (this) {
136             roots = null;
137         }
138         this.firePropertyChange(PROP_UNRESOLVED_ROOTS);
139     }
140     
141     public void addRoot(URL JavaDoc url) {
142         synchronized (this) {
143             if (this.unresolvedRoots == null) {
144                 this.initEntries();
145             }
146             PathResourceImplementation root = ClassPathSupport.createResource(url);
147             unresolvedRoots.add(root);
148             roots = null;
149         }
150         this.firePropertyChange(PROP_UNRESOLVED_ROOTS);
151     }
152
153     public synchronized PathResourceImplementation[] getUnresolvedRoots () {
154         if (this.unresolvedRoots == null) {
155             this.initEntries();
156         }
157         return (PathResourceImplementation[]) this.unresolvedRoots.toArray (new PathResourceImplementation[this.unresolvedRoots.size()]);
158     }
159
160     public void classPathRootResolved (PathResourceImplementation impl) {
161         synchronized (this) {
162             if (this.unresolvedRoots.remove(impl)) {
163                 this.cachedResources.add(impl);
164             }
165             roots = null;
166         }
167         this.firePropertyChange (PROP_RESOURCES);
168     }
169
170     public synchronized void removeRoot(PathResourceImplementation impl) {
171         this.unresolvedRoots.remove(impl);
172         this.missingRoots.add(impl.getRoots()[0]);
173         roots = null;
174     }
175     
176     public boolean removeMissingRoot(URL JavaDoc url) {
177         boolean result = false;
178         synchronized (this) {
179             while (missingRoots.remove(url)) {
180                 PathResourceImplementation resource = ClassPathSupport.createResource(url);
181                 assert resource != null : "ClassPathSupport.createResource() returned null"; //NOI18N
182
unresolvedRoots.add(resource);
183                 result = true;
184                 roots = null;
185             }
186         }
187         if (result) {
188             firePropertyChange(PROP_UNRESOLVED_ROOTS);
189         }
190         return result;
191     }
192     
193     public boolean addMissingRoot(URL JavaDoc url) {
194         boolean result = false;
195         synchronized (this) {
196             PathResourceImplementation resource = ClassPathSupport.createResource(url);
197             while (this.cachedResources.remove(resource)) {
198                 missingRoots.add(url);
199                 result = true;
200                 roots = null;
201             }
202         }
203         if (result) {
204             this.firePropertyChange(PROP_RESOURCES);
205         }
206         return result;
207     }
208     
209     public boolean updateRoot(URL JavaDoc url) {
210         boolean result = false;
211         synchronized (this) {
212             PathResourceImplementation resource = ClassPathSupport.createResource(url);
213             while (this.cachedResources.remove(resource)) {
214                 unresolvedRoots.add(resource);
215                 result = true;
216                 roots = null;
217             }
218         }
219         if (result) {
220             firePropertyChange(PROP_RESOURCES);
221             firePropertyChange(PROP_UNRESOLVED_ROOTS);
222         }
223         return result;
224     }
225
226     public void addUnresolvedRoots (List JavaDoc/*<PathResourceImplementation>*/ unresolvedRoots) {
227         synchronized (this) {
228             this.unresolvedRoots.addAll (unresolvedRoots);
229             roots = null;
230         }
231         this.firePropertyChange(PROP_UNRESOLVED_ROOTS);
232     }
233
234     /**
235      * Slower version of classPathRootResolved, use classPathRootResolved(PathResourceImplementation)
236      * where possible
237      * @param url
238      */

239     public void classPathRootResolved (URL JavaDoc url) {
240         synchronized (this) {
241             for (Iterator JavaDoc it = unresolvedRoots.iterator(); it.hasNext();) {
242                 PathResourceImplementation resource = (PathResourceImplementation) it.next ();
243                 if (resource.getRoots()[0].equals(url)) {
244                     it.remove();
245                     this.cachedResources.add(resource);
246                     break;
247                 }
248             }
249             roots = null;
250         }
251         this.firePropertyChange (PROP_RESOURCES);
252     }
253
254     private synchronized void initEntries () {
255         Set JavaDoc classPaths = new HashSet JavaDoc ();
256         roots = null;
257         this.unresolvedRoots = new ArrayList JavaDoc ();
258         this.resourceMap = new ClassPathMap();
259         classPaths.addAll(this.reg.getPaths(ClassPath.SOURCE));
260         classPaths.addAll(this.reg.getPaths(ClassPath.COMPILE));
261         classPaths.addAll(this.reg.getPaths(ClassPath.BOOT));
262         for(Iterator JavaDoc it = classPaths.iterator();it.hasNext();) {
263             ClassPath cp = (ClassPath) it.next ();
264             this.addClassPath (cp);
265         }
266     }
267
268     private synchronized void updateEntries (GlobalPathRegistryEvent event) {
269         if (this.cachedResources == null)
270             return;
271         for (Iterator JavaDoc it = event.getChangedPaths().iterator(); it.hasNext();) {
272             ClassPath cp = (ClassPath) it.next();
273             this.addClassPath (cp);
274         }
275     }
276
277     private void updateEntries (final ClassPath cp, final long eid) {
278         
279         boolean fire = false;
280         List JavaDoc oldResources;
281         Logger.getLogger("TEST-"+MergedClassPathImplementation.class.getName()).finest("testRaceCondition84603"); //NOI18N
282
List JavaDoc newResources = addClassPathResources (cp);
283         synchronized (this) {
284             if (eid == eventCounter) {
285                 oldResources = (List JavaDoc) this.resourceMap.remove (cp);
286                 assert oldResources != null : "Change in unknown classpath"; // NOI18N
287
Collection JavaDoc toRemove = new HashSet JavaDoc (oldResources);
288                 toRemove.removeAll (newResources);
289                 newResources.removeAll (oldResources); //To Add
290
for (Iterator JavaDoc it = toRemove.iterator(); it.hasNext();) {
291                     PathResourceImplementation resource = (PathResourceImplementation) it.next();
292                     oldResources.remove(resource);
293                     if (!this.cachedResources.remove(resource)) {
294                         if (!this.unresolvedRoots.remove (resource)) {
295                             missingRoots.remove(resource.getRoots()[0]);
296                         }
297                     }
298                     else {
299                         fire = true;
300                     }
301                 }
302                 for (Iterator JavaDoc it = newResources.iterator(); it.hasNext();) {
303                     PathResourceImplementation resource = (PathResourceImplementation) it.next();
304                     oldResources.add (resource);
305                 }
306                 this.resourceMap.put (cp,oldResources);
307                 this.unresolvedRoots.addAll(newResources);
308             }
309         }
310         if (fire) {
311             this.firePropertyChange(PROP_RESOURCES);
312         }
313         this.firePropertyChange(PROP_UNRESOLVED_ROOTS);
314     }
315
316     private void addClassPath (ClassPath cp) {
317         synchronized (this) {
318             if (this.resourceMap == null) {
319                 initEntries();
320             }
321             if (this.resourceMap.containsKey(cp)) {
322                 return;
323             }
324         }
325         List JavaDoc c = addClassPathResources(cp);
326         synchronized (this) {
327             if (!this.resourceMap.containsKey(cp)) {
328                 this.resourceMap.put(cp,c);
329                 this.unresolvedRoots.addAll(c);
330                 cp.addPropertyChangeListener((PropertyChangeListener JavaDoc)WeakListeners.create(PropertyChangeListener JavaDoc.class,this.pcListener,cp));
331             }
332         }
333     }
334     
335     private void firePropertyChange (String JavaDoc propName) {
336         this.support.firePropertyChange(propName,null,null);
337     }
338     
339     private class SFBQListener implements ChangeListener JavaDoc, PropertyChangeListener JavaDoc {
340         private final WeakReference JavaDoc cp;
341         private Map JavaDoc/*<URL,WeakReference<SourceForBinaryQuery.Result>>*/ results;
342         
343         public SFBQListener(ClassPath cp) {
344             this.cp = new WeakReference JavaDoc(cp);
345         // The listener attached just to have strong reference
346
// from cp to SFBQListener, it is otherwise only weakly reachable
347
// and we need similar lifecycyle as the cp instance.
348
cp.addPropertyChangeListener(this);
349         }
350         
351     public void propertyChange(PropertyChangeEvent JavaDoc event) {
352             // ignore
353
}
354
355         public SourceForBinaryQuery.Result getResult(URL JavaDoc url) {
356             if (results == null) {
357                 results = new HashMap JavaDoc();
358             }
359             SourceForBinaryQuery.Result result;
360             Reference JavaDoc ref = (Reference JavaDoc) results.get(url);
361             if (ref == null || (result = (SourceForBinaryQuery.Result) ref.get ()) == null) {
362                 result = SourceForBinaryQuery.findSourceRoots(url);
363                 results.put(url, new WeakReference JavaDoc(result));
364                 result.addChangeListener(WeakListeners.change(this, result));
365             }
366             return result;
367         }
368
369         public void stateChanged(ChangeEvent JavaDoc e) {
370             //System.err.println("SourceForBinaryQuery.Result changed");
371
ClassPath classPath = (ClassPath) cp.get();
372             if (classPath != null) {
373                 long id;
374                 synchronized (MergedClassPathImplementation.this) {
375                     id = (++eventCounter);
376                 }
377                 updateEntries(classPath,id);
378             }
379         }
380     }
381
382     private List JavaDoc addClassPathResources (final ClassPath cp) {
383         List JavaDoc entries = null;
384         SFBQListener listener = null;
385         synchronized (this) {
386             WeakReference JavaDoc wr = (WeakReference JavaDoc) sfbResultListeners.get(cp);
387             listener = (SFBQListener) (wr == null ? null : wr.get());
388             if (listener == null) {
389                 listener = new SFBQListener(cp);
390                 sfbResultListeners.put(cp, new WeakReference JavaDoc(listener));
391             }
392         }
393         entries = cp.entries();
394         List JavaDoc c = new ArrayList JavaDoc();
395         for (Iterator JavaDoc et = entries.iterator(); et.hasNext();) {
396             ClassPath.Entry entry = (ClassPath.Entry)et.next();
397             URL JavaDoc url = entry.getURL();
398             assert url != null : "ClassPath.Entry.getURL() returned null"; //NOI18N
399
addResources(url, listener, c);
400         }
401         return c;
402     }
403
404     private static void addResources (URL JavaDoc url, SFBQListener sfbResultListener, List JavaDoc resourceList) {
405         PathResourceImplementation resource = ClassPathSupport.createResource(url);
406         assert resource != null : "ClassPathSupport.createResource() returned null"; //NOI18N
407
boolean needsBinary = true;
408         SourceForBinaryQuery.Result result = sfbResultListener.getResult(url);
409         FileObject[] sources = result.getRoots();
410         List JavaDoc/*<FileObject>*/ sourcesL = Arrays.asList(sources);
411         if (sourcesL.contains(null)) {
412             // Diagnostic; cf. e.g. #58402.
413
ErrorManager.getDefault().log(ErrorManager.WARNING, "Warning: " + result.getClass().getName() + " illegally returned a null element from getResult(URL): " + sourcesL);
414             return;
415         }
416         List JavaDoc tempResult = new ArrayList JavaDoc(sources.length);
417         for (int i=0; i< sources.length; i++) {
418             try {
419                 URL JavaDoc surl = sources[i].getURL();
420                 if ("file".equals(surl.getProtocol())) needsBinary = false; // NOI18N
421
PathResourceImplementation sresource = ClassPathSupport.createResource(surl);
422                 assert sresource != null : "ClassPathSupport.createResource() returned null"; //NOI18N
423
tempResult.add(sresource);
424             } catch (FileStateInvalidException e) {
425                 ErrorManager.getDefault().notify(e);
426             }
427         }
428         if (needsBinary) resourceList.add(resource);
429         resourceList.addAll(tempResult);
430     }
431
432
433     public synchronized static MergedClassPathImplementation getDefault () {
434         if (instance == null ) {
435             instance = new MergedClassPathImplementation();
436         }
437         return instance;
438     }
439
440
441     private class ClassPathMap {
442
443         private List JavaDoc data;
444
445         public ClassPathMap () {
446             this.data = new ArrayList JavaDoc ();
447         }
448
449         public void put (Object JavaDoc key, Object JavaDoc value ) {
450             synchronized (MergedClassPathImplementation.this) {
451                 WeakPair wp = new WeakPair (key, value);
452                 data.add (wp);
453             }
454         }
455
456         public Object JavaDoc remove (Object JavaDoc key) {
457             if (key == null) {
458                 return null;
459             }
460             synchronized (MergedClassPathImplementation.this) {
461                 for (Iterator JavaDoc it = this.data.iterator(); it.hasNext();) {
462                     WeakPair pair = (WeakPair) it.next ();
463                     Object JavaDoc wpk = pair.getKey();
464                     if (key.equals(wpk)) {
465                         it.remove();
466                         return pair.getValue();
467                     }
468                 }
469             }
470             return null;
471         }
472
473         public boolean containsKey (Object JavaDoc key) {
474             if (key == null) {
475                 return false;
476             }
477             Iterator JavaDoc it;
478             synchronized (MergedClassPathImplementation.this) {
479                 it = new ArrayList JavaDoc(data).iterator();
480             }
481             while (it.hasNext()) {
482                 WeakPair pair = (WeakPair) it.next();
483                 Object JavaDoc pk = pair.getKey();
484                 if (key.equals(pk)) {
485                     return true;
486                 }
487             }
488             return false;
489         }
490
491         private void cleanUp (WeakPair toClean) {
492             boolean fire = false;
493             synchronized (MergedClassPathImplementation.this) {
494                 for (Iterator JavaDoc it = this.data.iterator(); it.hasNext();) {
495                     WeakPair pair = (WeakPair) it.next ();
496                     if (pair == toClean) {
497                         it.remove();
498                         for (Iterator JavaDoc resIt= ((Collection JavaDoc)pair.getValue()).iterator(); resIt.hasNext();) {
499                             PathResourceImplementation resource = (PathResourceImplementation) resIt.next();
500                             if (!MergedClassPathImplementation.this.cachedResources.remove(resource)) {
501                                 if (!MergedClassPathImplementation.this.unresolvedRoots.remove(resource)) {
502                                     missingRoots.remove(resource.getRoots()[0]);
503                                 }
504                             } else {
505                                 fire = true;
506                                 roots = null;
507                             }
508                         }
509                         break;
510                     }
511                 }
512             }
513             if (fire) {
514                 firePropertyChange(PROP_RESOURCES);
515             }
516         }
517
518         private class WeakPair extends WeakReference JavaDoc implements Runnable JavaDoc {
519
520             private Object JavaDoc value;
521
522             public WeakPair (Object JavaDoc key, Object JavaDoc value) {
523                 super (key, Utilities.activeReferenceQueue());
524                 this.value = value;
525             }
526
527             public Object JavaDoc getKey () {
528                 return get ();
529             }
530
531             public Object JavaDoc getValue () {
532                 return value;
533             }
534
535             public void run() {
536                 cleanUp(this);
537             }
538         }
539
540     }
541     
542     public Set JavaDoc getRoots() {
543         List JavaDoc _cachedResources;
544         synchronized (this) {
545             if (this.roots != null) {
546                 return this.roots;
547             }
548             _cachedResources = this.getResources(); //Save copy
549
}
550         Set JavaDoc _roots = new HashSet JavaDoc();
551         for (Iterator JavaDoc it = _cachedResources.iterator(); it.hasNext();) {
552             PathResourceImplementation res = (PathResourceImplementation) it.next();
553             URL JavaDoc[] urls = res.getRoots();
554             for (int i = 0; i < urls.length; i++) {
555                 FileObject obj = URLMapper.findFileObject(urls[i]);
556                 if (obj != null) {
557                     _roots.add(obj);
558                 }
559             }
560         }
561         synchronized (this) {
562             if (this.roots == null) {
563                 this.roots = Collections.unmodifiableSet(_roots);
564             }
565             return roots;
566         }
567         
568     }
569     
570     public boolean contains(FileObject fo) {
571         return findOwnerRoot(fo) != null;
572     }
573     
574     public FileObject findOwnerRoot(FileObject fo) {
575         Set JavaDoc roots = getRoots();
576         for (FileObject f = fo; f != null; f = f.getParent()) {
577             if (roots.contains(f)) {
578                 return f;
579             }
580         }
581         return null;
582     }
583 }
584
Popular Tags