1 19 20 package org.netbeans.api.java.classpath; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.util.ArrayList ; 25 import java.util.Arrays ; 26 import java.util.Collections ; 27 import java.util.HashMap ; 28 import java.util.HashSet ; 29 import java.util.LinkedHashSet ; 30 import java.util.LinkedList ; 31 import java.util.List ; 32 import java.util.Map ; 33 import java.util.Set ; 34 import javax.swing.event.ChangeEvent ; 35 import javax.swing.event.ChangeListener ; 36 import org.netbeans.api.java.queries.SourceForBinaryQuery; 37 import org.openide.filesystems.FileObject; 38 39 83 public final class GlobalPathRegistry { 84 85 private static GlobalPathRegistry DEFAULT = new GlobalPathRegistry(); 86 87 91 public static GlobalPathRegistry getDefault() { 92 return DEFAULT; 93 } 94 95 private int resetCount; 96 private final Map <String ,List <ClassPath>> paths = new HashMap <String ,List <ClassPath>>(); 97 private final List <GlobalPathRegistryListener> listeners = new ArrayList <GlobalPathRegistryListener>(); 98 private Set <FileObject> sourceRoots = null; 99 private Set <SourceForBinaryQuery.Result> results = new HashSet <SourceForBinaryQuery.Result>(); 100 101 102 private final ChangeListener resultListener = new SFBQListener (); 103 104 private PropertyChangeListener classpathListener = new PropertyChangeListener () { 105 public void propertyChange(PropertyChangeEvent evt) { 106 synchronized (GlobalPathRegistry.this) { 107 GlobalPathRegistry.this.resetSourceRootsCache (); 109 } 110 } 111 }; 112 113 private GlobalPathRegistry() {} 114 115 116 void clear() { 117 paths.clear(); 118 listeners.clear(); 119 } 120 121 126 public synchronized Set <ClassPath> getPaths(String id) { 127 if (id == null) { 128 throw new NullPointerException (); 129 } 130 List <ClassPath> l = paths.get(id); 131 if (l != null && !l.isEmpty()) { 132 return Collections.unmodifiableSet(new HashSet <ClassPath>(l)); 133 } else { 134 return Collections.<ClassPath>emptySet(); 135 } 136 } 137 138 143 public void register(String id, ClassPath[] paths) { 144 if (id == null || paths == null) { 145 throw new NullPointerException (); 146 } 147 GlobalPathRegistryEvent evt = null; 148 GlobalPathRegistryListener[] _listeners = null; 149 synchronized (this) { 150 List <ClassPath> l = this.paths.get(id); 151 if (l == null) { 152 l = new ArrayList <ClassPath>(); 153 this.paths.put(id, l); 154 } 155 Set <ClassPath> added = listeners.isEmpty() ? null : new HashSet <ClassPath>(); 156 for (ClassPath path : paths) { 157 if (path == null) { 158 throw new NullPointerException ("Null path encountered in " + Arrays.asList(paths) + " of type " + id); } 160 if (added != null && !added.contains(path) && !l.contains(path)) { 161 added.add(path); 162 } 163 if (!l.contains(path)) { 164 path.addPropertyChangeListener(classpathListener); 165 } 166 l.add(path); 167 } 168 if (added != null && !added.isEmpty()) { 169 _listeners = listeners.toArray(new GlobalPathRegistryListener[listeners.size()]); 170 evt = new GlobalPathRegistryEvent(this, id, Collections.unmodifiableSet(added)); 171 } 172 resetSourceRootsCache (); 174 } 175 if (_listeners != null) { 176 assert evt != null; 177 for (GlobalPathRegistryListener listener : _listeners) { 178 listener.pathsAdded(evt); 179 } 180 } 181 } 182 183 189 public void unregister(String id, ClassPath[] paths) throws IllegalArgumentException { 190 if (id == null || paths == null) { 191 throw new NullPointerException (); 192 } 193 GlobalPathRegistryEvent evt = null; 194 GlobalPathRegistryListener[] _listeners = null; 195 synchronized (this) { 196 List <ClassPath> l = this.paths.get(id); 197 if (l == null) { 198 l = new ArrayList <ClassPath>(); 199 } 200 List <ClassPath> l2 = new ArrayList <ClassPath>(l); Set <ClassPath> removed = listeners.isEmpty() ? null : new HashSet <ClassPath>(); 202 for (ClassPath path : paths) { 203 if (path == null) { 204 throw new NullPointerException (); 205 } 206 if (!l2.remove(path)) { 207 throw new IllegalArgumentException ("Attempt to remove nonexistent path " + path); } 209 if (removed != null && !removed.contains(path) && !l2.contains(path)) { 210 removed.add(path); 211 } 212 if (!l2.contains(path)) { 213 path.removePropertyChangeListener(classpathListener); 214 } 215 } 216 this.paths.put(id, l2); 217 if (removed != null && !removed.isEmpty()) { 218 _listeners = listeners.toArray(new GlobalPathRegistryListener[listeners.size()]); 219 evt = new GlobalPathRegistryEvent(this, id, Collections.unmodifiableSet(removed)); 220 } 221 resetSourceRootsCache (); 222 } 223 if (_listeners != null) { 224 assert evt != null; 225 for (GlobalPathRegistryListener listener : _listeners) { 226 listener.pathsRemoved(evt); 227 } 228 } 229 } 230 231 235 public synchronized void addGlobalPathRegistryListener(GlobalPathRegistryListener l) { 236 if (l == null) { 237 throw new NullPointerException (); 238 } 239 listeners.add(l); 240 } 241 242 246 public synchronized void removeGlobalPathRegistryListener(GlobalPathRegistryListener l) { 247 if (l == null) { 248 throw new NullPointerException (); 249 } 250 listeners.remove(l); 251 } 252 253 275 public Set <FileObject> getSourceRoots() { 276 int currentResetCount; 277 Set <ClassPath> sourcePaths, compileAndBootPaths; 278 synchronized (this) { 279 if (this.sourceRoots != null) { 280 return this.sourceRoots; 281 } 282 currentResetCount = this.resetCount; 283 sourcePaths = getPaths(ClassPath.SOURCE); 284 compileAndBootPaths = new LinkedHashSet <ClassPath>(getPaths(ClassPath.COMPILE)); 285 compileAndBootPaths.addAll(getPaths(ClassPath.BOOT)); 286 } 287 288 Set <FileObject> newSourceRoots = new LinkedHashSet <FileObject>(); 289 for (ClassPath sp : sourcePaths) { 290 newSourceRoots.addAll(Arrays.asList(sp.getRoots())); 291 } 292 293 final List <SourceForBinaryQuery.Result> newResults = new LinkedList <SourceForBinaryQuery.Result> (); 294 final ChangeListener tmpResultListener = new SFBQListener (); 295 for (ClassPath cp : compileAndBootPaths) { 296 for (ClassPath.Entry entry : cp.entries()) { 297 SourceForBinaryQuery.Result result = SourceForBinaryQuery.findSourceRoots(entry.getURL()); 298 result.addChangeListener(tmpResultListener); 299 newResults.add (result); 300 FileObject[] someRoots = result.getRoots(); 301 newSourceRoots.addAll(Arrays.asList(someRoots)); 302 } 303 } 304 305 newSourceRoots = Collections.unmodifiableSet(newSourceRoots); 306 synchronized (this) { 307 if (this.resetCount == currentResetCount) { 308 this.sourceRoots = newSourceRoots; 309 removeTmpSFBQListeners (newResults, tmpResultListener, true); 310 this.results.addAll (newResults); 311 } 312 else { 313 removeTmpSFBQListeners (newResults, tmpResultListener, false); 314 } 315 return newSourceRoots; 316 } 317 } 318 319 320 private void removeTmpSFBQListeners (List <? extends SourceForBinaryQuery.Result> results, ChangeListener listener, boolean addListener) { 321 for (SourceForBinaryQuery.Result res : results) { 322 if (addListener) { 323 res.addChangeListener (this.resultListener); 324 } 325 res.removeChangeListener(listener); 326 } 327 } 328 329 339 public FileObject findResource(String resource) { 340 for (ClassPath cp : getPaths(ClassPath.SOURCE)) { 342 FileObject f = cp.findResource(resource); 343 if (f != null) { 344 return f; 345 } 346 } 347 return null; 348 } 349 350 351 private synchronized void resetSourceRootsCache () { 352 this.sourceRoots = null; 353 for (SourceForBinaryQuery.Result result : results) { 354 result.removeChangeListener(this.resultListener); 355 } 356 this.resetCount++; 357 } 358 359 private class SFBQListener implements ChangeListener { 360 361 public void stateChanged (ChangeEvent event) { 362 synchronized (GlobalPathRegistry.this) { 363 GlobalPathRegistry.this.resetSourceRootsCache (); 365 } 366 } 367 }; 368 369 } 370 | Popular Tags |