KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > freeform > Classpaths


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.java.freeform;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.beans.PropertyChangeSupport JavaDoc;
25 import java.io.File JavaDoc;
26 import java.net.MalformedURLException JavaDoc;
27 import java.net.URI JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Arrays 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.LinkedHashMap JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Map JavaDoc;
38 import java.util.Set JavaDoc;
39 import java.util.WeakHashMap JavaDoc;
40 import java.util.concurrent.CountDownLatch JavaDoc;
41 import java.util.concurrent.TimeUnit JavaDoc;
42 import org.netbeans.api.java.classpath.ClassPath;
43 import org.netbeans.api.java.classpath.GlobalPathRegistry;
44 import org.netbeans.api.java.platform.JavaPlatform;
45 import org.netbeans.api.java.platform.JavaPlatformManager;
46 import org.netbeans.api.java.platform.Specification;
47 import org.netbeans.api.java.project.JavaProjectConstants;
48 import org.netbeans.api.project.ProjectManager;
49 import org.netbeans.modules.ant.freeform.spi.support.Util;
50 import org.netbeans.modules.java.freeform.jdkselection.JdkConfiguration;
51 import org.netbeans.spi.java.classpath.ClassPathFactory;
52 import org.netbeans.spi.java.classpath.ClassPathImplementation;
53 import org.netbeans.spi.java.classpath.ClassPathProvider;
54 import org.netbeans.spi.java.classpath.FilteringPathResourceImplementation;
55 import org.netbeans.spi.java.classpath.PathResourceImplementation;
56 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
57 import org.netbeans.spi.project.AuxiliaryConfiguration;
58 import org.netbeans.spi.project.support.ant.AntProjectEvent;
59 import org.netbeans.spi.project.support.ant.AntProjectHelper;
60 import org.netbeans.spi.project.support.ant.AntProjectListener;
61 import org.netbeans.spi.project.support.ant.PathMatcher;
62 import org.netbeans.spi.project.support.ant.PropertyEvaluator;
63 import org.netbeans.spi.project.support.ant.PropertyUtils;
64 import org.openide.ErrorManager;
65 import org.openide.filesystems.FileObject;
66 import org.openide.filesystems.FileUtil;
67 import org.openide.modules.SpecificationVersion;
68 import org.openide.util.Mutex;
69 import org.openide.util.Utilities;
70 import org.openide.util.WeakListeners;
71 import org.w3c.dom.Element JavaDoc;
72
73 /**
74  * Handle classpaths for the freeform project.
75  * Keeps three caches:
76  * <ol>
77  * <li>Classpaths registered when the project is opened. The same are unregistered
78  * when it is closed again, regardless of what might have changed since.
79  * <li>A map from abstract compilation units (keyed by the literal text of the
80  * <code>&lt;package-root&gt;</code> elements) to implementations which do the
81  * actual listening and (re-)computation of roots.
82  * <li>A map from actual package roots to the matching classpath.
83  * </ol>
84  * The complexity here is needed because
85  * <ol>
86  * <li>It is necessary to always unregister the exact same set of ClassPath objects
87  * you initially registered (even if some have since become invalid, etc.).
88  * Ideally, adding or removing whole paths would dynamically register or unregister
89  * them (if the project is currently open); the current code does not do this.
90  * <li>A given ClassPath object must fire changes if its list of roots changes.
91  * <li>It is necessary to return the same ClassPath object for the same FileObject.
92  * </ol>
93  * @author Jesse Glick
94  */

