1 19 20 package org.netbeans.modules.retouche.source; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.beans.PropertyChangeListener ; 25 import java.beans.PropertyChangeListener ; 26 import java.lang.ref.WeakReference ; 27 import java.net.URL ; 28 import java.util.ArrayList ; 29 import java.util.ArrayList ; 30 import java.util.Collection ; 31 import java.util.Collections ; 32 import java.util.HashMap ; 33 import java.util.HashSet ; 34 import java.util.Iterator ; 35 import java.util.List ; 36 import java.util.Map ; 37 import java.util.Set ; 38 import java.util.concurrent.CopyOnWriteArrayList ; 39 import javax.swing.event.ChangeEvent ; 40 import javax.swing.event.ChangeListener ; 41 import org.netbeans.api.java.classpath.ClassPath; 42 import org.netbeans.api.java.classpath.GlobalPathRegistry; 43 import org.netbeans.api.java.classpath.GlobalPathRegistryEvent; 44 import org.netbeans.api.java.classpath.GlobalPathRegistryListener; 45 import org.netbeans.api.java.queries.SourceForBinaryQuery; 46 import org.netbeans.spi.java.classpath.ClassPathImplementation; 47 import org.netbeans.spi.java.classpath.PathResourceImplementation; 48 import org.netbeans.spi.java.classpath.support.ClassPathSupport; 49 import org.openide.ErrorManager; 50 import org.openide.filesystems.FileObject; 51 import org.openide.filesystems.FileStateInvalidException; 52 import org.openide.util.Utilities; 53 import org.openide.util.WeakListeners; 54 55 64 public class GlobalSourcePath { 65 66 private static GlobalSourcePath instance; 67 68 private final GlobalPathRegistry gpr; 69 private List <? extends PathResourceImplementation> resources; 70 private List <? extends PathResourceImplementation> unknownResources; 71 private List <? extends PathResourceImplementation> binaryResources; 72 private Set <ClassPath> activeCps; 73 private Map <URL , SourceForBinaryQuery.Result> sourceResults; 74 private Map <URL , URL []> translatedRoots; 75 private Map <URL , WeakValue> unknownRoots; 76 private long timeStamp; private Runnable debugCallBack; 78 79 private final SourcePathImplementation sourcePath; 80 private final BinaryPathImplementation binaryPath; 81 private final UnknownSourcePathImplementation unknownSourcePath; 82 83 private final Listener listener; 84 85 86 private GlobalSourcePath() { 87 this.listener = new Listener (); 88 this.sourcePath = new SourcePathImplementation (); 89 this.binaryPath = new BinaryPathImplementation (); 90 this.unknownSourcePath = new UnknownSourcePathImplementation (); 91 this.timeStamp = -1; 92 this.gpr = GlobalPathRegistry.getDefault(); 93 this.activeCps = Collections.emptySet(); 94 this.sourceResults = Collections.emptyMap(); 95 this.unknownRoots = new HashMap <URL , WeakValue>(); 96 this.translatedRoots = new HashMap <URL , URL []> (); 97 this.gpr.addGlobalPathRegistryListener ((GlobalPathRegistryListener)WeakListeners.create(GlobalPathRegistryListener.class,this.listener,this.gpr)); 98 } 99 100 public URL [] getSourceRootForBinaryRoot (final URL binaryRoot, final ClassPath definingClassPath, final boolean fire) { 101 URL [] result = this.translatedRoots.get(binaryRoot); 102 if (result != null) { 103 if (result.length > 0) { 104 return result; 105 } 106 else { 107 return null; 108 } 109 } 110 else { 111 List <URL > cacheRoots = new ArrayList <URL > (); 112 Collection <? extends PathResourceImplementation> unknownRes = getSources(SourceForBinaryQuery.findSourceRoots(binaryRoot).getRoots(),cacheRoots,null); 113 if (unknownRes.isEmpty()) { 114 return null; 115 } 116 else { 117 result = new URL [cacheRoots.size()]; 118 synchronized (this) { 119 Iterator <URL > it = cacheRoots.iterator(); 120 for (int i=0; it.hasNext(); i++) { 121 result[i] = it.next (); 122 unknownRoots.put(result[i],new WeakValue(definingClassPath,result[i])); 123 } 124 } 125 if (fire) { 126 this.resetCacheAndFire(); 127 } 128 return result; 129 } 130 } 131 } 132 133 public ClassPathImplementation getSourcePath () { 134 return this.sourcePath; 135 } 136 137 public ClassPathImplementation getUnknownSourcePath () { 138 return this.unknownSourcePath; 139 } 140 141 public ClassPathImplementation getBinaryPath () { 142 return this.binaryPath; 143 } 144 145 private void resetCacheAndFire () { 146 synchronized (this) { 147 this.resources = null; 148 this.binaryResources = null; 149 this.unknownResources = null; 150 this.timeStamp++; 151 } 152 this.sourcePath.firePropertyChange (); 153 this.binaryPath.firePropertyChange (); 154 this.unknownSourcePath.firePropertyChange(); 155 } 156 157 private static Result createResources (final Request r) { 158 assert r != null; 159 Set <PathResourceImplementation> result = new HashSet <PathResourceImplementation> (); 160 Set <PathResourceImplementation> unknownResult = new HashSet <PathResourceImplementation> (); 161 Set <PathResourceImplementation> binaryResult = new HashSet <PathResourceImplementation> (); 162 final Map <URL ,URL []> translatedRoots = new HashMap <URL , URL []>(); 163 Set <ClassPath> newCps = new HashSet <ClassPath> (); 164 for (ClassPath cp : r.sourceCps) { 165 boolean isNew = !r.oldCps.remove(cp); 166 for (ClassPath.Entry entry : cp.entries()) { 167 result.add(ClassPathSupport.createResource(entry.getURL())); 168 } 169 boolean notContained = newCps.add (cp); 170 if (isNew && notContained) { 171 cp.addPropertyChangeListener(r.propertyListener); 172 } 173 } 174 for (ClassPath cp : r.bootCps) { 178 boolean isNew = !r.oldCps.remove(cp); 179 for (ClassPath.Entry entry : cp.entries()) { 180 result.add(ClassPathSupport.createResource(entry.getURL())); 181 } 182 boolean notContained = newCps.add (cp); 183 if (isNew && notContained) { 184 cp.addPropertyChangeListener(r.propertyListener); 185 } 186 } 187 Map <URL ,SourceForBinaryQuery.Result> newSR = new HashMap <URL ,SourceForBinaryQuery.Result> (); 189 226 227 for (ClassPath cp : r.compileCps) { 228 boolean isNew = !r.oldCps.remove(cp); 229 for (ClassPath.Entry entry : cp.entries()) { 230 URL url = entry.getURL(); 231 if (!translatedRoots.containsKey(url)) { 232 SourceForBinaryQuery.Result sr = r.oldSR.remove (url); 233 boolean isNewSR; 234 if (sr == null) { 235 sr = SourceForBinaryQuery.findSourceRoots(url); 236 isNewSR = true; 237 } 238 else { 239 isNewSR = false; 240 } 241 assert !newSR.containsKey(url); 242 newSR.put(url,sr); 243 List <URL > cacheURLs = new ArrayList <URL > (); 244 Collection <? extends PathResourceImplementation> srcRoots = getSources(sr.getRoots(),cacheURLs, r.unknownRoots); 245 if (srcRoots.isEmpty()) { 246 binaryResult.add(ClassPathSupport.createResource(url)); 247 } 248 else { 249 result.addAll(srcRoots); 250 } 251 translatedRoots.put(url, cacheURLs.toArray(new URL [cacheURLs.size()])); 252 if (isNewSR) { 253 sr.addChangeListener(r.changeListener); 254 } 255 } 256 } 257 boolean notContained = newCps.add (cp); 258 if (isNew && notContained) { 259 cp.addPropertyChangeListener(r.propertyListener); 260 } 261 } 262 263 for (ClassPath cp : r.oldCps) { 264 cp.removePropertyChangeListener(r.propertyListener); 265 } 266 267 for (Map.Entry <URL ,SourceForBinaryQuery.Result> entry : r.oldSR.entrySet()) { 268 entry.getValue().removeChangeListener(r.changeListener); 269 } 270 for (URL unknownRoot : r.unknownRoots.keySet()) { 271 unknownResult.add (ClassPathSupport.createResource(unknownRoot)); 272 } 273 return new Result (r.timeStamp, new ArrayList <PathResourceImplementation> (result), new ArrayList (binaryResult), new ArrayList <PathResourceImplementation>(unknownResult), 274 newCps,newSR,translatedRoots, r.unknownRoots); 275 } 276 277 285 void setDebugCallBack (final Runnable callBack) { 286 this.debugCallBack = callBack; 287 } 288 289 private static Collection <? extends PathResourceImplementation> getSources (final FileObject[] roots, final List <URL > cacheDirs, final Map <URL , WeakValue> unknownRoots) { 290 assert roots != null; 291 URL [] urls = new URL [roots.length]; 292 boolean add = true; 293 for (int i=0; i<roots.length; i++) { 294 try { 295 URL url = roots[i].getURL(); 296 if (!"file".equals(url.getProtocol())) { add = false; 298 break; 299 } 300 urls[i] = url; 301 } catch (FileStateInvalidException e) { 302 ErrorManager.getDefault().notify(e); 303 } 304 } 305 if (add) { 306 List <PathResourceImplementation> result = new ArrayList <PathResourceImplementation> (roots.length); 307 for (int i=0; i<urls.length; i++) { 308 if (cacheDirs != null) { 309 cacheDirs.add (urls[i]); 310 } 311 if (unknownRoots != null) { 312 unknownRoots.remove (urls[i]); 313 } 314 result.add(ClassPathSupport.createResource(urls[i])); 315 } 316 return result; 317 } 318 return Collections.<PathResourceImplementation>emptySet(); 319 } 320 321 private class WeakValue extends WeakReference <ClassPath> implements Runnable { 322 323 private URL key; 324 325 public WeakValue (ClassPath ref, URL key) { 326 super (ref, Utilities.activeReferenceQueue()); 327 assert key != null; 328 this.key = key; 329 } 330 331 public void run () { 332 boolean fire = false; 333 synchronized (GlobalSourcePath.this) { 334 fire = (GlobalSourcePath.this.unknownRoots.remove (key) != null); 335 } 336 if (fire) { 337 GlobalSourcePath.this.resetCacheAndFire(); 338 } 339 } 340 } 341 342 private long getTimeStamp () { 343 return this.timeStamp; 344 } 345 346 private static class Request { 347 348 final long timeStamp; 349 final Set <ClassPath> sourceCps; 350 final Set <ClassPath> bootCps; 351 final Set <ClassPath> compileCps; 352 final Set <ClassPath> oldCps; 353 final Map <URL , SourceForBinaryQuery.Result> oldSR; 354 final Map <URL , WeakValue> unknownRoots; 355 final PropertyChangeListener propertyListener; 356 final ChangeListener changeListener; 357 358 public Request (final long timeStamp, final Set <ClassPath> sourceCps, final Set <ClassPath> bootCps, final Set <ClassPath> compileCps, 359 final Set <ClassPath> oldCps, final Map <URL , SourceForBinaryQuery.Result> oldSR, final Map <URL , WeakValue> unknownRoots, 360 final PropertyChangeListener propertyListener, final ChangeListener changeListener) { 361 assert sourceCps != null; 362 assert bootCps != null; 363 assert compileCps != null; 364 assert oldCps != null; 365 assert oldSR != null; 366 assert unknownRoots != null; 367 assert propertyListener != null; 368 assert changeListener != null; 369 370 this.timeStamp = timeStamp; 371 this.sourceCps = sourceCps; 372 this.bootCps = bootCps; 373 this.compileCps = compileCps; 374 this.oldCps = oldCps; 375 this.oldSR = oldSR; 376 this.unknownRoots = unknownRoots; 377 this.propertyListener = propertyListener; 378 this.changeListener = changeListener; 379 } 380 } 381 382 private static class Result { 383 384 final long timeStamp; 385 final List <? extends PathResourceImplementation> resources; 386 final List <? extends PathResourceImplementation> binaryResources; 387 final List <? extends PathResourceImplementation> unknownResources; 388 final Set <ClassPath> newCps; 389 final Map <URL , SourceForBinaryQuery.Result> newSR; 390 final Map <URL , URL []> translatedRoots; 391 final Map <URL , WeakValue> unknownRoots; 392 393 public Result (final long timeStamp, final List <? extends PathResourceImplementation> resources, 394 final List <? extends PathResourceImplementation> binaryResources, 395 final List <? extends PathResourceImplementation> unknownResources, 396 final Set <ClassPath> newCps, 397 final Map <URL , SourceForBinaryQuery.Result> newSR, final Map <URL , URL []> translatedRoots, 398 final Map <URL , WeakValue> unknownRoots) { 399 assert resources != null; 400 assert binaryResources != null; 401 assert unknownResources != null; 402 assert newCps != null; 403 assert newSR != null; 404 assert translatedRoots != null; 405 this.timeStamp = timeStamp; 406 this.resources = resources; 407 this.binaryResources = binaryResources; 408 this.unknownResources = unknownResources; 409 this.newCps = newCps; 410 this.newSR = newSR; 411 this.translatedRoots = translatedRoots; 412 this.unknownRoots = unknownRoots; 413 } 414 } 415 416 private class SourcePathImplementation implements ClassPathImplementation { 417 418 private List <PropertyChangeListener > listeners = new ArrayList <PropertyChangeListener >(); 419 420 421 public List <? extends PathResourceImplementation> getResources() { 422 Request request; 423 synchronized (GlobalSourcePath.this) { 424 if (GlobalSourcePath.this.resources != null) { 425 return GlobalSourcePath.this.resources; 426 } 427 request = new Request ( 428 GlobalSourcePath.this.getTimeStamp(), 429 GlobalSourcePath.this.gpr.getPaths(ClassPath.SOURCE), 430 GlobalSourcePath.this.gpr.getPaths(ClassPath.BOOT), 431 GlobalSourcePath.this.gpr.getPaths(ClassPath.COMPILE), 432 GlobalSourcePath.this.activeCps, 433 GlobalSourcePath.this.sourceResults, 434 new HashMap <URL ,WeakValue> (GlobalSourcePath.this.unknownRoots), 435 GlobalSourcePath.this.listener, 436 GlobalSourcePath.this.listener); 437 } 438 Result res = createResources (request); 439 if (GlobalSourcePath.this.debugCallBack != null) { 440 GlobalSourcePath.this.debugCallBack.run(); 441 } 442 synchronized (this) { 443 if (GlobalSourcePath.this.getTimeStamp() == res.timeStamp) { 444 if (GlobalSourcePath.this.resources == null) { 445 GlobalSourcePath.this.resources = res.resources; 446 GlobalSourcePath.this.binaryResources = res.binaryResources; 447 GlobalSourcePath.this.unknownResources = res.unknownResources; 448 GlobalSourcePath.this.activeCps = res.newCps; 449 GlobalSourcePath.this.sourceResults = res.newSR; 450 GlobalSourcePath.this.translatedRoots = res.translatedRoots; 451 GlobalSourcePath.this.unknownRoots = res.unknownRoots; 452 } 453 return GlobalSourcePath.this.resources; 454 } 455 else { 456 return res.resources; 457 } 458 } 459 } 460 461 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { 462 assert listener != null; 463 if (this.listeners == null) { 464 this.listeners = new ArrayList <PropertyChangeListener > (); 465 } 466 this.listeners.add (listener); 467 } 468 469 public void removePropertyChangeListener(PropertyChangeListener listener) { 470 assert listener != null; 471 if (this.listeners == null) { 472 return; 473 } 474 this.listeners.remove(listener); 475 } 476 477 void firePropertyChange () { 478 PropertyChangeListener [] _listeners; 479 synchronized (this) { 480 if (this.listeners == null) { 481 return; 482 } 483 _listeners = this.listeners.toArray(new PropertyChangeListener [this.listeners.size()]); 484 } 485 PropertyChangeEvent event = new PropertyChangeEvent (this,PROP_RESOURCES,null,null); 486 for (PropertyChangeListener l : _listeners) { 487 l.propertyChange (event); 488 } 489 } 490 } 491 492 private class UnknownSourcePathImplementation implements ClassPathImplementation { 493 494 private List <PropertyChangeListener > listeners = new CopyOnWriteArrayList <PropertyChangeListener > (); 495 496 public List <? extends PathResourceImplementation> getResources() { 497 Request request; 498 synchronized (GlobalSourcePath.this) { 499 if (GlobalSourcePath.this.unknownResources != null) { 500 return GlobalSourcePath.this.unknownResources; 501 } 502 request = new Request ( 503 GlobalSourcePath.this.getTimeStamp(), 504 GlobalSourcePath.this.gpr.getPaths(ClassPath.SOURCE), 505 GlobalSourcePath.this.gpr.getPaths(ClassPath.BOOT), 506 GlobalSourcePath.this.gpr.getPaths(ClassPath.COMPILE), 507 GlobalSourcePath.this.activeCps, 508 GlobalSourcePath.this.sourceResults, 509 new HashMap <URL , WeakValue> (GlobalSourcePath.this.unknownRoots), 510 GlobalSourcePath.this.listener, 511 GlobalSourcePath.this.listener); 512 } 513 Result res = createResources (request); 514 if (GlobalSourcePath.this.debugCallBack != null) { 515 GlobalSourcePath.this.debugCallBack.run(); 516 } 517 synchronized (this) { 518 if (GlobalSourcePath.this.getTimeStamp() == res.timeStamp) { 519 if (GlobalSourcePath.this.binaryResources == null) { 520 GlobalSourcePath.this.resources = res.resources; 521 GlobalSourcePath.this.binaryResources = res.binaryResources; 522 GlobalSourcePath.this.unknownResources = res.unknownResources; 523 GlobalSourcePath.this.activeCps = res.newCps; 524 GlobalSourcePath.this.sourceResults = res.newSR; 525 GlobalSourcePath.this.translatedRoots = res.translatedRoots; 526 GlobalSourcePath.this.unknownRoots = res.unknownRoots; 527 } 528 return GlobalSourcePath.this.unknownResources; 529 } 530 else { 531 return res.unknownResources; 532 } 533 } 534 } 535 536 public void addPropertyChangeListener(PropertyChangeListener listener) { 537 assert listener != null; 538 this.listeners.add (listener); 539 } 540 541 public void removePropertyChangeListener(PropertyChangeListener listener) { 542 assert listener != null; 543 this.listeners.remove (listener); 544 } 545 546 547 void firePropertyChange () { 548 PropertyChangeEvent event = new PropertyChangeEvent (this,PROP_RESOURCES,null,null); 549 for (PropertyChangeListener l : this.listeners) { 550 l.propertyChange (event); 551 } 552 } 553 } 554 555 private class BinaryPathImplementation implements ClassPathImplementation { 556 private List <PropertyChangeListener > listeners = new ArrayList <PropertyChangeListener >(); 557 558 public List <? extends PathResourceImplementation> getResources() { 559 Request request; 560 synchronized (GlobalSourcePath.this) { 561 if (GlobalSourcePath.this.binaryResources != null) { 562 return GlobalSourcePath.this.binaryResources; 563 } 564 request = new Request ( 565 GlobalSourcePath.this.getTimeStamp(), 566 GlobalSourcePath.this.gpr.getPaths(ClassPath.SOURCE), 567 GlobalSourcePath.this.gpr.getPaths(ClassPath.BOOT), 568 GlobalSourcePath.this.gpr.getPaths(ClassPath.COMPILE), 569 GlobalSourcePath.this.activeCps, 570 GlobalSourcePath.this.sourceResults, 571 new HashMap <URL , WeakValue> (GlobalSourcePath.this.unknownRoots), 572 GlobalSourcePath.this.listener, 573 GlobalSourcePath.this.listener); 574 } 575 Result res = createResources (request); 576 if (GlobalSourcePath.this.debugCallBack != null) { 577 GlobalSourcePath.this.debugCallBack.run(); 578 } 579 synchronized (this) { 580 if (GlobalSourcePath.this.getTimeStamp() == res.timeStamp) { 581 if (GlobalSourcePath.this.binaryResources == null) { 582 GlobalSourcePath.this.resources = res.resources; 583 GlobalSourcePath.this.binaryResources = res.binaryResources; 584 GlobalSourcePath.this.unknownResources = res.unknownResources; 585 GlobalSourcePath.this.activeCps = res.newCps; 586 GlobalSourcePath.this.sourceResults = res.newSR; 587 GlobalSourcePath.this.translatedRoots = res.translatedRoots; 588 GlobalSourcePath.this.unknownRoots = res.unknownRoots; 589 } 590 return GlobalSourcePath.this.binaryResources; 591 } 592 else { 593 return res.binaryResources; 594 } 595 } 596 } 597 598 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { 599 assert listener != null; 600 if (this.listeners == null) { 601 this.listeners = new ArrayList <PropertyChangeListener > (); 602 } 603 this.listeners.add (listener); 604 } 605 606 public void removePropertyChangeListener(PropertyChangeListener listener) { 607 assert listener != null; 608 if (this.listeners == null) { 609 return; 610 } 611 this.listeners.remove(listener); 612 } 613 614 void firePropertyChange () { 615 PropertyChangeListener [] _listeners; 616 synchronized (this) { 617 if (this.listeners == null) { 618 return; 619 } 620 _listeners = this.listeners.toArray(new PropertyChangeListener [this.listeners.size()]); 621 } 622 PropertyChangeEvent event = new PropertyChangeEvent (this,PROP_RESOURCES,null,null); 623 for (PropertyChangeListener l : _listeners) { 624 l.propertyChange (event); 625 } 626 } 627 } 628 629 630 private class Listener implements GlobalPathRegistryListener, PropertyChangeListener , ChangeListener { 631 632 public void pathsAdded(GlobalPathRegistryEvent event) { 633 resetCacheAndFire (); 634 } 635 636 public void pathsRemoved(GlobalPathRegistryEvent event) { 637 resetCacheAndFire (); 638 } 639 640 public void propertyChange(PropertyChangeEvent evt) { 641 if (ClassPath.PROP_ENTRIES.equals(evt.getPropertyName())) { 642 resetCacheAndFire (); 643 } 644 } 645 646 public void stateChanged (ChangeEvent event) { 647 resetCacheAndFire(); 648 } 649 } 650 651 public static synchronized GlobalSourcePath getDefault () { 652 if (instance == null) { 653 instance = new GlobalSourcePath (); 654 } 655 return instance; 656 } 657 } 658 | Popular Tags |