1 19 package org.openide.nodes; 20 21 import java.lang.ref.Reference ; 22 import java.lang.ref.WeakReference ; 23 24 import java.util.*; 25 26 import javax.swing.event.ChangeEvent ; 27 import javax.swing.event.ChangeListener ; 28 import javax.swing.event.EventListenerList ; 29 import org.openide.util.Lookup; 30 import org.openide.util.lookup.AbstractLookup; 31 import org.openide.util.lookup.InstanceContent; 32 33 34 41 public final class CookieSet extends Object implements Lookup.Provider { 42 43 private static ThreadLocal <Object > QUERY_MODE = new ThreadLocal <Object >(); 44 45 46 private HashMap<Class , R> map = new HashMap<Class ,R>(31); 47 48 49 private EventListenerList listeners = new EventListenerList (); 50 51 52 private final CookieSetLkp ic; 53 54 private Lookup lookup; 55 56 57 public CookieSet() { 58 this(null, null); 59 } 60 61 private CookieSet(CookieSetLkp ic, Lookup lookup) { 62 this.ic = ic; 63 this.lookup = lookup; 64 } 65 66 79 public static CookieSet createGeneric(Before before) { 80 CookieSetLkp al = new CookieSetLkp(before); 81 return new CookieSet(al, al); 82 } 83 84 91 public Lookup getLookup() { 92 synchronized (QUERY_MODE) { 93 if (lookup == null) { 94 AbstractNode an = new AbstractNode(this); 95 lookup = an.getLookup(); 96 } 97 } 98 return lookup; 99 } 100 101 102 111 public void add(Node.Cookie cookie) { 112 addImpl((Object )cookie); 113 fireChangeEvent(); 114 } 115 116 private void addImpl(Object cookie) { 117 synchronized (this) { 118 registerCookie(cookie.getClass(), cookie); 119 } 120 if (ic != null) { 121 ic.add(cookie); 122 } 123 } 124 125 128 public void remove(Node.Cookie cookie) { 129 removeImpl((Object )cookie); 130 fireChangeEvent(); 131 } 132 133 void removeImpl(Object cookie) { 134 synchronized (this) { 135 unregisterCookie(cookie.getClass(), cookie); 136 } 137 if (ic != null) { 138 ic.remove(cookie); 139 } 140 } 141 142 144 145 146 151 public <T extends Node.Cookie> T getCookie(Class <T> clazz) { 152 if (ic != null) { 153 ic.beforeLookupImpl(clazz); 154 } 155 156 Node.Cookie ret = null; 157 Object queryMode = QUERY_MODE.get(); 158 159 synchronized (this) { 160 R r = findR(clazz); 161 162 if (r == null) { 163 if (queryMode == null || ic == null) { 164 return null; 165 } 166 } else { 167 ret = r.cookie(); 168 169 if (queryMode instanceof Set) { 170 @SuppressWarnings ("unchecked") 171 Set<Class > keys = (Set<Class >)queryMode; 172 keys.addAll(map.keySet()); 173 } 174 } 175 } 176 177 if (ret instanceof CookieEntry) { 178 if (clazz == queryMode) { 179 QUERY_MODE.set(ret); 182 ret = null; 183 } else { 184 ret = ((CookieEntry) ret).getCookie(true); 186 } 187 } else if (ret == null) { 188 if (ic != null && 189 (!Node.Cookie.class.isAssignableFrom(clazz) || clazz == Node.Cookie.class) 190 ) { 191 enhancedQueryMode(lookup, clazz); 192 ret = null; 193 } 194 } 195 196 return clazz.cast(ret); 197 } 198 199 static void enhancedQueryMode(Lookup lookup, Class <?> clazz) { 200 Object type = QUERY_MODE.get(); 201 if (type != clazz) { 202 return; 203 } 204 Collection<? extends Lookup.Item<?>> items = lookup.lookupResult(clazz).allItems(); 205 if (items.size() == 0) { 206 return; 207 } 208 AbstractLookup.Pair[] arr = new AbstractLookup.Pair[items.size()]; 209 Iterator<? extends Lookup.Item> it = items.iterator(); 210 for (int i = 0; i < arr.length; i++) { 211 arr[i] = new PairWrap(it.next()); 212 } 213 QUERY_MODE.set(arr); 214 } 215 216 219 public void addChangeListener(ChangeListener l) { 220 listeners.add(ChangeListener .class, l); 221 } 222 223 226 public void removeChangeListener(ChangeListener l) { 227 listeners.remove(ChangeListener .class, l); 228 } 229 230 231 233 static Object entryQueryMode(Class c) { 234 Object prev = QUERY_MODE.get(); 235 QUERY_MODE.set(c); 236 237 return prev; 238 } 239 240 242 static Object entryAllClassesMode() { 243 Object prev = QUERY_MODE.get(); 244 QUERY_MODE.set(new HashSet()); 245 246 return prev; 247 } 248 249 251 static Collection<AbstractLookup.Pair> exitQueryMode(Object prev) { 252 Object cookie = QUERY_MODE.get(); 253 QUERY_MODE.set(prev); 254 255 if (cookie instanceof CookieSet.CookieEntry) { 256 return Collections.singleton((AbstractLookup.Pair)new CookieEntryPair((CookieSet.CookieEntry) cookie)); 257 } else if (cookie instanceof AbstractLookup.Pair[]) { 258 return Arrays.asList((AbstractLookup.Pair[])cookie); 259 } else { 260 return null; 261 } 262 } 263 264 265 static Set exitAllClassesMode(Object prev) { 266 Object cookie = QUERY_MODE.get(); 267 QUERY_MODE.set(prev); 268 269 if (cookie instanceof HashSet) { 270 return (Set) cookie; 271 } 272 273 return null; 274 } 275 276 278 final void fireChangeEvent() { 279 Object [] arr = listeners.getListenerList(); 280 281 if (arr.length > 0) { 282 ChangeEvent ev = null; 283 284 for (int i = arr.length - 2; i >= 0; i -= 2) { 287 if (arr[i] == ChangeListener .class) { 288 if (ev == null) { 289 ev = new ChangeEvent (this); 290 } 291 292 ((ChangeListener ) arr[i + 1]).stateChanged(ev); 293 } 294 } 295 } 296 } 297 298 304 private void registerCookie(Class <?> c, Object cookie) { 305 if ((c == null) || !Node.Cookie.class.isAssignableFrom(c)) { 306 return; 307 } 308 Class <? extends Node.Cookie> nc = c.asSubclass(Node.Cookie.class); 309 310 R r = findR(nc); 311 312 if (r == null) { 313 r = new R(); 314 map.put(c, r); 315 } 316 317 r.add((Node.Cookie)cookie); 318 319 registerCookie(c.getSuperclass(), cookie); 320 321 Class [] inter = c.getInterfaces(); 322 323 for (int i = 0; i < inter.length; i++) { 324 registerCookie(inter[i], cookie); 325 } 326 } 327 328 334 private void unregisterCookie(Class <?> c, Object cookie) { 335 if ((c == null) || !Node.Cookie.class.isAssignableFrom(c)) { 336 return; 337 } 338 Class <? extends Node.Cookie> nc = c.asSubclass(Node.Cookie.class); 339 340 341 R r = findR(nc); 343 344 if (r != null) { 345 r.remove((Node.Cookie)cookie); 347 } 348 349 unregisterCookie(c.getSuperclass(), cookie); 350 351 Class [] inter = c.getInterfaces(); 352 353 for (int i = 0; i < inter.length; i++) { 354 unregisterCookie(inter[i], cookie); 355 } 356 } 357 358 359 public void add(Class <? extends Node.Cookie> cookieClass, Factory factory) { 360 if (factory == null) { 361 throw new IllegalArgumentException (); 362 } 363 364 synchronized (this) { 365 registerCookie(cookieClass, new CookieEntry(factory, cookieClass)); 366 } 367 if (ic != null) { 368 ic.add(new FactAndClass(cookieClass, factory), C.INSTANCE); 369 } 370 fireChangeEvent(); 371 } 372 373 374 public void add(Class <? extends Node.Cookie>[] cookieClass, Factory factory) { 375 if (factory == null) { 376 throw new IllegalArgumentException (); 377 } 378 379 synchronized (this) { 380 for (int i = 0; i < cookieClass.length; i++) { 381 registerCookie(cookieClass[i], new CookieEntry(factory, cookieClass[i])); 382 } 383 } 384 385 if (ic != null) { 386 for (Class <? extends Node.Cookie> c : cookieClass) { 387 ic.add(new FactAndClass(c, factory), C.INSTANCE); 388 } 389 } 390 fireChangeEvent(); 391 } 392 393 397 public void remove(Class <? extends Node.Cookie> cookieClass, Factory factory) { 398 if (factory == null) { 399 throw new IllegalArgumentException (); 400 } 401 402 synchronized (this) { 403 R r = findR(cookieClass); 404 405 if (r != null) { 406 Node.Cookie c = r.cookie(); 407 408 if (c instanceof CookieEntry) { 409 CookieEntry ce = (CookieEntry) c; 410 411 if (ce.factory == factory) { 412 unregisterCookie(cookieClass, c); 413 } 414 } 415 } 416 } 417 if (ic != null) { 418 ic.remove(new FactAndClass(cookieClass, factory), C.INSTANCE); 419 } 420 421 fireChangeEvent(); 422 } 423 424 428 public void remove(Class <? extends Node.Cookie>[] cookieClass, Factory factory) { 429 if (factory == null) { 430 throw new IllegalArgumentException (); 431 } 432 433 synchronized (this) { 434 for (int i = 0; i < cookieClass.length; i++) { 435 R r = findR(cookieClass[i]); 436 437 if (r != null) { 438 Node.Cookie c = r.cookie(); 439 440 if (c instanceof CookieEntry) { 441 CookieEntry ce = (CookieEntry) c; 442 443 if (ce.factory == factory) { 444 unregisterCookie(cookieClass[i], c); 445 } 446 } 447 } 448 } 449 } 450 451 if (ic != null) { 452 for (Class <? extends Node.Cookie> c : cookieClass) { 453 ic.remove(new FactAndClass(c, factory), C.INSTANCE); 454 } 455 } 456 457 fireChangeEvent(); 458 } 459 460 468 public <T> void assign(Class <? extends T> clazz, T... instances) { 469 if (Node.Cookie.class.isAssignableFrom(clazz)) { 470 Class <? extends Node.Cookie> cookieClazz = clazz.asSubclass(Node.Cookie.class); 471 for(;;) { 472 Node.Cookie cookie = getCookie(cookieClazz); 473 if (cookie != null) { 474 removeImpl(cookie); 475 } else { 476 break; 477 } 478 } 479 for (T t : instances) { 480 addImpl(t); 481 } 482 483 fireChangeEvent(); 484 } else if (ic != null) { 485 synchronized (this) { 486 for (T t : instances) { 487 registerCookie(t.getClass(), t); 488 } 489 } 490 ic.replaceInstances(clazz, instances, this); 491 } 492 } 493 494 512 513 515 private R findR(Class <? extends Node.Cookie> c) { 516 return map.get(c); 517 } 518 519 523 private static Class <? extends Node.Cookie> baseForCookie(Node.Cookie c) { 524 if (c instanceof CookieEntry) { 525 return ((CookieEntry) c).klass; 526 } 527 528 return c.getClass(); 529 } 530 531 532 public interface Factory { 533 536 <T extends Node.Cookie> T createCookie(Class <T> klass); 537 } 538 539 542 public interface Before { 543 public void beforeLookup(Class <?> clazz); 544 } 545 546 547 private static class CookieEntry implements Node.Cookie { 548 549 final Factory factory; 550 551 552 private final Class <? extends Node.Cookie> klass; 553 554 private Reference <Node.Cookie> cookie; 555 556 557 public CookieEntry(Factory factory, Class <? extends Node.Cookie> klass) { 558 this.factory = factory; 559 this.klass = klass; 560 } 561 562 566 public synchronized Node.Cookie getCookie(boolean create) { 567 Node.Cookie ret; 568 569 if (create) { 570 if ((cookie == null) || ((ret = cookie.get()) == null)) { 571 ret = factory.createCookie(klass); 572 573 if (ret == null) { 574 return null; 575 } 576 577 cookie = new WeakReference <Node.Cookie>(ret); 578 } 579 } else { 580 ret = (cookie == null) ? null : cookie.get(); 581 } 582 583 return ret; 584 } 585 } 587 589 private static final class R extends Object { 590 591 public List <Node.Cookie> cookies; 592 593 594 public Class base; 595 596 R() { 597 } 598 599 602 public void add(Node.Cookie cookie) { 603 if (cookies == null) { 604 cookies = new ArrayList<Node.Cookie>(1); 605 cookies.add(cookie); 606 base = baseForCookie(cookie); 607 608 return; 609 } 610 611 Class <?> newBase = baseForCookie(cookie); 612 613 if ((base == null) || newBase.isAssignableFrom(base)) { 614 cookies.set(0, cookie); 615 base = newBase; 616 } else { 617 cookies.add(cookie); 618 } 619 } 620 621 624 public boolean remove(Node.Cookie cookie) { 625 if (cookies == null) { 626 return true; 627 } 628 629 if (cookies.remove(cookie) && (cookies.size() == 0)) { 630 base = null; 631 cookies = null; 632 633 return true; 634 } 635 636 base = baseForCookie(cookies.get(0)); 637 638 return false; 639 } 640 641 643 public Node.Cookie cookie() { 644 return ((cookies == null) || cookies.isEmpty()) ? null : cookies.get(0); 645 } 646 } 647 648 650 private static final class PairWrap extends AbstractLookup.Pair { 651 private Lookup.Item<?> item; 652 private boolean created; 653 654 public PairWrap(Lookup.Item<?> item) { 655 this.item = item; 656 } 657 658 protected boolean instanceOf(Class c) { 659 Class <?> k = c; 660 return k.isAssignableFrom(getType()); 661 } 662 663 protected boolean creatorOf(Object obj) { 664 return created && getInstance() == obj; 665 } 666 667 public Object getInstance() { 668 created = true; 669 return item.getInstance(); 670 } 671 672 public Class <? extends Object > getType() { 673 return item.getType(); 674 } 675 676 public String getId() { 677 return item.getId(); 678 } 679 680 public String getDisplayName() { 681 return item.getDisplayName(); 682 } 683 684 public int hashCode() { 685 return 777 + item.hashCode(); 686 } 687 688 public boolean equals(Object object) { 689 if (object instanceof PairWrap) { 690 PairWrap p = (PairWrap)object; 691 return item.equals(p.item); 692 } 693 return false; 694 } 695 } 697 699 private static final class CookieEntryPair extends AbstractLookup.Pair { 700 private CookieEntry entry; 701 702 public CookieEntryPair(CookieEntry e) { 703 this.entry = e; 704 } 705 706 protected boolean creatorOf(Object obj) { 707 return obj == entry.getCookie(false); 708 } 709 710 public String getDisplayName() { 711 return getId(); 712 } 713 714 public String getId() { 715 return entry.klass.getName(); 716 } 717 718 public Object getInstance() { 719 return entry.getCookie(true); 720 } 721 722 public Class getType() { 723 return entry.klass; 724 } 725 726 protected boolean instanceOf(Class c) { 727 Class <?> k = c; 728 return k.isAssignableFrom(entry.klass); 729 } 730 731 public int hashCode() { 732 return entry.hashCode() + 5; 733 } 734 735 public boolean equals(Object obj) { 736 if (obj instanceof CookieEntryPair) { 737 return ((CookieEntryPair) obj).entry == entry; 738 } 739 740 return false; 741 } 742 } 744 private static final class FactAndClass { 745 final Class <? extends Node.Cookie> clazz; 746 final Factory factory; 747 748 public FactAndClass(Class <? extends Node.Cookie> clazz, Factory factory) { 749 this.clazz = clazz; 750 this.factory = factory; 751 } 752 753 public int hashCode() { 754 return clazz.hashCode() + factory.hashCode(); 755 } 756 757 public boolean equals(Object o) { 758 if (o instanceof FactAndClass) { 759 FactAndClass f = (FactAndClass)o; 760 return f.clazz.equals(clazz) && f.factory == factory; 761 } 762 return false; 763 } 764 } 765 766 private static class C implements InstanceContent.Convertor<FactAndClass, Node.Cookie> { 767 static final C INSTANCE = new C(); 768 769 770 public Node.Cookie convert(CookieSet.FactAndClass obj) { 771 return obj.factory.createCookie(obj.clazz); 772 } 773 774 public Class <? extends Node.Cookie> type(CookieSet.FactAndClass obj) { 775 return obj.clazz; 776 } 777 778 public String id(CookieSet.FactAndClass obj) { 779 return obj.clazz.getName(); 780 } 781 782 public String displayName(CookieSet.FactAndClass obj) { 783 return obj.clazz.getName(); 784 } 785 } 786 } 787 | Popular Tags |