95 final class Classpaths implements ClassPathProvider, AntProjectListener, PropertyChangeListener JavaDoc {
96     
97     private static final ErrorManager err = ErrorManager.getDefault().getInstance(Classpaths.class.getName());
98
99     //for tests only:
100
static CountDownLatch JavaDoc TESTING_LATCH = null;
101     
102     private AntProjectHelper helper;
103     private PropertyEvaluator evaluator;
104     private AuxiliaryConfiguration aux;
105     
106     /**
107      * Map from classpath types to maps from package roots to classpaths.
108      */

109     private final Map JavaDoc<String JavaDoc,Map JavaDoc<FileObject,ClassPath>> classpaths = new HashMap JavaDoc<String JavaDoc,Map JavaDoc<FileObject,ClassPath>>();
110     
111     /**
112      * Map from classpath types to maps from lists of package root names to classpath impls.
113      */

114     private final Map JavaDoc<String JavaDoc,Map JavaDoc<List JavaDoc<String JavaDoc>,MutableClassPathImplementation>> mutablePathImpls = new HashMap JavaDoc<String JavaDoc,Map JavaDoc<List JavaDoc<String JavaDoc>,MutableClassPathImplementation>>();
115     
116     private final Map JavaDoc<MutableClassPathImplementation,ClassPath> mutableClassPathImpl2ClassPath = new HashMap JavaDoc<MutableClassPathImplementation,ClassPath>();
117     
118     /**
119      * Map from classpath types to sets of classpaths we last registered to GlobalPathRegistry.
120      */

121     private Map JavaDoc<String JavaDoc,Set JavaDoc<ClassPath>> registeredClasspaths = null;
122
123     public Classpaths(AntProjectHelper helper, PropertyEvaluator evaluator, AuxiliaryConfiguration aux) {
124         this.helper = helper;
125         this.evaluator = evaluator;
126         this.aux = aux;
127         helper.addAntProjectListener(this);
128         evaluator.addPropertyChangeListener(this);
129     }
130     
131     public ClassPath findClassPath(final FileObject file, final String JavaDoc type) {
132         //#77015: the findClassPathImpl method takes read access on ProjectManager.mutex
133
//taking the read access before the private lock to prevent deadlocks.
134
return ProjectManager.mutex().readAccess(new Mutex.Action<ClassPath>() {
135             public ClassPath run() {
136                 return findClassPathImpl(file, type);
137             }
138         });
139     }
140
141     private synchronized ClassPath findClassPathImpl(FileObject file, String JavaDoc type) {
142         if (TESTING_LATCH != null) {
143             //only for tests:
144
TESTING_LATCH.countDown();
145             try {
146                 TESTING_LATCH.await(2, TimeUnit.SECONDS);
147             } catch (InterruptedException JavaDoc e) {
148                 ErrorManager.getDefault().notify(e);
149             }
150             
151             classpaths.clear();
152         }
153         
154         Map JavaDoc<FileObject,ClassPath> classpathsByType = classpaths.get(type);
155         if (classpathsByType == null) {
156             classpathsByType = new WeakHashMap JavaDoc<FileObject,ClassPath>();
157             classpaths.put(type, classpathsByType);
158         }
159         // Check for cached value.
160
Iterator JavaDoc it = classpathsByType.entrySet().iterator();
161         while (it.hasNext()) {
162             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)it.next();
163             FileObject root = (FileObject)entry.getKey();
164             if (root == file || FileUtil.isParentOf(root, file)) {
165                 // Already have it.
166
return (ClassPath)entry.getValue();
167             }
168         }
169         // Need to create it.
170
Element JavaDoc java = aux.getConfigurationFragment(JavaProjectNature.EL_JAVA, JavaProjectNature.NS_JAVA_2, true);
171         if (java == null) {
172             return null;
173         }
174         List JavaDoc<Element JavaDoc> compilationUnits = Util.findSubElements(java);
175         it = compilationUnits.iterator();
176         while (it.hasNext()) {
177             Element JavaDoc compilationUnitEl = (Element JavaDoc)it.next();
178             assert compilationUnitEl.getLocalName().equals("compilation-unit") : compilationUnitEl;
179             List JavaDoc<FileObject> packageRoots = findPackageRoots(helper, evaluator, compilationUnitEl);
180             Iterator JavaDoc it2 = packageRoots.iterator();
181             while (it2.hasNext()) {
182                 FileObject root = (FileObject)it2.next();
183                 if (root == file || FileUtil.isParentOf(root, file)) {
184                     // Got it. Compute classpath and cache it (for each root).
185
ClassPath cp = getPath(compilationUnitEl, packageRoots, type);
186                     it2 = packageRoots.iterator();
187                     while (it2.hasNext()) {
188                         FileObject root2 = (FileObject)it2.next();
189                         classpathsByType.put(root2, cp);
190                     }
191                     return cp;
192                 }
193             }
194         }
195         return null;
196     }
197     
198     /** All classpath types we handle. */
199     private static final String JavaDoc[] TYPES = {
200         ClassPath.SOURCE,
201         ClassPath.BOOT,
202         ClassPath.EXECUTE,
203         ClassPath.COMPILE,
204     };
205     
206     /**
207      * Called when project is opened.
208      * Tries to find all compilation units and calculate all the paths needed
209      * for each of them and register them all.
210      */

