001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.java.source.classpath;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.lang.ref.WeakReference;
047: import java.net.URISyntaxException;
048: import java.net.URL;
049: import java.util.ArrayList;
050: import java.util.Arrays;
051: import java.util.Collection;
052: import java.util.Collections;
053: import java.util.HashMap;
054: import java.util.HashSet;
055: import java.util.Iterator;
056: import java.util.List;
057: import java.util.Map;
058: import java.util.Set;
059: import java.util.TooManyListenersException;
060: import java.util.concurrent.CopyOnWriteArrayList;
061: import java.util.logging.Logger;
062: import javax.swing.event.ChangeEvent;
063: import javax.swing.event.ChangeListener;
064: import org.netbeans.api.java.classpath.ClassPath;
065: import org.netbeans.api.java.classpath.GlobalPathRegistry;
066: import org.netbeans.api.java.classpath.GlobalPathRegistryEvent;
067: import org.netbeans.api.java.classpath.GlobalPathRegistryListener;
068: import org.netbeans.api.java.platform.JavaPlatform;
069: import org.netbeans.api.java.platform.JavaPlatformManager;
070: import org.netbeans.api.java.queries.SourceForBinaryQuery;
071: import org.netbeans.api.project.FileOwnerQuery;
072: import org.netbeans.api.project.Project;
073: import org.netbeans.api.project.ProjectManager;
074: import org.netbeans.api.project.SourceGroup;
075: import org.netbeans.api.project.Sources;
076: import org.netbeans.api.project.libraries.Library;
077: import org.netbeans.api.project.libraries.LibraryManager;
078: import org.netbeans.spi.java.classpath.ClassPathImplementation;
079: import org.netbeans.spi.java.classpath.PathResourceImplementation;
080: import org.netbeans.spi.java.classpath.support.ClassPathSupport;
081: import org.netbeans.spi.project.libraries.support.LibrariesSupport;
082: import org.openide.ErrorManager;
083: import org.openide.filesystems.FileObject;
084: import org.openide.filesystems.FileStateInvalidException;
085: import org.openide.util.Exceptions;
086: import org.openide.util.Mutex;
087: import org.openide.util.RequestProcessor;
088: import org.openide.util.Utilities;
089: import org.openide.util.WeakListeners;
090:
091: /**
092: *
093: * @author Tomas Zezula
094: */
095: public class GlobalSourcePath {
096:
097: public static final String PROP_INCLUDES = ClassPath.PROP_INCLUDES;
098:
099: private static final RequestProcessor firer = new RequestProcessor();
100:
101: private static GlobalSourcePath instance;
102:
103: private final GlobalPathRegistry gpr;
104: private List<? extends PathResourceImplementation> resources;
105: private List<? extends PathResourceImplementation> unknownResources;
106: private List<? extends PathResourceImplementation> binaryResources;
107: private Set<ClassPath> activeCps;
108: private Map<URL, SourceForBinaryQuery.Result> sourceResults;
109: private Map<URL, URL[]> translatedRoots;
110: private Map<URL, WeakValue> unknownRoots;
111: private long timeStamp; //Lamport event ordering
112: private Runnable debugCallBack;
113: private volatile boolean useLibraries = true;
114:
115: private final SourcePathImplementation sourcePath;
116: private final BinaryPathImplementation binaryPath;
117: private final UnknownSourcePathImplementation unknownSourcePath;
118:
119: private final JavaPlatformManager pm;
120: private Set<JavaPlatform> seenPlatforms;
121: private Set<Library> seenLibs;
122: private Collection<LibraryManager> seenLibManagers;
123:
124: private Set<URL> libsSrcs;
125:
126: private final Listener listener;
127: private final LibsListener libsListener;
128:
129: private volatile PropertyChangeListener excludesListener;
130:
131: /** Creates a new instance of GlobalSourcePath */
132: private GlobalSourcePath() {
133: this .listener = new Listener();
134: this .sourcePath = new SourcePathImplementation();
135: this .binaryPath = new BinaryPathImplementation();
136: this .unknownSourcePath = new UnknownSourcePathImplementation();
137: this .timeStamp = -1;
138: this .gpr = GlobalPathRegistry.getDefault();
139: this .activeCps = Collections.emptySet();
140: this .sourceResults = Collections.emptyMap();
141: this .unknownRoots = new HashMap<URL, WeakValue>();
142: this .translatedRoots = new HashMap<URL, URL[]>();
143: this .gpr
144: .addGlobalPathRegistryListener((GlobalPathRegistryListener) WeakListeners
145: .create(GlobalPathRegistryListener.class,
146: this .listener, this .gpr));
147: this .seenPlatforms = new HashSet<JavaPlatform>();
148: this .seenLibs = new HashSet<Library>();
149: this .seenLibManagers = new HashSet<LibraryManager>();
150: this .libsListener = new LibsListener();
151: LibraryManager
152: .addOpenManagersPropertyChangeListener(WeakListeners
153: .propertyChange(libsListener, null));
154: this .pm = JavaPlatformManager.getDefault();
155: this .pm.addPropertyChangeListener(WeakListeners.propertyChange(
156: libsListener, this .pm));
157: }
158:
159: public synchronized void setExcludesListener(
160: final PropertyChangeListener listener)
161: throws TooManyListenersException {
162: if (listener != null && this .excludesListener != null) {
163: throw new TooManyListenersException();
164: }
165: this .excludesListener = listener;
166: }
167:
168: public URL[] getSourceRootForBinaryRoot(final URL binaryRoot,
169: final ClassPath definingClassPath, final boolean fire) {
170: URL[] result = this .translatedRoots.get(binaryRoot);
171: if (result != null) {
172: if (result.length > 0) {
173: return result;
174: } else {
175: return null;
176: }
177: } else {
178: List<URL> cacheRoots = new ArrayList<URL>();
179: Collection<? extends PathResourceImplementation> unknownRes = getSources(
180: SourceForBinaryQuery.findSourceRoots(binaryRoot)
181: .getRoots(), cacheRoots, null);
182: if (unknownRes.isEmpty()) {
183: return null;
184: } else {
185: result = new URL[cacheRoots.size()];
186: synchronized (this ) {
187: Iterator<URL> it = cacheRoots.iterator();
188: for (int i = 0; it.hasNext(); i++) {
189: result[i] = it.next();
190: unknownRoots.put(result[i], new WeakValue(
191: definingClassPath, result[i]));
192: }
193: }
194: if (fire) {
195: this .resetCacheAndFire();
196: }
197: return result;
198: }
199: }
200: }
201:
202: public boolean isLibrary(final ClassPath cp) {
203: assert cp != null;
204: Set<URL> libs = getLibsSources();
205: for (ClassPath.Entry entry : cp.entries()) {
206: if (libs.contains(entry.getURL())) {
207: return true;
208: }
209: }
210: return false;
211: }
212:
213: public boolean isLibrary(final URL root) {
214: assert root != null;
215: Set<URL> libs = getLibsSources();
216: return libs.contains(root);
217: }
218:
219: public ClassPathImplementation getSourcePath() {
220: return this .sourcePath;
221: }
222:
223: public ClassPathImplementation getUnknownSourcePath() {
224: return this .unknownSourcePath;
225: }
226:
227: public ClassPathImplementation getBinaryPath() {
228: return this .binaryPath;
229: }
230:
231: private void resetCacheAndFire() {
232: synchronized (this ) {
233: this .resources = null;
234: this .binaryResources = null;
235: this .unknownResources = null;
236: this .timeStamp++;
237: }
238:
239: firer.post(new Runnable() {
240: public void run() {
241: sourcePath.firePropertyChange();
242: binaryPath.firePropertyChange();
243: unknownSourcePath.firePropertyChange();
244: }
245: });
246: }
247:
248: private Result createResources(final Request r) {
249: assert r != null;
250: Set<PathResourceImplementation> result = new HashSet<PathResourceImplementation>();
251: Set<PathResourceImplementation> unknownResult = new HashSet<PathResourceImplementation>();
252: Set<PathResourceImplementation> binaryResult = new HashSet<PathResourceImplementation>();
253: final Map<URL, URL[]> translatedRoots = new HashMap<URL, URL[]>();
254: Set<ClassPath> newCps = new HashSet<ClassPath>();
255: for (ClassPath cp : r.sourceCps) {
256: boolean isNew = !r.oldCps.remove(cp);
257: for (ClassPath.Entry entry : cp.entries()) {
258: result.add(ClassPathSupport.createResource(entry
259: .getURL()));
260: }
261: boolean notContained = newCps.add(cp);
262: if (isNew && notContained) {
263: cp.addPropertyChangeListener(r.propertyListener);
264: }
265: }
266: Map<URL, SourceForBinaryQuery.Result> newSR = new HashMap<URL, SourceForBinaryQuery.Result>();
267: for (ClassPath cp : r.bootCps) {
268: boolean isNew = !r.oldCps.remove(cp);
269: for (ClassPath.Entry entry : cp.entries()) {
270: URL url = entry.getURL();
271: if (!translatedRoots.containsKey(url)) {
272: SourceForBinaryQuery.Result sr = r.oldSR
273: .remove(url);
274: boolean isNewSR;
275: if (sr == null) {
276: sr = SourceForBinaryQuery.findSourceRoots(url);
277: isNewSR = true;
278: } else {
279: isNewSR = false;
280: }
281: assert !newSR.containsKey(url);
282: newSR.put(url, sr);
283: List<URL> cacheURLs = new ArrayList<URL>();
284: Collection<? extends PathResourceImplementation> srcRoots = getSources(
285: sr.getRoots(), cacheURLs, r.unknownRoots);
286: if (srcRoots.isEmpty()) {
287: binaryResult.add(ClassPathSupport
288: .createResource(url));
289: } else {
290: result.addAll(srcRoots);
291: }
292: translatedRoots.put(url, cacheURLs
293: .toArray(new URL[cacheURLs.size()]));
294: if (isNewSR) {
295: sr.addChangeListener(r.changeListener);
296: }
297: }
298: }
299: boolean notContained = newCps.add(cp);
300: if (isNew && notContained) {
301: cp.addPropertyChangeListener(r.propertyListener);
302: }
303: }
304:
305: for (ClassPath cp : r.compileCps) {
306: boolean isNew = !r.oldCps.remove(cp);
307: for (ClassPath.Entry entry : cp.entries()) {
308: URL url = entry.getURL();
309: if (!translatedRoots.containsKey(url)) {
310: SourceForBinaryQuery.Result sr = r.oldSR
311: .remove(url);
312: boolean isNewSR;
313: if (sr == null) {
314: sr = SourceForBinaryQuery.findSourceRoots(url);
315: isNewSR = true;
316: } else {
317: isNewSR = false;
318: }
319: assert !newSR.containsKey(url);
320: newSR.put(url, sr);
321: List<URL> cacheURLs = new ArrayList<URL>();
322: Collection<? extends PathResourceImplementation> srcRoots = getSources(
323: sr.getRoots(), cacheURLs, r.unknownRoots);
324: if (srcRoots.isEmpty()) {
325: binaryResult.add(ClassPathSupport
326: .createResource(url));
327: } else {
328: result.addAll(srcRoots);
329: }
330: translatedRoots.put(url, cacheURLs
331: .toArray(new URL[cacheURLs.size()]));
332: if (isNewSR) {
333: sr.addChangeListener(r.changeListener);
334: }
335: }
336: }
337: boolean notContained = newCps.add(cp);
338: if (isNew && notContained) {
339: cp.addPropertyChangeListener(r.propertyListener);
340: }
341: }
342:
343: for (ClassPath cp : r.oldCps) {
344: cp.removePropertyChangeListener(r.propertyListener);
345: }
346:
347: for (Map.Entry<URL, SourceForBinaryQuery.Result> entry : r.oldSR
348: .entrySet()) {
349: entry.getValue().removeChangeListener(r.changeListener);
350: }
351: for (URL unknownRoot : r.unknownRoots.keySet()) {
352: unknownResult.add(ClassPathSupport
353: .createResource(unknownRoot));
354: }
355: return new Result(
356: r.timeStamp,
357: new ArrayList<PathResourceImplementation>(result),
358: new ArrayList(binaryResult),
359: new ArrayList<PathResourceImplementation>(unknownResult),
360: newCps, newSR, translatedRoots, r.unknownRoots);
361: }
362:
363: /**
364: * Unit test method, used to set a callback which is called
365: * form getResources to emulate a race condition.
366: * The access to debugCallBack is not synchronized, it should be
367: * set before test and unset after test.
368: * @param callBack to be called
369: *
370: **/
371: void setDebugCallBack(final Runnable callBack) {
372: this .debugCallBack = callBack;
373: }
374:
375: /**
376: * Unit test method, used to set disable use of {@link Library}
377: * int the test in which the libraries SPI is not initialized.
378: * @param use if false libraries will not be used
379: *
380: **/
381: void setUseLibraries(final boolean use) {
382: StackTraceElement[] st = Thread.currentThread().getStackTrace();
383: final String myClass = this .getClass().getName();
384: final String expectedCallerClass = this .getClass().getPackage()
385: .getName()
386: + ".GlobalSourcePathTestUtil"; //NOI18N
387: for (int i = 0; i < st.length; i++) {
388: if (myClass.equals(st[i].getClassName())) {
389: if (expectedCallerClass
390: .equals(st[i + 1].getClassName())) {
391: this .useLibraries = use;
392: return;
393: } else {
394: throw new AssertionError(
395: "Can be called only by GlobalSourcePathTestUtil"); //NOI18N
396: }
397: }
398: }
399: }
400:
401: private Collection<? extends PathResourceImplementation> getSources(
402: final FileObject[] roots, final List<URL> cacheDirs,
403: final Map<URL, WeakValue> unknownRoots) {
404: assert roots != null;
405: URL[] urls = new URL[roots.length];
406: boolean add = true;
407: Set<URL> libs = getLibsSources();
408: for (int i = 0; i < roots.length; i++) {
409: try {
410: URL url = roots[i].getURL();
411: if (!"file".equals(url.getProtocol())) { //NOI18N
412: add = false;
413: break;
414: }
415: if (libs.contains(url)) {
416: add = false;
417: break;
418: }
419: urls[i] = url;
420: } catch (FileStateInvalidException e) {
421: ErrorManager.getDefault().notify(e);
422: }
423: }
424: if (add) {
425: List<PathResourceImplementation> result = new ArrayList<PathResourceImplementation>(
426: roots.length);
427: for (int i = 0; i < urls.length; i++) {
428: if (cacheDirs != null) {
429: cacheDirs.add(urls[i]);
430: }
431: if (unknownRoots != null) {
432: unknownRoots.remove(urls[i]);
433: }
434: result.add(ClassPathSupport.createResource(urls[i]));
435: }
436: return result;
437: }
438: return Collections.<PathResourceImplementation> emptySet();
439: }
440:
441: private Set<URL> getLibsSources() {
442: if (!useLibraries) {
443: //Running in the test where libraries modules SPI is not initialized
444: return Collections.<URL> emptySet();
445: }
446: // retrieve list outside of java mutex:
447: final Collection<LibraryManager> libraryManagers = LibraryManager
448: .getOpenManagers();
449: final Mutex.Action<Set<URL>> libsTask = new Mutex.Action<Set<URL>>() {
450: public Set<URL> run() {
451: synchronized (GlobalSourcePath.this ) {
452: if (GlobalSourcePath.this .libsSrcs == null) {
453: final Set<URL> _libSrcs = new HashSet<URL>();
454: Set<JavaPlatform> platforms = new HashSet<JavaPlatform>(
455: Arrays.asList(pm
456: .getInstalledPlatforms()));
457: Set<JavaPlatform> oldPlatforms = new HashSet<JavaPlatform>(
458: GlobalSourcePath.this .seenPlatforms);
459: OUTER: for (JavaPlatform platform : platforms) {
460: if (!oldPlatforms.remove(platform)) {
461: platform
462: .addPropertyChangeListener(GlobalSourcePath.this .libsListener);
463: }
464: ClassPath cp = platform.getSourceFolders();
465: assert cp != null : platform.getClass();
466: for (ClassPath.Entry e : cp.entries()) {
467: URL url = e.getURL();
468: try {
469: Project p = FileOwnerQuery
470: .getOwner(url.toURI());
471: if (p != null) {
472: Sources src = p.getLookup()
473: .lookup(Sources.class);
474: if (src != null) {
475: for (SourceGroup group : src
476: .getSourceGroups("java")) { //NOI18N
477: if (url
478: .equals(group
479: .getRootFolder()
480: .getURL())) {
481: continue OUTER;
482: }
483: }
484: }
485: }
486: } catch (URISyntaxException ex) {
487: Exceptions.printStackTrace(ex);
488: } catch (FileStateInvalidException ex) {
489: Exceptions.printStackTrace(ex);
490: }
491: _libSrcs.add(url);
492: }
493: }
494: for (JavaPlatform platform : oldPlatforms) {
495: platform
496: .removePropertyChangeListener(GlobalSourcePath.this .libsListener);
497: }
498: GlobalSourcePath.this .seenPlatforms = platforms;
499:
500: for (LibraryManager lm : GlobalSourcePath.this .seenLibManagers) {
501: lm
502: .removePropertyChangeListener(libsListener);
503: }
504: Set<Library> oldLibs = new HashSet<Library>(
505: GlobalSourcePath.this .seenLibs);
506: Set<Library> newLibs = new HashSet<Library>();
507: for (LibraryManager lm : libraryManagers) {
508: lm.addPropertyChangeListener(libsListener);
509:
510: Set<Library> libs = new HashSet<Library>(
511: Arrays.asList(lm.getLibraries()));
512: OUTER: for (Library lib : libs) {
513: newLibs.add(lib);
514: if (!oldLibs.remove(lib)) {
515: lib
516: .addPropertyChangeListener(GlobalSourcePath.this .libsListener);
517: }
518: if (lib.getContent("classpath") != null) { //NOI18N
519: List<URL> libSrc = lib
520: .getContent("src"); //NOI18N
521: for (URL url : libSrc) {
522: try {
523: url = LibrariesSupport
524: .resolveLibraryEntryURL(
525: lm
526: .getLocation(),
527: url);
528: Project p = FileOwnerQuery
529: .getOwner(url
530: .toURI());
531: if (p != null) {
532: Sources src = p
533: .getLookup()
534: .lookup(
535: Sources.class);
536: if (src != null) {
537: for (SourceGroup group : src
538: .getSourceGroups("java")) { //NOI18N
539: if (url
540: .equals(group
541: .getRootFolder()
542: .getURL())) {
543: continue OUTER;
544: }
545: }
546: }
547: }
548: } catch (URISyntaxException ex) {
549: Exceptions
550: .printStackTrace(ex);
551: } catch (FileStateInvalidException ex) {
552: Exceptions
553: .printStackTrace(ex);
554: }
555: _libSrcs.add(url);
556: }
557:
558: }
559: }
560: }
561: for (Library lib : oldLibs) {
562: lib
563: .removePropertyChangeListener(GlobalSourcePath.this .libsListener);
564: }
565: GlobalSourcePath.this .seenLibManagers = libraryManagers;
566: GlobalSourcePath.this .seenLibs = newLibs;
567: GlobalSourcePath.this .libsSrcs = _libSrcs;
568: }
569: return GlobalSourcePath.this .libsSrcs;
570: }
571: }
572: };
573: return ProjectManager.mutex().readAccess(libsTask);
574: }
575:
576: private class WeakValue extends WeakReference<ClassPath> implements
577: Runnable {
578:
579: private URL key;
580:
581: public WeakValue(ClassPath ref, URL key) {
582: super (ref, Utilities.activeReferenceQueue());
583: assert key != null;
584: this .key = key;
585: }
586:
587: public void run() {
588: boolean fire = false;
589: synchronized (GlobalSourcePath.this ) {
590: fire = (GlobalSourcePath.this .unknownRoots.remove(key) != null);
591: }
592: if (fire) {
593: GlobalSourcePath.this .resetCacheAndFire();
594: }
595: }
596: }
597:
598: private long getTimeStamp() {
599: return this .timeStamp;
600: }
601:
602: private static class Request {
603:
604: final long timeStamp;
605: final Set<ClassPath> sourceCps;
606: final Set<ClassPath> bootCps;
607: final Set<ClassPath> compileCps;
608: final Set<ClassPath> oldCps;
609: final Map<URL, SourceForBinaryQuery.Result> oldSR;
610: final Map<URL, WeakValue> unknownRoots;
611: final PropertyChangeListener propertyListener;
612: final ChangeListener changeListener;
613:
614: public Request(final long timeStamp,
615: final Set<ClassPath> sourceCps,
616: final Set<ClassPath> bootCps,
617: final Set<ClassPath> compileCps,
618: final Set<ClassPath> oldCps,
619: final Map<URL, SourceForBinaryQuery.Result> oldSR,
620: final Map<URL, WeakValue> unknownRoots,
621: final PropertyChangeListener propertyListener,
622: final ChangeListener changeListener) {
623: assert sourceCps != null;
624: assert bootCps != null;
625: assert compileCps != null;
626: assert oldCps != null;
627: assert oldSR != null;
628: assert unknownRoots != null;
629: assert propertyListener != null;
630: assert changeListener != null;
631:
632: this .timeStamp = timeStamp;
633: this .sourceCps = sourceCps;
634: this .bootCps = bootCps;
635: this .compileCps = compileCps;
636: this .oldCps = oldCps;
637: this .oldSR = oldSR;
638: this .unknownRoots = unknownRoots;
639: this .propertyListener = propertyListener;
640: this .changeListener = changeListener;
641: }
642: }
643:
644: private static class Result {
645:
646: final long timeStamp;
647: final List<? extends PathResourceImplementation> resources;
648: final List<? extends PathResourceImplementation> binaryResources;
649: final List<? extends PathResourceImplementation> unknownResources;
650: final Set<ClassPath> newCps;
651: final Map<URL, SourceForBinaryQuery.Result> newSR;
652: final Map<URL, URL[]> translatedRoots;
653: final Map<URL, WeakValue> unknownRoots;
654:
655: public Result(
656: final long timeStamp,
657: final List<? extends PathResourceImplementation> resources,
658: final List<? extends PathResourceImplementation> binaryResources,
659: final List<? extends PathResourceImplementation> unknownResources,
660: final Set<ClassPath> newCps,
661: final Map<URL, SourceForBinaryQuery.Result> newSR,
662: final Map<URL, URL[]> translatedRoots,
663: final Map<URL, WeakValue> unknownRoots) {
664: assert resources != null;
665: assert binaryResources != null;
666: assert unknownResources != null;
667: assert newCps != null;
668: assert newSR != null;
669: assert translatedRoots != null;
670: this .timeStamp = timeStamp;
671: this .resources = resources;
672: this .binaryResources = binaryResources;
673: this .unknownResources = unknownResources;
674: this .newCps = newCps;
675: this .newSR = newSR;
676: this .translatedRoots = translatedRoots;
677: this .unknownRoots = unknownRoots;
678: }
679: }
680:
681: private class SourcePathImplementation implements
682: ClassPathImplementation {
683:
684: private List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
685:
686: public List<? extends PathResourceImplementation> getResources() {
687: Request request;
688: synchronized (GlobalSourcePath.this ) {
689: if (GlobalSourcePath.this .resources != null) {
690: return GlobalSourcePath.this .resources;
691: }
692: request = new Request(
693: GlobalSourcePath.this .getTimeStamp(),
694: GlobalSourcePath.this .gpr
695: .getPaths(ClassPath.SOURCE),
696: GlobalSourcePath.this .gpr
697: .getPaths(ClassPath.BOOT),
698: GlobalSourcePath.this .gpr
699: .getPaths(ClassPath.COMPILE),
700: new HashSet(GlobalSourcePath.this .activeCps),
701: new HashMap(GlobalSourcePath.this .sourceResults),
702: new HashMap<URL, WeakValue>(
703: GlobalSourcePath.this .unknownRoots),
704: GlobalSourcePath.this .listener,
705: GlobalSourcePath.this .listener);
706: }
707: Result res = createResources(request);
708: if (GlobalSourcePath.this .debugCallBack != null) {
709: GlobalSourcePath.this .debugCallBack.run();
710: }
711: synchronized (this ) {
712: if (GlobalSourcePath.this .getTimeStamp() == res.timeStamp) {
713: if (GlobalSourcePath.this .resources == null) {
714: GlobalSourcePath.this .resources = res.resources;
715: GlobalSourcePath.this .binaryResources = res.binaryResources;
716: GlobalSourcePath.this .unknownResources = res.unknownResources;
717: GlobalSourcePath.this .activeCps = res.newCps;
718: GlobalSourcePath.this .sourceResults = res.newSR;
719: GlobalSourcePath.this .translatedRoots = res.translatedRoots;
720: GlobalSourcePath.this .unknownRoots = res.unknownRoots;
721: }
722: return GlobalSourcePath.this .resources;
723: } else {
724: return res.resources;
725: }
726: }
727: }
728:
729: public synchronized void addPropertyChangeListener(
730: PropertyChangeListener listener) {
731: assert listener != null;
732: if (this .listeners == null) {
733: this .listeners = new ArrayList<PropertyChangeListener>();
734: }
735: this .listeners.add(listener);
736: }
737:
738: public synchronized void removePropertyChangeListener(
739: PropertyChangeListener listener) {
740: assert listener != null;
741: if (this .listeners == null) {
742: return;
743: }
744: this .listeners.remove(listener);
745: }
746:
747: void firePropertyChange() {
748: PropertyChangeListener[] _listeners;
749: synchronized (this ) {
750: if (this .listeners == null) {
751: return;
752: }
753: _listeners = this .listeners
754: .toArray(new PropertyChangeListener[this .listeners
755: .size()]);
756: }
757: PropertyChangeEvent event = new PropertyChangeEvent(this ,
758: PROP_RESOURCES, null, null);
759: for (PropertyChangeListener l : _listeners) {
760: l.propertyChange(event);
761: }
762: }
763: }
764:
765: private class UnknownSourcePathImplementation implements
766: ClassPathImplementation {
767:
768: private List<PropertyChangeListener> listeners = new CopyOnWriteArrayList<PropertyChangeListener>();
769:
770: public List<? extends PathResourceImplementation> getResources() {
771: Request request;
772: synchronized (GlobalSourcePath.this ) {
773: if (GlobalSourcePath.this .unknownResources != null) {
774: return GlobalSourcePath.this .unknownResources;
775: }
776: request = new Request(
777: GlobalSourcePath.this .getTimeStamp(),
778: GlobalSourcePath.this .gpr
779: .getPaths(ClassPath.SOURCE),
780: GlobalSourcePath.this .gpr
781: .getPaths(ClassPath.BOOT),
782: GlobalSourcePath.this .gpr
783: .getPaths(ClassPath.COMPILE),
784: new HashSet(GlobalSourcePath.this .activeCps),
785: new HashMap(GlobalSourcePath.this .sourceResults),
786: new HashMap<URL, WeakValue>(
787: GlobalSourcePath.this .unknownRoots),
788: GlobalSourcePath.this .listener,
789: GlobalSourcePath.this .listener);
790: }
791: Result res = createResources(request);
792: if (GlobalSourcePath.this .debugCallBack != null) {
793: GlobalSourcePath.this .debugCallBack.run();
794: }
795: synchronized (this ) {
796: if (GlobalSourcePath.this .getTimeStamp() == res.timeStamp) {
797: if (GlobalSourcePath.this .binaryResources == null) {
798: GlobalSourcePath.this .resources = res.resources;
799: GlobalSourcePath.this .binaryResources = res.binaryResources;
800: GlobalSourcePath.this .unknownResources = res.unknownResources;
801: GlobalSourcePath.this .activeCps = res.newCps;
802: GlobalSourcePath.this .sourceResults = res.newSR;
803: GlobalSourcePath.this .translatedRoots = res.translatedRoots;
804: GlobalSourcePath.this .unknownRoots = res.unknownRoots;
805: }
806: return GlobalSourcePath.this .unknownResources;
807: } else {
808: return res.unknownResources;
809: }
810: }
811: }
812:
813: public void addPropertyChangeListener(
814: PropertyChangeListener listener) {
815: assert listener != null;
816: this .listeners.add(listener);
817: }
818:
819: public void removePropertyChangeListener(
820: PropertyChangeListener listener) {
821: assert listener != null;
822: this .listeners.remove(listener);
823: }
824:
825: void firePropertyChange() {
826: PropertyChangeEvent event = new PropertyChangeEvent(this ,
827: PROP_RESOURCES, null, null);
828: for (PropertyChangeListener l : this .listeners) {
829: l.propertyChange(event);
830: }
831: }
832: }
833:
834: private class BinaryPathImplementation implements
835: ClassPathImplementation {
836: private List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
837:
838: public List<? extends PathResourceImplementation> getResources() {
839: Request request;
840: synchronized (GlobalSourcePath.this ) {
841: if (GlobalSourcePath.this .binaryResources != null) {
842: return GlobalSourcePath.this .binaryResources;
843: }
844: request = new Request(
845: GlobalSourcePath.this .getTimeStamp(),
846: GlobalSourcePath.this .gpr
847: .getPaths(ClassPath.SOURCE),
848: GlobalSourcePath.this .gpr
849: .getPaths(ClassPath.BOOT),
850: GlobalSourcePath.this .gpr
851: .getPaths(ClassPath.COMPILE),
852: new HashSet(GlobalSourcePath.this .activeCps),
853: new HashMap(GlobalSourcePath.this .sourceResults),
854: new HashMap<URL, WeakValue>(
855: GlobalSourcePath.this .unknownRoots),
856: GlobalSourcePath.this .listener,
857: GlobalSourcePath.this .listener);
858: }
859: Result res = createResources(request);
860: if (GlobalSourcePath.this .debugCallBack != null) {
861: GlobalSourcePath.this .debugCallBack.run();
862: }
863: synchronized (this ) {
864: if (GlobalSourcePath.this .getTimeStamp() == res.timeStamp) {
865: if (GlobalSourcePath.this .binaryResources == null) {
866: GlobalSourcePath.this .resources = res.resources;
867: GlobalSourcePath.this .binaryResources = res.binaryResources;
868: GlobalSourcePath.this .unknownResources = res.unknownResources;
869: GlobalSourcePath.this .activeCps = res.newCps;
870: GlobalSourcePath.this .sourceResults = res.newSR;
871: GlobalSourcePath.this .translatedRoots = res.translatedRoots;
872: GlobalSourcePath.this .unknownRoots = res.unknownRoots;
873: }
874: return GlobalSourcePath.this .binaryResources;
875: } else {
876: return res.binaryResources;
877: }
878: }
879: }
880:
881: public synchronized void addPropertyChangeListener(
882: PropertyChangeListener listener) {
883: assert listener != null;
884: if (this .listeners == null) {
885: this .listeners = new ArrayList<PropertyChangeListener>();
886: }
887: this .listeners.add(listener);
888: }
889:
890: public synchronized void removePropertyChangeListener(
891: PropertyChangeListener listener) {
892: assert listener != null;
893: if (this .listeners == null) {
894: return;
895: }
896: this .listeners.remove(listener);
897: }
898:
899: void firePropertyChange() {
900: PropertyChangeListener[] _listeners;
901: synchronized (this ) {
902: if (this .listeners == null) {
903: return;
904: }
905: _listeners = this .listeners
906: .toArray(new PropertyChangeListener[this .listeners
907: .size()]);
908: }
909: PropertyChangeEvent event = new PropertyChangeEvent(this ,
910: PROP_RESOURCES, null, null);
911: for (PropertyChangeListener l : _listeners) {
912: l.propertyChange(event);
913: }
914: }
915: }
916:
917: private class Listener implements GlobalPathRegistryListener,
918: PropertyChangeListener, ChangeListener {
919:
920: private WeakReference<Object> lastPropagationId;
921:
922: public void pathsAdded(GlobalPathRegistryEvent event) {
923: resetCacheAndFire();
924: }
925:
926: public void pathsRemoved(GlobalPathRegistryEvent event) {
927: resetCacheAndFire();
928: }
929:
930: public void propertyChange(PropertyChangeEvent evt) {
931: String propName = evt.getPropertyName();
932: if (ClassPath.PROP_ENTRIES.equals(propName)) {
933: resetCacheAndFire();
934: } else if (ClassPath.PROP_INCLUDES.equals(propName)) {
935: PropertyChangeListener _excludesListener;
936: _excludesListener = excludesListener;
937: if (_excludesListener != null) {
938: final Object newPropagationId = evt
939: .getPropagationId();
940: boolean fire;
941: synchronized (this ) {
942: fire = (newPropagationId == null
943: || lastPropagationId == null || lastPropagationId
944: .get() != newPropagationId);
945: lastPropagationId = new WeakReference<Object>(
946: newPropagationId);
947: }
948: if (fire) {
949: PropertyChangeEvent event = new PropertyChangeEvent(
950: GlobalSourcePath.this , PROP_INCLUDES,
951: evt.getSource(), evt.getSource());
952: _excludesListener.propertyChange(event);
953: }
954: }
955: }
956: }
957:
958: public void stateChanged(ChangeEvent event) {
959: resetCacheAndFire();
960: }
961: }
962:
963: private class LibsListener implements PropertyChangeListener {
964:
965: public void propertyChange(PropertyChangeEvent evt) {
966: synchronized (GlobalSourcePath.this ) {
967: GlobalSourcePath.this .libsSrcs = null;
968: }
969: }
970:
971: }
972:
973: public static synchronized GlobalSourcePath getDefault() {
974: if (instance == null) {
975: instance = new GlobalSourcePath();
976: }
977: return instance;
978: }
979:
980: }
|