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.gsfret.source;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.beans.PropertyChangeListener;
047: import java.beans.PropertyChangeListener;
048: import java.lang.ref.WeakReference;
049: import java.net.URL;
050: import java.util.ArrayList;
051: import java.util.ArrayList;
052: import java.util.Collection;
053: import java.util.Collections;
054: import java.util.HashMap;
055: import java.util.HashSet;
056: import java.util.Iterator;
057: import java.util.List;
058: import java.util.Map;
059: import java.util.Set;
060: import java.util.TooManyListenersException;
061: import java.util.concurrent.CopyOnWriteArrayList;
062: import javax.swing.event.ChangeEvent;
063: import javax.swing.event.ChangeListener;
064: import org.netbeans.modules.gsfpath.api.classpath.ClassPath;
065: import org.netbeans.modules.gsfpath.api.classpath.GlobalPathRegistry;
066: import org.netbeans.modules.gsfpath.api.classpath.GlobalPathRegistryEvent;
067: import org.netbeans.modules.gsfpath.api.classpath.GlobalPathRegistryListener;
068: import org.netbeans.modules.gsfpath.api.queries.SourceForBinaryQuery;
069: import org.netbeans.modules.gsfpath.spi.classpath.ClassPathImplementation;
070: import org.netbeans.modules.gsfpath.spi.classpath.PathResourceImplementation;
071: import org.netbeans.modules.gsfpath.spi.classpath.support.ClassPathSupport;
072: import org.openide.ErrorManager;
073: import org.openide.filesystems.FileObject;
074: import org.openide.filesystems.FileStateInvalidException;
075: import org.openide.util.Utilities;
076: import org.openide.util.WeakListeners;
077:
078: /**
079: * This file is originally from Retouche, the Java Support
080: * infrastructure in NetBeans. I have modified the file as little
081: * as possible to make merging Retouche fixes back as simple as
082: * possible.
083: *
084: *
085: * @author Tomas Zezula
086: */
087: public class GlobalSourcePath {
088:
089: public static final String PROP_INCLUDES = ClassPath.PROP_INCLUDES;
090:
091: private static GlobalSourcePath instance;
092:
093: private final GlobalPathRegistry gpr;
094: private List<? extends PathResourceImplementation> resources;
095: private List<? extends PathResourceImplementation> unknownResources;
096: private List<? extends PathResourceImplementation> binaryResources;
097: private Set<ClassPath> activeCps;
098: private Map<URL, SourceForBinaryQuery.Result> sourceResults;
099: private Map<URL, URL[]> translatedRoots;
100: private Map<URL, WeakValue> unknownRoots;
101: private long timeStamp; //Lamport event ordering
102: private Runnable debugCallBack;
103:
104: private final SourcePathImplementation sourcePath;
105: private final BinaryPathImplementation binaryPath;
106: private final UnknownSourcePathImplementation unknownSourcePath;
107:
108: private final Listener listener;
109:
110: private PropertyChangeListener excludesListener;
111:
112: /** Creates a new instance of GlobalSourcePath */
113: private GlobalSourcePath() {
114: this .listener = new Listener();
115: this .sourcePath = new SourcePathImplementation();
116: this .binaryPath = new BinaryPathImplementation();
117: this .unknownSourcePath = new UnknownSourcePathImplementation();
118: this .timeStamp = -1;
119: this .gpr = GlobalPathRegistry.getDefault();
120: this .activeCps = Collections.emptySet();
121: this .sourceResults = Collections.emptyMap();
122: this .unknownRoots = new HashMap<URL, WeakValue>();
123: this .translatedRoots = new HashMap<URL, URL[]>();
124: this .gpr
125: .addGlobalPathRegistryListener((GlobalPathRegistryListener) WeakListeners
126: .create(GlobalPathRegistryListener.class,
127: this .listener, this .gpr));
128: }
129:
130: public void setExcludesListener(
131: final PropertyChangeListener listener)
132: throws TooManyListenersException {
133: if (this .excludesListener != null) {
134: throw new TooManyListenersException();
135: }
136: this .excludesListener = listener;
137: }
138:
139: public URL[] getSourceRootForBinaryRoot(final URL binaryRoot,
140: final ClassPath definingClassPath, final boolean fire) {
141: URL[] result = this .translatedRoots.get(binaryRoot);
142: if (result != null) {
143: if (result.length > 0) {
144: return result;
145: } else {
146: return null;
147: }
148: } else {
149: List<URL> cacheRoots = new ArrayList<URL>();
150: Collection<? extends PathResourceImplementation> unknownRes = getSources(
151: SourceForBinaryQuery.findSourceRoots(binaryRoot)
152: .getRoots(), cacheRoots, null);
153: if (unknownRes.isEmpty()) {
154: return null;
155: } else {
156: result = new URL[cacheRoots.size()];
157: synchronized (this ) {
158: Iterator<URL> it = cacheRoots.iterator();
159: for (int i = 0; it.hasNext(); i++) {
160: result[i] = it.next();
161: unknownRoots.put(result[i], new WeakValue(
162: definingClassPath, result[i]));
163: }
164: }
165: if (fire) {
166: this .resetCacheAndFire();
167: }
168: return result;
169: }
170: }
171: }
172:
173: public ClassPathImplementation getSourcePath() {
174: return this .sourcePath;
175: }
176:
177: public ClassPathImplementation getUnknownSourcePath() {
178: return this .unknownSourcePath;
179: }
180:
181: public ClassPathImplementation getBinaryPath() {
182: return this .binaryPath;
183: }
184:
185: private void resetCacheAndFire() {
186: synchronized (this ) {
187: this .resources = null;
188: this .binaryResources = null;
189: this .unknownResources = null;
190: this .timeStamp++;
191: }
192: this .sourcePath.firePropertyChange();
193: this .binaryPath.firePropertyChange();
194: this .unknownSourcePath.firePropertyChange();
195: }
196:
197: private static Result createResources(final Request r) {
198: assert r != null;
199: Set<PathResourceImplementation> result = new HashSet<PathResourceImplementation>();
200: Set<PathResourceImplementation> unknownResult = new HashSet<PathResourceImplementation>();
201: Set<PathResourceImplementation> binaryResult = new HashSet<PathResourceImplementation>();
202: final Map<URL, URL[]> translatedRoots = new HashMap<URL, URL[]>();
203: Set<ClassPath> newCps = new HashSet<ClassPath>();
204: for (ClassPath cp : r.sourceCps) {
205: boolean isNew = !r.oldCps.remove(cp);
206: for (ClassPath.Entry entry : cp.entries()) {
207: result.add(ClassPathSupport.createResource(entry
208: .getURL()));
209: }
210: boolean notContained = newCps.add(cp);
211: if (isNew && notContained) {
212: cp.addPropertyChangeListener(r.propertyListener);
213: }
214: }
215: // BEGIN TOR MODIFICATIONS
216: // NEW: Treat bootCps as a source path, not a binary - I want to scan
217: // directories
218: for (ClassPath cp : r.bootCps) {
219: boolean isNew = !r.oldCps.remove(cp);
220: for (ClassPath.Entry entry : cp.entries()) {
221: result.add(ClassPathSupport.createResource(entry
222: .getURL()));
223: }
224: boolean notContained = newCps.add(cp);
225: if (isNew && notContained) {
226: cp.addPropertyChangeListener(r.propertyListener);
227: }
228: }
229: // END TOR MODIFICATIONS
230: Map<URL, SourceForBinaryQuery.Result> newSR = new HashMap<URL, SourceForBinaryQuery.Result>();
231: /*
232: for (ClassPath cp : r.bootCps) {
233: boolean isNew = !r.oldCps.remove(cp);
234: for (ClassPath.Entry entry : cp.entries()) {
235: URL url = entry.getURL();
236: if (!translatedRoots.containsKey(url)) {
237: SourceForBinaryQuery.Result sr = r.oldSR.remove (url);
238: boolean isNewSR;
239: if (sr == null) {
240: sr = SourceForBinaryQuery.findSourceRoots(url);
241: isNewSR = true;
242: }
243: else {
244: isNewSR = false;
245: }
246: assert !newSR.containsKey(url);
247: newSR.put(url,sr);
248: List<URL> cacheURLs = new ArrayList<URL> ();
249: Collection<? extends PathResourceImplementation> srcRoots = getSources (sr.getRoots(), cacheURLs, r.unknownRoots);
250: if (srcRoots.isEmpty()) {
251: binaryResult.add (ClassPathSupport.createResource(url));
252: }
253: else {
254: result.addAll(srcRoots);
255: }
256: translatedRoots.put(url, cacheURLs.toArray(new URL[cacheURLs.size()]));
257: if (isNewSR) {
258: sr.addChangeListener(r.changeListener);
259: }
260: }
261: }
262: boolean notContained = newCps.add (cp);
263: if (isNew && notContained) {
264: cp.addPropertyChangeListener(r.propertyListener);
265: }
266: }
267: */
268:
269: for (ClassPath cp : r.compileCps) {
270: boolean isNew = !r.oldCps.remove(cp);
271: for (ClassPath.Entry entry : cp.entries()) {
272: URL url = entry.getURL();
273: if (!translatedRoots.containsKey(url)) {
274: SourceForBinaryQuery.Result sr = r.oldSR
275: .remove(url);
276: boolean isNewSR;
277: if (sr == null) {
278: sr = SourceForBinaryQuery.findSourceRoots(url);
279: isNewSR = true;
280: } else {
281: isNewSR = false;
282: }
283: assert !newSR.containsKey(url);
284: newSR.put(url, sr);
285: List<URL> cacheURLs = new ArrayList<URL>();
286: Collection<? extends PathResourceImplementation> srcRoots = getSources(
287: sr.getRoots(), cacheURLs, r.unknownRoots);
288: if (srcRoots.isEmpty()) {
289: binaryResult.add(ClassPathSupport
290: .createResource(url));
291: } else {
292: result.addAll(srcRoots);
293: }
294: translatedRoots.put(url, cacheURLs
295: .toArray(new URL[cacheURLs.size()]));
296: if (isNewSR) {
297: sr.addChangeListener(r.changeListener);
298: }
299: }
300: }
301: boolean notContained = newCps.add(cp);
302: if (isNew && notContained) {
303: cp.addPropertyChangeListener(r.propertyListener);
304: }
305: }
306:
307: for (ClassPath cp : r.oldCps) {
308: cp.removePropertyChangeListener(r.propertyListener);
309: }
310:
311: for (Map.Entry<URL, SourceForBinaryQuery.Result> entry : r.oldSR
312: .entrySet()) {
313: entry.getValue().removeChangeListener(r.changeListener);
314: }
315: for (URL unknownRoot : r.unknownRoots.keySet()) {
316: unknownResult.add(ClassPathSupport
317: .createResource(unknownRoot));
318: }
319: return new Result(
320: r.timeStamp,
321: new ArrayList<PathResourceImplementation>(result),
322: new ArrayList(binaryResult),
323: new ArrayList<PathResourceImplementation>(unknownResult),
324: newCps, newSR, translatedRoots, r.unknownRoots);
325: }
326:
327: /**
328: * Unit test method, used to set a callback which is called
329: * form getResources to emulate a race condition.
330: * The access to debugCallBack is not synchronized, it should be
331: * set before test and unset after test.
332: * @param callBack to be called
333: *
334: **/
335: void setDebugCallBack(final Runnable callBack) {
336: this .debugCallBack = callBack;
337: }
338:
339: private static Collection<? extends PathResourceImplementation> getSources(
340: final FileObject[] roots, final List<URL> cacheDirs,
341: final Map<URL, WeakValue> unknownRoots) {
342: assert roots != null;
343: URL[] urls = new URL[roots.length];
344: boolean add = true;
345: for (int i = 0; i < roots.length; i++) {
346: try {
347: URL url = roots[i].getURL();
348: if (!"file".equals(url.getProtocol())) { //NOI18N
349: add = false;
350: break;
351: }
352: urls[i] = url;
353: } catch (FileStateInvalidException e) {
354: ErrorManager.getDefault().notify(e);
355: }
356: }
357: if (add) {
358: List<PathResourceImplementation> result = new ArrayList<PathResourceImplementation>(
359: roots.length);
360: for (int i = 0; i < urls.length; i++) {
361: if (cacheDirs != null) {
362: cacheDirs.add(urls[i]);
363: }
364: if (unknownRoots != null) {
365: unknownRoots.remove(urls[i]);
366: }
367: result.add(ClassPathSupport.createResource(urls[i]));
368: }
369: return result;
370: }
371: return Collections.<PathResourceImplementation> emptySet();
372: }
373:
374: private class WeakValue extends WeakReference<ClassPath> implements
375: Runnable {
376:
377: private URL key;
378:
379: public WeakValue(ClassPath ref, URL key) {
380: super (ref, Utilities.activeReferenceQueue());
381: assert key != null;
382: this .key = key;
383: }
384:
385: public void run() {
386: boolean fire = false;
387: synchronized (GlobalSourcePath.this ) {
388: fire = (GlobalSourcePath.this .unknownRoots.remove(key) != null);
389: }
390: if (fire) {
391: GlobalSourcePath.this .resetCacheAndFire();
392: }
393: }
394: }
395:
396: private long getTimeStamp() {
397: return this .timeStamp;
398: }
399:
400: private static class Request {
401:
402: final long timeStamp;
403: final Set<ClassPath> sourceCps;
404: final Set<ClassPath> bootCps;
405: final Set<ClassPath> compileCps;
406: final Set<ClassPath> oldCps;
407: final Map<URL, SourceForBinaryQuery.Result> oldSR;
408: final Map<URL, WeakValue> unknownRoots;
409: final PropertyChangeListener propertyListener;
410: final ChangeListener changeListener;
411:
412: public Request(final long timeStamp,
413: final Set<ClassPath> sourceCps,
414: final Set<ClassPath> bootCps,
415: final Set<ClassPath> compileCps,
416: final Set<ClassPath> oldCps,
417: final Map<URL, SourceForBinaryQuery.Result> oldSR,
418: final Map<URL, WeakValue> unknownRoots,
419: final PropertyChangeListener propertyListener,
420: final ChangeListener changeListener) {
421: assert sourceCps != null;
422: assert bootCps != null;
423: assert compileCps != null;
424: assert oldCps != null;
425: assert oldSR != null;
426: assert unknownRoots != null;
427: assert propertyListener != null;
428: assert changeListener != null;
429:
430: this .timeStamp = timeStamp;
431: this .sourceCps = sourceCps;
432: this .bootCps = bootCps;
433: this .compileCps = compileCps;
434: this .oldCps = oldCps;
435: this .oldSR = oldSR;
436: this .unknownRoots = unknownRoots;
437: this .propertyListener = propertyListener;
438: this .changeListener = changeListener;
439: }
440: }
441:
442: private static class Result {
443:
444: final long timeStamp;
445: final List<? extends PathResourceImplementation> resources;
446: final List<? extends PathResourceImplementation> binaryResources;
447: final List<? extends PathResourceImplementation> unknownResources;
448: final Set<ClassPath> newCps;
449: final Map<URL, SourceForBinaryQuery.Result> newSR;
450: final Map<URL, URL[]> translatedRoots;
451: final Map<URL, WeakValue> unknownRoots;
452:
453: public Result(
454: final long timeStamp,
455: final List<? extends PathResourceImplementation> resources,
456: final List<? extends PathResourceImplementation> binaryResources,
457: final List<? extends PathResourceImplementation> unknownResources,
458: final Set<ClassPath> newCps,
459: final Map<URL, SourceForBinaryQuery.Result> newSR,
460: final Map<URL, URL[]> translatedRoots,
461: final Map<URL, WeakValue> unknownRoots) {
462: assert resources != null;
463: assert binaryResources != null;
464: assert unknownResources != null;
465: assert newCps != null;
466: assert newSR != null;
467: assert translatedRoots != null;
468: this .timeStamp = timeStamp;
469: this .resources = resources;
470: this .binaryResources = binaryResources;
471: this .unknownResources = unknownResources;
472: this .newCps = newCps;
473: this .newSR = newSR;
474: this .translatedRoots = translatedRoots;
475: this .unknownRoots = unknownRoots;
476: }
477: }
478:
479: private class SourcePathImplementation implements
480: ClassPathImplementation {
481:
482: private List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
483:
484: public List<? extends PathResourceImplementation> getResources() {
485: Request request;
486: synchronized (GlobalSourcePath.this ) {
487: if (GlobalSourcePath.this .resources != null) {
488: return GlobalSourcePath.this .resources;
489: }
490: request = new Request(
491: GlobalSourcePath.this .getTimeStamp(),
492: GlobalSourcePath.this .gpr
493: .getPaths(ClassPath.SOURCE),
494: GlobalSourcePath.this .gpr
495: .getPaths(ClassPath.BOOT),
496: GlobalSourcePath.this .gpr
497: .getPaths(ClassPath.COMPILE),
498: new HashSet(GlobalSourcePath.this .activeCps),
499: new HashMap(GlobalSourcePath.this .sourceResults),
500: new HashMap<URL, WeakValue>(
501: GlobalSourcePath.this .unknownRoots),
502: GlobalSourcePath.this .listener,
503: GlobalSourcePath.this .listener);
504: }
505: Result res = createResources(request);
506: if (GlobalSourcePath.this .debugCallBack != null) {
507: GlobalSourcePath.this .debugCallBack.run();
508: }
509: synchronized (this ) {
510: if (GlobalSourcePath.this .getTimeStamp() == res.timeStamp) {
511: if (GlobalSourcePath.this .resources == null) {
512: GlobalSourcePath.this .resources = res.resources;
513: GlobalSourcePath.this .binaryResources = res.binaryResources;
514: GlobalSourcePath.this .unknownResources = res.unknownResources;
515: GlobalSourcePath.this .activeCps = res.newCps;
516: GlobalSourcePath.this .sourceResults = res.newSR;
517: GlobalSourcePath.this .translatedRoots = res.translatedRoots;
518: GlobalSourcePath.this .unknownRoots = res.unknownRoots;
519: }
520: return GlobalSourcePath.this .resources;
521: } else {
522: return res.resources;
523: }
524: }
525: }
526:
527: public synchronized void addPropertyChangeListener(
528: PropertyChangeListener listener) {
529: assert listener != null;
530: if (this .listeners == null) {
531: this .listeners = new ArrayList<PropertyChangeListener>();
532: }
533: this .listeners.add(listener);
534: }
535:
536: public void removePropertyChangeListener(
537: PropertyChangeListener listener) {
538: assert listener != null;
539: if (this .listeners == null) {
540: return;
541: }
542: this .listeners.remove(listener);
543: }
544:
545: void firePropertyChange() {
546: PropertyChangeListener[] _listeners;
547: synchronized (this ) {
548: if (this .listeners == null) {
549: return;
550: }
551: _listeners = this .listeners
552: .toArray(new PropertyChangeListener[this .listeners
553: .size()]);
554: }
555: PropertyChangeEvent event = new PropertyChangeEvent(this ,
556: PROP_RESOURCES, null, null);
557: for (PropertyChangeListener l : _listeners) {
558: l.propertyChange(event);
559: }
560: }
561: }
562:
563: private class UnknownSourcePathImplementation implements
564: ClassPathImplementation {
565:
566: private List<PropertyChangeListener> listeners = new CopyOnWriteArrayList<PropertyChangeListener>();
567:
568: public List<? extends PathResourceImplementation> getResources() {
569: Request request;
570: synchronized (GlobalSourcePath.this ) {
571: if (GlobalSourcePath.this .unknownResources != null) {
572: return GlobalSourcePath.this .unknownResources;
573: }
574: request = new Request(
575: GlobalSourcePath.this .getTimeStamp(),
576: GlobalSourcePath.this .gpr
577: .getPaths(ClassPath.SOURCE),
578: GlobalSourcePath.this .gpr
579: .getPaths(ClassPath.BOOT),
580: GlobalSourcePath.this .gpr
581: .getPaths(ClassPath.COMPILE),
582: new HashSet(GlobalSourcePath.this .activeCps),
583: new HashMap(GlobalSourcePath.this .sourceResults),
584: new HashMap<URL, WeakValue>(
585: GlobalSourcePath.this .unknownRoots),
586: GlobalSourcePath.this .listener,
587: GlobalSourcePath.this .listener);
588: }
589: Result res = createResources(request);
590: if (GlobalSourcePath.this .debugCallBack != null) {
591: GlobalSourcePath.this .debugCallBack.run();
592: }
593: synchronized (this ) {
594: if (GlobalSourcePath.this .getTimeStamp() == res.timeStamp) {
595: if (GlobalSourcePath.this .binaryResources == null) {
596: GlobalSourcePath.this .resources = res.resources;
597: GlobalSourcePath.this .binaryResources = res.binaryResources;
598: GlobalSourcePath.this .unknownResources = res.unknownResources;
599: GlobalSourcePath.this .activeCps = res.newCps;
600: GlobalSourcePath.this .sourceResults = res.newSR;
601: GlobalSourcePath.this .translatedRoots = res.translatedRoots;
602: GlobalSourcePath.this .unknownRoots = res.unknownRoots;
603: }
604: return GlobalSourcePath.this .unknownResources;
605: } else {
606: return res.unknownResources;
607: }
608: }
609: }
610:
611: public void addPropertyChangeListener(
612: PropertyChangeListener listener) {
613: assert listener != null;
614: this .listeners.add(listener);
615: }
616:
617: public void removePropertyChangeListener(
618: PropertyChangeListener listener) {
619: assert listener != null;
620: this .listeners.remove(listener);
621: }
622:
623: void firePropertyChange() {
624: PropertyChangeEvent event = new PropertyChangeEvent(this ,
625: PROP_RESOURCES, null, null);
626: for (PropertyChangeListener l : this .listeners) {
627: l.propertyChange(event);
628: }
629: }
630: }
631:
632: private class BinaryPathImplementation implements
633: ClassPathImplementation {
634: private List<PropertyChangeListener> listeners = new ArrayList<PropertyChangeListener>();
635:
636: public List<? extends PathResourceImplementation> getResources() {
637: Request request;
638: synchronized (GlobalSourcePath.this ) {
639: if (GlobalSourcePath.this .binaryResources != null) {
640: return GlobalSourcePath.this .binaryResources;
641: }
642: request = new Request(
643: GlobalSourcePath.this .getTimeStamp(),
644: GlobalSourcePath.this .gpr
645: .getPaths(ClassPath.SOURCE),
646: GlobalSourcePath.this .gpr
647: .getPaths(ClassPath.BOOT),
648: GlobalSourcePath.this .gpr
649: .getPaths(ClassPath.COMPILE),
650: new HashSet(GlobalSourcePath.this .activeCps),
651: new HashMap(GlobalSourcePath.this .sourceResults),
652: new HashMap<URL, WeakValue>(
653: GlobalSourcePath.this .unknownRoots),
654: GlobalSourcePath.this .listener,
655: GlobalSourcePath.this .listener);
656: }
657: Result res = createResources(request);
658: if (GlobalSourcePath.this .debugCallBack != null) {
659: GlobalSourcePath.this .debugCallBack.run();
660: }
661: synchronized (this ) {
662: if (GlobalSourcePath.this .getTimeStamp() == res.timeStamp) {
663: if (GlobalSourcePath.this .binaryResources == null) {
664: GlobalSourcePath.this .resources = res.resources;
665: GlobalSourcePath.this .binaryResources = res.binaryResources;
666: GlobalSourcePath.this .unknownResources = res.unknownResources;
667: GlobalSourcePath.this .activeCps = res.newCps;
668: GlobalSourcePath.this .sourceResults = res.newSR;
669: GlobalSourcePath.this .translatedRoots = res.translatedRoots;
670: GlobalSourcePath.this .unknownRoots = res.unknownRoots;
671: }
672: return GlobalSourcePath.this .binaryResources;
673: } else {
674: return res.binaryResources;
675: }
676: }
677: }
678:
679: public synchronized void addPropertyChangeListener(
680: PropertyChangeListener listener) {
681: assert listener != null;
682: if (this .listeners == null) {
683: this .listeners = new ArrayList<PropertyChangeListener>();
684: }
685: this .listeners.add(listener);
686: }
687:
688: public void removePropertyChangeListener(
689: PropertyChangeListener listener) {
690: assert listener != null;
691: if (this .listeners == null) {
692: return;
693: }
694: this .listeners.remove(listener);
695: }
696:
697: void firePropertyChange() {
698: PropertyChangeListener[] _listeners;
699: synchronized (this ) {
700: if (this .listeners == null) {
701: return;
702: }
703: _listeners = this .listeners
704: .toArray(new PropertyChangeListener[this .listeners
705: .size()]);
706: }
707: PropertyChangeEvent event = new PropertyChangeEvent(this ,
708: PROP_RESOURCES, null, null);
709: for (PropertyChangeListener l : _listeners) {
710: l.propertyChange(event);
711: }
712: }
713: }
714:
715: private class Listener implements GlobalPathRegistryListener,
716: PropertyChangeListener, ChangeListener {
717:
718: private Object lastPropagationId;
719:
720: public void pathsAdded(GlobalPathRegistryEvent event) {
721: resetCacheAndFire();
722: }
723:
724: public void pathsRemoved(GlobalPathRegistryEvent event) {
725: resetCacheAndFire();
726: }
727:
728: public void propertyChange(PropertyChangeEvent evt) {
729: String propName = evt.getPropertyName();
730: if (ClassPath.PROP_ENTRIES.equals(propName)) {
731: resetCacheAndFire();
732: } else if (ClassPath.PROP_INCLUDES.equals(propName)) {
733: if (excludesListener != null) {
734: final Object newPropagationId = evt
735: .getPropagationId();
736: if (newPropagationId == null
737: || lastPropagationId != newPropagationId) {
738: PropertyChangeEvent event = new PropertyChangeEvent(
739: this , PROP_INCLUDES, evt.getSource(),
740: evt.getSource());
741: excludesListener.propertyChange(event);
742: }
743: lastPropagationId = newPropagationId;
744: }
745: }
746: }
747:
748: public void stateChanged(ChangeEvent event) {
749: resetCacheAndFire();
750: }
751: }
752:
753: public static synchronized GlobalSourcePath getDefault() {
754: if (instance == null) {
755: instance = new GlobalSourcePath();
756: }
757: return instance;
758: }
759:
760: }
|