211     public void opened() {
212         // #97366: taking read access to prevent deadlock, same trick as #77015
213
ProjectManager.mutex().readAccess(new Mutex.Action<Void JavaDoc>() {
214             public Void JavaDoc run() {
215                 openedImpl();
216                 return null;
217             }
218         });
219     }
220     
221     private synchronized void openedImpl() {
222         if (registeredClasspaths != null) {
223             return;
224         }
225         Map JavaDoc<String JavaDoc,Set JavaDoc<ClassPath>> _registeredClasspaths = new HashMap JavaDoc<String JavaDoc,Set JavaDoc<ClassPath>>();
226         for (String JavaDoc type : TYPES) {
227             _registeredClasspaths.put(type, new HashSet JavaDoc<ClassPath>());
228         }
229         Element JavaDoc java = aux.getConfigurationFragment(JavaProjectNature.EL_JAVA, JavaProjectNature.NS_JAVA_2, true);
230         if (java == null) {
231             return;
232         }
233         for (Element JavaDoc compilationUnitEl : Util.findSubElements(java)) {
234             assert compilationUnitEl.getLocalName().equals("compilation-unit") : compilationUnitEl;
235             // For each compilation unit, find the package roots first.
236
List JavaDoc<FileObject> packageRoots = findPackageRoots(helper, evaluator, compilationUnitEl);
237             for (String JavaDoc type : TYPES) {
238                 // Then for each type, collect the classpath (creating it as needed).
239
Map JavaDoc<FileObject,ClassPath> classpathsByType = classpaths.get(type);
240                 if (classpathsByType == null) {
241                     classpathsByType = new WeakHashMap JavaDoc<FileObject,ClassPath>();
242                     classpaths.put(type, classpathsByType);
243                 }
244                 Set JavaDoc<ClassPath> registeredClasspathsOfType = _registeredClasspaths.get(type);
245                 assert registeredClasspathsOfType != null;
246                 // Check if there is already a ClassPath registered to one of these roots.
247
ClassPath cp = null;
248                 for (FileObject root : packageRoots) {
249                     cp = classpathsByType.get(root);
250                     if (cp != null) {
251                         break;
252                     }
253                 }
254                 if (cp == null) {
255                     // Nope. Calculate and register it now.
256
cp = getPath(compilationUnitEl, packageRoots, type);
257                     for (FileObject root : packageRoots) {
258                         classpathsByType.put(root, cp);
259                     }
260                 }
261                 assert cp != null;
262                 registeredClasspathsOfType.add(cp);
263             }
264         }
265         if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
266             err.log("classpaths for " + helper.getProjectDirectory() + ": " + classpaths);
267         }
268         // Don't do this before it is calculated, or a runtime error above might corrupt state:
269
this.registeredClasspaths = _registeredClasspaths;
270         // Register all of the classpaths we found.
271
GlobalPathRegistry gpr = GlobalPathRegistry.getDefault();
272         for (String JavaDoc type : TYPES) {
273             Set JavaDoc<ClassPath> registeredClasspathsOfType = registeredClasspaths.get(type);
274             gpr.register(type, registeredClasspathsOfType.toArray(new ClassPath[registeredClasspathsOfType.size()]));
275         }
276     }
277     
278     private synchronized void registerNewClasspath(String JavaDoc type, ClassPath cp) {
279         if (registeredClasspaths == null) {
280             return;
281         }
282         Set JavaDoc<ClassPath> s = registeredClasspaths.get(type);
283         s.add(cp);
284         GlobalPathRegistry.getDefault().register(type, new ClassPath[] {cp});
285     }
286     
287     /**
288      * Called when project is closed.
289      * Unregisters any previously registered classpaths.
290      */

