1 19 20 package org.openide.util.lookup; 21 22 import java.lang.ref.Reference ; 23 import java.lang.ref.WeakReference ; 24 import java.util.ArrayList ; 25 import java.util.Arrays ; 26 import java.util.Collection ; 27 import java.util.Collections ; 28 import java.util.HashMap ; 29 import java.util.HashSet ; 30 import java.util.Iterator ; 31 import java.util.List ; 32 import java.util.Set ; 33 import javax.swing.event.EventListenerList ; 34 import org.openide.util.Lookup; 35 import org.openide.util.LookupEvent; 36 import org.openide.util.LookupListener; 37 38 43 public class ProxyLookup extends Lookup { 44 45 private static final Lookup[] EMPTY_ARR = new Lookup[0]; 46 47 48 private Object lookups; 49 50 51 private HashMap <Template<?>,Reference <R>> results; 52 53 56 public ProxyLookup(Lookup... lookups) { 57 this.setLookupsNoFire(lookups); 58 } 59 60 65 protected ProxyLookup() { 66 this(EMPTY_ARR); 67 } 68 69 public String toString() { 70 return "ProxyLookup(class=" + getClass() + ")->" + Arrays.asList(getLookups(false)); } 72 73 77 protected final Lookup[] getLookups() { 78 return getLookups(true); 79 } 80 81 84 private final Lookup[] getLookups(boolean clone) { 85 Object l = this.lookups; 86 if (l instanceof Lookup) { 87 return new Lookup[] { (Lookup)l }; 88 } else { 89 Lookup[] arr = (Lookup[])l; 90 if (clone) { 91 arr = arr.clone(); 92 } 93 return arr; 94 } 95 } 96 97 100 private void setLookupsNoFire(Lookup[] lookups) { 101 if (lookups.length == 1) { 102 this.lookups = lookups[0]; 103 assert this.lookups != null : "Cannot assign null delegate"; 104 } else { 105 if (lookups.length == 0) { 106 this.lookups = EMPTY_ARR; 107 } else { 108 this.lookups = lookups.clone(); 109 } 110 } 111 } 112 113 119 protected final void setLookups(Lookup... lookups) { 120 Collection <Reference <R>> arr; 121 HashSet <Lookup> newL; 122 HashSet <Lookup> current; 123 Lookup[] old; 124 125 synchronized (this) { 126 old = getLookups(false); 127 current = new HashSet <Lookup>(Arrays.asList(old)); 128 newL = new HashSet <Lookup>(Arrays.asList(lookups)); 129 130 setLookupsNoFire(lookups); 131 132 if ((results == null) || results.isEmpty()) { 133 return; 135 } 136 137 arr = new ArrayList <Reference <R>>(results.values()); 138 139 HashSet <Lookup> removed = new HashSet <Lookup>(current); 140 removed.removeAll(newL); newL.removeAll(current); 143 if (removed.isEmpty() && newL.isEmpty()) { 144 return; 146 } 147 148 for (Reference <R> ref : arr) { 149 R r = ref.get(); 150 if (r != null) { 151 r.lookupChange(newL, removed, old, lookups); 152 } 153 } 154 } 155 156 ArrayList <Object > evAndListeners = new ArrayList <Object >(); 158 for (Reference <R> ref : arr) { 159 R<?> r = ref.get(); 160 if (r != null) { 161 r.collectFires(evAndListeners); 162 } 163 } 164 165 { 166 Iterator it = evAndListeners.iterator(); 167 while (it.hasNext()) { 168 LookupEvent ev = (LookupEvent)it.next(); 169 LookupListener l = (LookupListener)it.next(); 170 l.resultChanged(ev); 171 } 172 } 173 } 174 175 183 protected void beforeLookup(Template<?> template) { 184 } 185 186 public final <T> T lookup(Class <T> clazz) { 187 beforeLookup(new Template<T>(clazz)); 188 189 Lookup[] lookups = this.getLookups(false); 190 191 for (int i = 0; i < lookups.length; i++) { 192 T o = lookups[i].lookup(clazz); 193 194 if (o != null) { 195 return o; 196 } 197 } 198 199 return null; 200 } 201 202 public final <T> Item<T> lookupItem(Template<T> template) { 203 beforeLookup(template); 204 205 Lookup[] lookups = this.getLookups(false); 206 207 for (int i = 0; i < lookups.length; i++) { 208 Item<T> o = lookups[i].lookupItem(template); 209 210 if (o != null) { 211 return o; 212 } 213 } 214 215 return null; 216 } 217 218 @SuppressWarnings ("unchecked") 219 private static <T> R<T> convertResult(R r) { 220 return (R<T>)r; 221 } 222 223 public final synchronized <T> Result<T> lookup(Lookup.Template<T> template) { 224 if (results != null) { 225 Reference <R> ref = results.get(template); 226 R r = (ref == null) ? null : ref.get(); 227 228 if (r != null) { 229 return convertResult(r); 230 } 231 } else { 232 results = new HashMap <Template<?>,Reference <R>>(); 233 } 234 235 R<T> newR = new R<T>(template); 236 results.put(template, new java.lang.ref.SoftReference <R>(newR)); 237 238 return newR; 239 } 240 241 243 private final synchronized void unregisterTemplate(Template<?> template) { 244 if (results == null) { 245 return; 246 } 247 248 Reference <R> ref = results.remove(template); 249 250 if ((ref != null) && (ref.get() != null)) { 251 results.put(template, ref); 254 } 255 } 256 257 261 private final class R<T> extends WaitableResult<T> { 262 263 private javax.swing.event.EventListenerList listeners; 264 265 266 private Lookup.Template<T> template; 267 268 269 private Collection [] cache; 270 271 272 private WeakResult<T> weakL; 273 274 276 public R(Lookup.Template<T> t) { 277 template = t; 278 weakL = new WeakResult<T>(this); 279 } 280 281 283 protected void finalize() { 284 unregisterTemplate(template); 285 } 286 287 @SuppressWarnings ("unchecked") 288 private Result<T>[] newResults(int len) { 289 return new Result[len]; 290 } 291 292 294 private Result<T>[] initResults() { 295 synchronized (this) { 296 if (weakL.results != null) { 297 return weakL.results; 298 } 299 } 300 301 Lookup[] myLkps = getLookups(false); 302 Result<T>[] arr = newResults(myLkps.length); 303 304 for (int i = 0; i < arr.length; i++) { 305 arr[i] = myLkps[i].lookup(template); 306 } 307 308 synchronized (this) { 309 if (weakL.results != null) { 312 return weakL.results; 313 } 314 315 for (int i = 0; i < arr.length; i++) { 316 arr[i].addLookupListener(weakL); 317 } 318 319 weakL.results = arr; 320 321 return arr; 322 } 323 } 324 325 330 protected void lookupChange(Set added, Set removed, Lookup[] old, Lookup[] current) { 331 synchronized (this) { 332 if (weakL.results == null) { 333 return; 335 } 336 337 HashMap <Lookup,Result<T>> map = new HashMap <Lookup,Result<T>>(old.length * 2); 339 340 for (int i = 0; i < old.length; i++) { 341 if (removed.contains(old[i])) { 342 weakL.results[i].removeLookupListener(weakL); 344 } else { 345 map.put(old[i], weakL.results[i]); 347 } 348 } 349 350 Lookup.Result<T>[] arr = newResults(current.length); 351 352 for (int i = 0; i < current.length; i++) { 353 if (added.contains(current[i])) { 354 arr[i] = current[i].lookup(template); 356 arr[i].addLookupListener(weakL); 357 } else { 358 arr[i] = map.get(current[i]); 360 361 if (arr[i] == null) { 362 throw new IllegalStateException (); 364 } 365 } 366 } 367 368 weakL.results = arr; 370 } 371 } 372 373 375 public void addLookupListener(LookupListener l) { 376 if (listeners == null) { 377 synchronized (this) { 378 if (listeners == null) { 379 listeners = new EventListenerList (); 380 } 381 } 382 } 383 384 listeners.add(LookupListener.class, l); 385 } 386 387 389 public void removeLookupListener(LookupListener l) { 390 if (listeners != null) { 391 listeners.remove(LookupListener.class, l); 392 } 393 } 394 395 398 @SuppressWarnings ("unchecked") 399 public java.util.Collection <T> allInstances() { 400 return computeResult(0); 401 } 402 403 407 @SuppressWarnings ("unchecked") 408 public java.util.Set <Class <? extends T>> allClasses() { 409 return (java.util.Set <Class <? extends T>>) computeResult(1); 410 } 411 412 416 @SuppressWarnings ("unchecked") 417 public java.util.Collection <? extends Item<T>> allItems() { 418 return computeResult(2); 419 } 420 421 425 private java.util.Collection computeResult(int indexToCache) { 426 Lookup.Result<T>[] arr = myBeforeLookup(); 428 429 synchronized (this) { 431 if (cache != null) { 432 Collection result = cache[indexToCache]; 433 if (result != null) { 434 return result; 435 } 436 } 437 } 438 439 Collection <Object > compute; 441 Collection <Object > ret; 442 443 if (indexToCache == 1) { 444 HashSet <Object > s = new HashSet <Object >(); 445 compute = s; 446 ret = Collections.unmodifiableSet(s); 447 } else { 448 List <Object > l = new ArrayList <Object >(arr.length * 2); 449 compute = l; 450 ret = Collections.unmodifiableList(l); 451 } 452 453 for (int i = 0; i < arr.length; i++) { 455 switch (indexToCache) { 456 case 0: 457 compute.addAll(arr[i].allInstances()); 458 break; 459 case 1: 460 compute.addAll(arr[i].allClasses()); 461 break; 462 case 2: 463 compute.addAll(arr[i].allItems()); 464 break; 465 default: 466 assert false : "Wrong index: " + indexToCache; 467 } 468 } 469 470 471 472 synchronized (this) { 473 if (cache == null) { 474 cache = new Collection [3]; 476 } 477 478 if (arr == weakL.results) { 479 cache[indexToCache] = ret; 482 } 483 } 484 485 return ret; 486 } 487 488 490 public void resultChanged(LookupEvent ev) { 491 collectFires(null); 492 } 493 494 protected void collectFires(Collection <Object > evAndListeners) { 495 Collection oldItems; 497 Collection oldInstances; 498 synchronized (this) { 499 if (cache == null) { 500 return; 502 } 503 oldInstances = cache[0]; 504 oldItems = cache[2]; 505 506 507 if (listeners == null || listeners.getListenerCount() == 0) { 508 cache = new Collection [3]; 510 return; 511 } 512 513 cache = null; 516 } 517 518 boolean modified = true; 519 520 if (oldItems != null) { 521 Collection newItems = allItems(); 522 if (oldItems.equals(newItems)) { 523 modified = false; 524 } 525 } else { 526 if (oldInstances != null) { 527 Collection newInstances = allInstances(); 528 if (oldInstances.equals(newInstances)) { 529 modified = false; 530 } 531 } else { 532 synchronized (this) { 533 if (cache == null) { 534 cache = new Collection [3]; 537 } 538 } 539 } 540 } 541 542 assert cache != null; 543 544 if (modified) { 545 LookupEvent ev = new LookupEvent(this); 546 AbstractLookup.notifyListeners(listeners.getListenerList(), ev, evAndListeners); 547 } 548 } 549 550 553 private Lookup.Result<T>[] myBeforeLookup() { 554 ProxyLookup.this.beforeLookup(template); 555 556 Lookup.Result<T>[] arr = initResults(); 557 558 for (int i = 0; i < arr.length; i++) { 560 if (arr[i] instanceof WaitableResult) { 561 WaitableResult w = (WaitableResult) arr[i]; 562 w.beforeLookup(template); 563 } 564 } 565 566 return arr; 567 } 568 569 571 protected void beforeLookup(Lookup.Template t) { 572 if (t.getType() == template.getType()) { 573 myBeforeLookup(); 574 } 575 } 576 } 577 private static final class WeakResult<T> extends WaitableResult<T> implements LookupListener { 578 579 private Lookup.Result<T>[] results; 580 581 private Reference <R> result; 582 583 public WeakResult(R r) { 584 this.result = new WeakReference <R>(r); 585 } 586 587 protected void beforeLookup(Lookup.Template t) { 588 R r = result.get(); 589 if (r != null) { 590 r.beforeLookup(t); 591 } else { 592 removeListeners(); 593 } 594 } 595 596 private void removeListeners() { 597 Lookup.Result<T>[] arr = this.results; 598 if (arr == null) { 599 return; 600 } 601 602 for(int i = 0; i < arr.length; i++) { 603 arr[i].removeLookupListener(this); 604 } 605 } 606 607 protected void collectFires(Collection <Object > evAndListeners) { 608 R<?> r = result.get(); 609 if (r != null) { 610 r.collectFires(evAndListeners); 611 } else { 612 removeListeners(); 613 } 614 } 615 616 public void addLookupListener(LookupListener l) { 617 assert false; 618 } 619 620 public void removeLookupListener(LookupListener l) { 621 assert false; 622 } 623 624 public Collection <T> allInstances() { 625 assert false; 626 return null; 627 } 628 629 public void resultChanged(LookupEvent ev) { 630 R r = result.get(); 631 if (r != null) { 632 r.resultChanged(ev); 633 } else { 634 removeListeners(); 635 } 636 } 637 638 public Collection <? extends Item<T>> allItems() { 639 assert false; 640 return null; 641 } 642 643 public Set <Class <? extends T>> allClasses() { 644 assert false; 645 return null; 646 } 647 } } 649 | Popular Tags |