291     public synchronized void closed() {
292         if (registeredClasspaths == null) {
293             return;
294         }
295         GlobalPathRegistry gpr = GlobalPathRegistry.getDefault();
296         for (int i = 0; i < TYPES.length; i++) {
297             String JavaDoc type = TYPES[i];
298             Set JavaDoc<ClassPath> registeredClasspathsOfType = registeredClasspaths.get(type);
299             gpr.unregister(type, registeredClasspathsOfType.toArray(new ClassPath[registeredClasspathsOfType.size()]));
300         }
301         registeredClasspaths = null;
302     }
303     
304     static List JavaDoc<String JavaDoc> findPackageRootNames(Element JavaDoc compilationUnitEl) {
305         List JavaDoc<String JavaDoc> names = new ArrayList JavaDoc<String JavaDoc>();
306         Iterator JavaDoc it = Util.findSubElements(compilationUnitEl).iterator();
307         while (it.hasNext()) {
308             Element JavaDoc e = (Element JavaDoc) it.next();
309             if (!e.getLocalName().equals("package-root")) { // NOI18N
310
continue;
311             }
312             String JavaDoc location = Util.findText(e);
313             names.add(location);
314         }
315         return names;
316     }
317     
318     static Map JavaDoc<String JavaDoc,FileObject> findPackageRootsByName(AntProjectHelper helper, PropertyEvaluator evaluator, List JavaDoc<String JavaDoc> packageRootNames) {
319         Map JavaDoc<String JavaDoc,FileObject> roots = new LinkedHashMap JavaDoc<String JavaDoc,FileObject>();
320         Iterator JavaDoc it = packageRootNames.iterator();
321         while (it.hasNext()) {
322             String JavaDoc location = (String JavaDoc) it.next();
323             String JavaDoc locationEval = evaluator.evaluate(location);
324             if (locationEval != null) {
325                 File JavaDoc locationFile = helper.resolveFile(locationEval);
326                 FileObject locationFileObject = FileUtil.toFileObject(locationFile);
327                 if (locationFileObject != null) {
328                     if (FileUtil.isArchiveFile(locationFileObject)) {
329                         locationFileObject = FileUtil.getArchiveRoot(locationFileObject);
330                     }
331                     roots.put(location, locationFileObject);
332                 }
333             }
334         }
335         return roots;
336     }
337     
338     private static List JavaDoc<FileObject> findPackageRoots(AntProjectHelper helper, PropertyEvaluator evaluator, List JavaDoc<String JavaDoc> packageRootNames) {
339         return new ArrayList JavaDoc<FileObject>(findPackageRootsByName(helper, evaluator, packageRootNames).values());
340     }
341     
342     public static List JavaDoc<FileObject> findPackageRoots(AntProjectHelper helper, PropertyEvaluator evaluator, Element JavaDoc compilationUnitEl) {
343         return findPackageRoots(helper, evaluator, findPackageRootNames(compilationUnitEl));
344     }
345     
346     private ClassPath getPath(Element JavaDoc compilationUnitEl, List JavaDoc<FileObject> packageRoots, String JavaDoc type) {
347         if (type.equals(ClassPath.SOURCE) || type.equals(ClassPath.COMPILE) ||
348                 type.equals(ClassPath.EXECUTE) || type.equals(ClassPath.BOOT)) {
349             List JavaDoc<String JavaDoc> packageRootNames = findPackageRootNames(compilationUnitEl);
350             Map JavaDoc<List JavaDoc<String JavaDoc>,MutableClassPathImplementation> mutablePathImplsByType = mutablePathImpls.get(type);
351             if (mutablePathImplsByType == null) {
352                 mutablePathImplsByType = new HashMap JavaDoc<List JavaDoc<String JavaDoc>,MutableClassPathImplementation>();
353                 mutablePathImpls.put(type, mutablePathImplsByType);
354             }
355             MutableClassPathImplementation impl = mutablePathImplsByType.get(packageRootNames);
356             if (impl == null) {
357                 // XXX will it ever not be null?
358
impl = new MutableClassPathImplementation(packageRootNames, type, compilationUnitEl);
359                 mutablePathImplsByType.put(packageRootNames, impl);
360             }
361             ClassPath cp = mutableClassPathImpl2ClassPath.get(impl);
362             if (cp == null) {
363                 cp = ClassPathFactory.createClassPath(impl);
364                 mutableClassPathImpl2ClassPath.put(impl, cp);
365                 registerNewClasspath(type, cp);
366             }
367             return cp;
368         } else {
369             // Unknown.
370
return null;
371         }
372     }
373     
374     private List JavaDoc<URL JavaDoc> createSourcePath(List JavaDoc<String JavaDoc> packageRootNames) {
375         List JavaDoc<URL JavaDoc> roots = new ArrayList JavaDoc<URL JavaDoc>(packageRootNames.size());
376         for (String JavaDoc location : packageRootNames) {
377             String JavaDoc locationEval = evaluator.evaluate(location);
378             if (locationEval != null) {
379                 roots.add(createClasspathEntry(locationEval));
380             }
381         }
382         return roots;
383     }
384     
385     private List JavaDoc<URL JavaDoc> createCompileClasspath(Element JavaDoc compilationUnitEl) {
386         for (Element JavaDoc e : Util.findSubElements(compilationUnitEl)) {
387             if (e.getLocalName().equals("classpath") && e.getAttribute("mode").equals("compile")) { // NOI18N
388
return createClasspath(e);
389             }
390         }
391         // None specified; assume it is empty.
392
return Collections.emptyList();
393     }
394     
395     /**
396      * Create a classpath from a &lt;classpath&gt; element.
397      */

398     private List JavaDoc<URL JavaDoc> createClasspath(Element JavaDoc classpathEl) {
399         String JavaDoc cp = Util.findText(classpathEl);
400         if (cp == null) {
401             cp = "";
402         }
403         String JavaDoc cpEval = evaluator.evaluate(cp);
404         if (cpEval == null) {
405             return Collections.emptyList();
406         }
407         String JavaDoc[] path = PropertyUtils.tokenizePath(cpEval);
408         URL JavaDoc[] pathURL = new URL JavaDoc[path.length];
409         for (int i = 0; i < path.length; i++) {
410             pathURL[i] = createClasspathEntry(path[i]);
411         }
412         return Arrays.asList(pathURL);
413     }
414     
415     private URL JavaDoc createClasspathEntry(String JavaDoc text) {
416         File JavaDoc entryFile = helper.resolveFile(text);
417         URL JavaDoc entry;
418         try {
419             entry = entryFile.toURI().toURL();
420         } catch (MalformedURLException JavaDoc x) {
421             throw new AssertionError JavaDoc(x);
422         }
423         if (FileUtil.isArchiveFile(entry)) {
424             return FileUtil.getArchiveRoot(entry);
425         } else {
426             String JavaDoc entryS = entry.toExternalForm();
427             if (!entryS.endsWith("/")) { // NOI18N
428
// A nonexistent dir. Have to add trailing slash ourselves.
429
try {
430                     return new URL JavaDoc(entryS + '/');
431                 } catch (MalformedURLException JavaDoc x) {
432                     throw new AssertionError JavaDoc(x);
433                 }
434             } else {
435                 return entry;
436             }
437         }
438     }
439     
440     private List JavaDoc<URL JavaDoc> createExecuteClasspath(List JavaDoc<String JavaDoc> packageRoots, Element JavaDoc compilationUnitEl) {
441         for (Element JavaDoc e : Util.findSubElements(compilationUnitEl)) {
442             if (e.getLocalName().equals("classpath") && e.getAttribute("mode").equals("execute")) { // NOI18N
443
return createClasspath(e);
444             }
445         }
446         // None specified; assume it is same as compile classpath plus (cf. #49113) <built-to> dirs/JARs
447
// if there are any (else include the source dir(s) as a fallback for the I18N wizard to work).
448
List JavaDoc<URL JavaDoc> urls = new ArrayList JavaDoc<URL JavaDoc>();
449         urls.addAll(createCompileClasspath(compilationUnitEl));
450         boolean foundBuiltTos = false;
451         for (Element JavaDoc builtTo : Util.findSubElements(compilationUnitEl)) {
452             if (!builtTo.getLocalName().equals("built-to")) { // NOI18N
453
continue;
454             }
455             foundBuiltTos = true;
456             String JavaDoc rawtext = Util.findText(builtTo);
457             assert rawtext != null : "Must have nonempty text inside <built-to>";
458             String JavaDoc text = evaluator.evaluate(rawtext);
459             if (text == null) {
460                 continue;
461             }
462             urls.add(createClasspathEntry(text));
463         }
464         if (!foundBuiltTos) {
465             urls.addAll(createSourcePath(packageRoots));
466         }
467         return urls;
468     }
469     
470     private List JavaDoc<URL JavaDoc> createBootClasspath(Element JavaDoc compilationUnitEl) {
471         for (Element JavaDoc e : Util.findSubElements(compilationUnitEl)) {
472             if (e.getLocalName().equals("classpath") && e.getAttribute("mode").equals("boot")) { // NOI18N
473
return createClasspath(e);
474             }
475         }
476         // None specified; try to find a matching Java platform.
477
// First check whether user has configured a specific JDK.
478
JavaPlatform platform = new JdkConfiguration(null, helper, evaluator).getSelectedPlatform();
479         if (platform == null) {
480             // Nope; so look for some platform of reasonable source level instead.
481
JavaPlatformManager jpm = JavaPlatformManager.getDefault();
482             platform = jpm.getDefaultPlatform(); // fallback
483
for (Element JavaDoc e : Util.findSubElements(compilationUnitEl)) {
484                 if (e.getLocalName().equals("source-level")) { // NOI18N
485
String JavaDoc level = Util.findText(e);
486                     Specification spec = new Specification("j2se", new SpecificationVersion(level)); // NOI18N
487
JavaPlatform[] matchingPlatforms = jpm.getPlatforms(null, spec);
488                     if (matchingPlatforms.length > 0) {
489                         // Pick one. Prefer one with sources if there is a choice, else with Javadoc.
490
platform = matchingPlatforms[0];
491                         for (JavaPlatform matchingPlatform : matchingPlatforms) {
492                             if (!matchingPlatform.getJavadocFolders().isEmpty()) {
493                                 platform = matchingPlatform;
494                                 break;
495                             }
496                         }
497                         for (JavaPlatform matchingPlatform : matchingPlatforms) {
498                             if (matchingPlatform.getSourceFolders().getRoots().length > 0) {
499                                 platform = matchingPlatform;
500                                 break;
501                             }
502                         }
503                     }
504                     break;
505                 }
506             }
507         }
508         if (platform != null) {
509             // XXX this is not ideal; should try to reuse the ClassPath as is?
510
// The current impl will not listen to changes in the platform classpath correctly.
511
List JavaDoc<ClassPath.Entry> entries = platform.getBootstrapLibraries().entries();
512             List JavaDoc<URL JavaDoc> urls = new ArrayList JavaDoc<URL JavaDoc>(entries.size());
513             for (ClassPath.Entry entry : entries) {
514                 urls.add(entry.getURL());
515             }
516             return urls;
517         } else {
518             assert false : "JavaPlatformManager has no default platform";
519             return Collections.emptyList();
520         }
521     }
522
523     public void configurationXmlChanged(AntProjectEvent ev) {
524         pathsChanged();
525     }
526
527     public void propertiesChanged(AntProjectEvent ev) {
528         pathsChanged(); // in case it is nbjdk.properties
529
}
530
531     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
532         pathsChanged();
533     }
534     
535     private void pathsChanged() {
536         synchronized (this) {
537             classpaths.clear();
538         }
539         //System.err.println("pathsChanged: " + mutablePathImpls);
540
for (Map JavaDoc<List JavaDoc<String JavaDoc>,MutableClassPathImplementation> m : mutablePathImpls.values()) {
541             for (MutableClassPathImplementation impl : m.values()) {
542                 impl.change();
543             }
544         }
545     }
546
547     /**
548      * Representation of one path.
549      * Listens to changes in project.xml and/or evaluator and responds.
550      */

551     private final class MutableClassPathImplementation implements ClassPathImplementation {
552         
553         private final List JavaDoc<String JavaDoc> packageRootNames;
554         private final String JavaDoc type;
555         private final PropertyChangeSupport JavaDoc pcs = new PropertyChangeSupport JavaDoc(this);
556         private List JavaDoc<URL JavaDoc> roots; // should always be non-null
557
private List JavaDoc<PathResourceImplementation> resources;
558         
559         public MutableClassPathImplementation(List JavaDoc<String JavaDoc> packageRootNames, String JavaDoc type, Element JavaDoc initialCompilationUnit) {
560             this.packageRootNames = packageRootNames;
561             this.type = type;
562             initRoots(initialCompilationUnit);
563         }
564         
565         private Element JavaDoc findCompilationUnit() {
566             Element JavaDoc java = aux.getConfigurationFragment(JavaProjectNature.EL_JAVA, JavaProjectNature.NS_JAVA_2, true);
567             if (java == null) {
568                 return null;
569             }
570             List JavaDoc<Element JavaDoc> compilationUnits = Util.findSubElements(java);
571             Iterator JavaDoc it = compilationUnits.iterator();
572             while (it.hasNext()) {
573                 Element JavaDoc compilationUnitEl = (Element JavaDoc)it.next();
574                 assert compilationUnitEl.getLocalName().equals("compilation-unit") : compilationUnitEl;
575                 if (packageRootNames.equals(findPackageRootNames(compilationUnitEl))) {
576                     // Found a matching compilation unit.
577
return compilationUnitEl;
578                 }
579             }
580             // Did not find it.
581
return null;
582         }
583         
584         /**
585          * Initialize list of URL roots.
586          */

587         private boolean initRoots(Element JavaDoc compilationUnitEl) {
588             List JavaDoc<URL JavaDoc> oldRoots = roots;
589             if (compilationUnitEl != null) {
590                 if (type.equals(ClassPath.SOURCE)) {
591                     roots = createSourcePath(packageRootNames);
592                 } else if (type.equals(ClassPath.COMPILE)) {
593                     roots = createCompileClasspath(compilationUnitEl);
594                 } else if (type.equals(ClassPath.EXECUTE)) {
595                     roots = createExecuteClasspath(packageRootNames, compilationUnitEl);
596                 } else {
597                     assert type.equals(ClassPath.BOOT) : type;
598                     roots = createBootClasspath(compilationUnitEl);
599                 }
600             } else {
601                 // Dead.
602
roots = Collections.emptyList();
603             }
604             assert roots != null;
605             if (!roots.equals(oldRoots)) {
606                 resources = new ArrayList JavaDoc<PathResourceImplementation>(roots.size());
607                 for (URL JavaDoc root : roots) {
608                     assert root.toExternalForm().endsWith("/") : "Had bogus roots " + roots + " for type " + type + " in " + helper.getProjectDirectory();
609                     PathResourceImplementation pri;
610                     if (type.equals(ClassPath.SOURCE)) {
611                         pri = new SourcePRI(root);
612                     } else {
613                         pri = ClassPathSupport.createResource(root);
614                     }
615                     resources.add(pri);
616                 }
617                 return true;
618             } else {
619                 return false;
620             }
621         }
622
623         public List JavaDoc<PathResourceImplementation> getResources() {
624             return resources;
625         }
626
627         /**
628          * Notify impl of a possible change in data.
629          */

630         public void change() {
631             if (initRoots(findCompilationUnit())) {
632                 if (err.isLoggable(ErrorManager.INFORMATIONAL)) {
633                     err.log("MutableClassPathImplementation.change: packageRootNames=" + packageRootNames + " type=" + type + " roots=" + roots);
634                 }
635                 pcs.firePropertyChange(ClassPathImplementation.PROP_RESOURCES, null, null);
636             }
637         }
638
639         public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
640             pcs.addPropertyChangeListener(listener);
641         }
642
643         public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
644             pcs.removePropertyChangeListener(listener);
645         }
646         
647     }
648     
649     private final class SourcePRI implements FilteringPathResourceImplementation, PropertyChangeListener JavaDoc, AntProjectListener {
650         private final URL JavaDoc root;
651         private final PropertyChangeSupport JavaDoc pcs = new PropertyChangeSupport JavaDoc(this);
652         private PathMatcher matcher;
653         private String JavaDoc includes, excludes;
654         public SourcePRI(URL JavaDoc root) {
655             this.root = root;
656             helper.addAntProjectListener(WeakListeners.create(AntProjectListener.class, this, helper));
657             evaluator.addPropertyChangeListener(WeakListeners.propertyChange(this, evaluator));
658             computeMatcher();
659         }
660         private boolean computeMatcher() {
661             String JavaDoc includes = null;
662             String JavaDoc excludes = null;
663             // Annoying to duplicate logic from FreeformSources.
664
// But using SourceGroup.contains is not an option since that requires FileObject creation.
665
File JavaDoc rootFolder = new File JavaDoc(URI.create(root.toExternalForm()));
666             Element JavaDoc genldata = Util.getPrimaryConfigurationData(helper);
667             Element JavaDoc foldersE = Util.findElement(genldata, "folders", Util.NAMESPACE); // NOI18N
668
if (foldersE != null) {
669                 for (Element JavaDoc folderE : Util.findSubElements(foldersE)) {
670                     if (folderE.getLocalName().equals("source-folder")) {
671                         Element JavaDoc typeE = Util.findElement(folderE, "type", Util.NAMESPACE); // NOI18N
672
if (typeE != null) {
673                             String JavaDoc type = Util.findText(typeE);
674                             if (type.equals(JavaProjectConstants.SOURCES_TYPE_JAVA)) {
675                                 Element JavaDoc locationE = Util.findElement(folderE, "location", Util.NAMESPACE); // NOI18N
676
String JavaDoc location = evaluator.evaluate(Util.findText(locationE));
677                                 if (location != null && helper.resolveFile(location).equals(rootFolder)) {
678                                     Element JavaDoc includesE = Util.findElement(folderE, "includes", Util.NAMESPACE); // NOI18N
679
if (includesE != null) {
680                                         includes = evaluator.evaluate(Util.findText(includesE));
681                                         if (includes != null && includes.matches("\\$\\{[^}]+\\}")) { // NOI18N
682
// Clearly intended to mean "include everything".
683
includes = null;
684                                         }
685                                     }
686                                     Element JavaDoc excludesE = Util.findElement(folderE, "excludes", Util.NAMESPACE); // NOI18N
687
if (excludesE != null) {
688                                         excludes = evaluator.evaluate(Util.findText(excludesE));
689                                     }
690                                 }
691                             }
692                         }
693                     }
694                 }
695             }
696             if (!Utilities.compareObjects(includes, this.includes) || !Utilities.compareObjects(excludes, this.excludes)) {
697                 this.includes = includes;
698                 this.excludes = excludes;
699                 matcher = new PathMatcher(includes, excludes, rootFolder);
700                 return true;
701             } else {
702                 if (matcher == null) {
703                     matcher = new PathMatcher(includes, excludes, rootFolder);
704                 }
705                 return false;
706             }
707         }
708         public URL JavaDoc[] getRoots() {
709             return new URL JavaDoc[] {root};
710         }
711         public boolean includes(URL JavaDoc root, String JavaDoc resource) {
712             return matcher.matches(resource, true);
713         }
714         public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
715             pcs.addPropertyChangeListener(listener);
716         }
717         public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
718             pcs.removePropertyChangeListener(listener);
719         }
720         public ClassPathImplementation getContent() {
721             return null;
722         }
723         public void propertyChange(PropertyChangeEvent JavaDoc ev) {
724             change(ev);
725         }
726         public void configurationXmlChanged(AntProjectEvent ev) {
727             change(ev);
728         }
729         public void propertiesChanged(AntProjectEvent ev) {}
730         private void change(Object JavaDoc propid) {
731             if (computeMatcher()) {
732                 PropertyChangeEvent JavaDoc ev = new PropertyChangeEvent JavaDoc(this, FilteringPathResourceImplementation.PROP_INCLUDES, null, null);
733                 ev.setPropagationId(propid);
734                 pcs.firePropertyChange(ev);
735             }
736         }
737     }
738     
739 }
740
Popular Tags