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.api.debugger;
043:
044: import java.beans.Customizer;
045: import java.beans.PropertyChangeEvent;
046: import java.beans.PropertyChangeListener;
047: import java.io.BufferedReader;
048: import java.io.IOException;
049: import java.io.InputStream;
050: import java.io.InputStreamReader;
051: import java.lang.reflect.Constructor;
052: import java.lang.reflect.InvocationTargetException;
053: import java.lang.reflect.Method;
054: import java.net.URL;
055: import java.util.ArrayList;
056: import java.util.Collection;
057: import java.util.Collections;
058: import java.util.Comparator;
059: import java.util.Enumeration;
060: import java.util.HashMap;
061: import java.util.HashSet;
062: import java.util.Iterator;
063: import java.util.LinkedHashMap;
064: import java.util.List;
065: import java.util.Map;
066: import java.util.Set;
067:
068: import java.util.logging.Level;
069: import java.util.logging.Logger;
070: import org.netbeans.spi.debugger.ContextProvider;
071: import org.openide.ErrorManager;
072: import org.openide.modules.ModuleInfo;
073: import org.openide.util.LookupEvent;
074: import org.openide.util.RequestProcessor;
075: import org.openide.util.WeakListeners;
076: import org.openide.util.WeakSet;
077:
078: /**
079: * Lookup implementation, which provides services in a list.
080: * The list can refresh itself when the services change.
081: * The refreshing is performed under a lock on the list object so that
082: * clients have consistent data under synchronization on the list instance.
083: *
084: * @author Jan Jancura, Martin Entlicher
085: */
086: abstract class Lookup implements ContextProvider {
087:
088: public static final String NOTIFY_LOAD_FIRST = "load first";
089: public static final String NOTIFY_LOAD_LAST = "load last";
090: public static final String NOTIFY_UNLOAD_FIRST = "unload first";
091: public static final String NOTIFY_UNLOAD_LAST = "unload last";
092:
093: public <T> T lookupFirst(String folder, Class<T> service) {
094: List<? extends T> l = lookup(folder, service);
095: synchronized (l) {
096: if (l.isEmpty())
097: return null;
098: return l.get(0);
099: }
100: }
101:
102: public abstract <T> List<? extends T> lookup(String folder,
103: Class<T> service);
104:
105: private static boolean verbose = System
106: .getProperty("netbeans.debugger.registration") != null;
107:
108: static class Instance extends Lookup {
109: private Object[] services;
110:
111: Instance(Object[] services) {
112: this .services = services;
113: }
114:
115: public <T> List<? extends T> lookup(String folder,
116: Class<T> service) {
117: List<T> l = new ArrayList<T>();
118: for (Object s : services) {
119: if (service.isInstance(s)) {
120: l.add(service.cast(s));
121: if (verbose)
122: System.out.println("\nR instance " + s
123: + " found");
124: }
125: }
126: return l;
127: }
128: }
129:
130: static class Compound extends Lookup {
131: private ContextProvider l1;
132: private ContextProvider l2;
133:
134: Compound(ContextProvider l1, ContextProvider l2) {
135: this .l1 = l1;
136: this .l2 = l2;
137: setContext(this );
138: }
139:
140: public <T> List<? extends T> lookup(String folder,
141: Class<T> service) {
142: return new CompoundLookupList<T>(folder, service);
143: }
144:
145: void setContext(Lookup context) {
146: if (l1 instanceof Compound)
147: ((Compound) l1).setContext(context);
148: if (l1 instanceof MetaInf)
149: ((MetaInf) l1).setContext(context);
150: if (l2 instanceof Compound)
151: ((Compound) l2).setContext(context);
152: if (l2 instanceof MetaInf)
153: ((MetaInf) l2).setContext(context);
154: }
155:
156: private class CompoundLookupList<T> extends LookupList<T>
157: implements Customizer, PropertyChangeListener {
158:
159: private String folder;
160: private Class<T> service;
161: private List<PropertyChangeListener> propertyChangeListeners;
162: private Customizer sublist1, sublist2;
163:
164: public CompoundLookupList(String folder, Class<T> service) {
165: super (null);
166: this .folder = folder;
167: this .service = service;
168: setUp();
169: }
170:
171: private synchronized void setUp() {
172: clear();
173: List<? extends T> list1 = l1.lookup(folder, service);
174: List<? extends T> list2 = l2.lookup(folder, service);
175: addAll(list1);
176: addAll(list2);
177: sublist1 = (list1 instanceof Customizer) ? (Customizer) list1
178: : null;
179: sublist2 = (list2 instanceof Customizer) ? (Customizer) list2
180: : null;
181: }
182:
183: public synchronized void setObject(Object bean) {
184: if (sublist1 != null)
185: sublist1.setObject(bean);
186: if (sublist2 != null)
187: sublist2.setObject(bean);
188: }
189:
190: public synchronized void addPropertyChangeListener(
191: PropertyChangeListener listener) {
192: if (propertyChangeListeners == null) {
193: propertyChangeListeners = new ArrayList<PropertyChangeListener>();
194: if (sublist1 != null)
195: sublist1.addPropertyChangeListener(this );
196: if (sublist2 != null)
197: sublist2.addPropertyChangeListener(this );
198: }
199: propertyChangeListeners.add(listener);
200: }
201:
202: public synchronized void removePropertyChangeListener(
203: PropertyChangeListener listener) {
204: propertyChangeListeners.remove(listener);
205: }
206:
207: public void propertyChange(PropertyChangeEvent e) {
208: setUp();
209: List<PropertyChangeListener> listeners;
210: synchronized (this ) {
211: if (propertyChangeListeners == null) {
212: return;
213: }
214: listeners = new ArrayList<PropertyChangeListener>(
215: propertyChangeListeners);
216: }
217: PropertyChangeEvent evt = new PropertyChangeEvent(this ,
218: "content", null, null);
219: for (PropertyChangeListener l : listeners) {
220: l.propertyChange(evt);
221: }
222: }
223: }
224: }
225:
226: static class MetaInf extends Lookup {
227:
228: private static final String HIDDEN = "-hidden"; // NOI18N
229:
230: private String rootFolder;
231: private Map<String, List<String>> registrationCache = new HashMap<String, List<String>>();
232: private HashMap<String, Object> instanceCache = new HashMap<String, Object>();
233: private Lookup context;
234: private org.openide.util.Lookup.Result<ModuleInfo> moduleLookupResult;
235: private ModuleChangeListener modulesChangeListener;
236: private Map<ClassLoader, ModuleChangeListener> moduleChangeListeners = new HashMap<ClassLoader, ModuleChangeListener>();
237: private Map<ModuleInfo, ModuleChangeListener> disabledModuleChangeListeners = new HashMap<ModuleInfo, ModuleChangeListener>();
238: private Set<MetaInfLookupList> lookupLists = new WeakSet<MetaInfLookupList>();
239:
240: MetaInf(String rootFolder) {
241: this .rootFolder = rootFolder;
242: moduleLookupResult = org.openide.util.Lookup.getDefault()
243: .lookupResult(ModuleInfo.class);
244: //System.err.println("\nModules = "+moduleLookupResult.allInstances().size()+"\n");
245: modulesChangeListener = new ModuleChangeListener(null);
246: moduleLookupResult.addLookupListener(WeakListeners.create(
247: org.openide.util.LookupListener.class,
248: modulesChangeListener, moduleLookupResult));
249: }
250:
251: void setContext(Lookup context) {
252: this .context = context;
253: }
254:
255: public <T> List<? extends T> lookup(String folder,
256: Class<T> service) {
257: MetaInfLookupList<T> mll = new MetaInfLookupList<T>(folder,
258: service);
259: synchronized (lookupLists) {
260: lookupLists.add(mll);
261: }
262: return mll;
263: }
264:
265: private List<String> list(String folder, Class<?> service) {
266: String name = service.getName();
267: String resourceName = "META-INF/debugger/"
268: + ((rootFolder == null) ? "" : rootFolder + "/")
269: + ((folder == null) ? "" : folder + "/") + name;
270: synchronized (registrationCache) {
271: List<String> l = registrationCache.get(resourceName);
272: if (l == null) {
273: l = loadMetaInf(resourceName);
274: registrationCache.put(resourceName, l);
275: }
276: return l;
277: }
278: }
279:
280: private static Set<String> getHiddenClassNames(List l) {
281: Set<String> s = null;
282: int i, k = l.size();
283: for (i = 0; i < k; i++) {
284: String className = (String) l.get(i);
285: if (className.endsWith(HIDDEN)) {
286: if (s == null) {
287: s = new HashSet<String>();
288: }
289: s.add(className.substring(0, className.length()
290: - HIDDEN.length()));
291: }
292: }
293: return s;
294: }
295:
296: /**
297: * Loads instances of given class from META-INF/debugger from given
298: * folder. Given context isused as the parameter to constructor.
299: */
300: private List<String> loadMetaInf(String resourceName) {
301: List<String> l = new ArrayList<String>();
302: try {
303: ClassLoader cl = org.openide.util.Lookup.getDefault()
304: .lookup(ClassLoader.class);
305: String v = "\nR lookup " + resourceName;
306: Enumeration<URL> e = cl.getResources(resourceName);
307: Set<URL> urls = new HashSet<URL>();
308: while (e.hasMoreElements()) {
309: URL url = e.nextElement();
310: // Ignore duplicated URLs, necessary because of tests
311: if (urls.contains(url))
312: continue;
313: urls.add(url);
314: InputStream is = url.openStream();
315: if (is == null)
316: continue;
317: BufferedReader br = new BufferedReader(
318: new InputStreamReader(is));
319: for (String s = br.readLine(); s != null; s = br
320: .readLine()) {
321: if (s.startsWith("#"))
322: continue;
323: if (s.length() == 0)
324: continue;
325: if (verbose)
326: v += "\nR service " + s + " found";
327:
328: l.add(s);
329: }
330: }
331: if (verbose)
332: System.out.println(v);
333: return l;
334: } catch (IOException e) {
335: e.printStackTrace();
336: }
337: throw new InternalError("Can not read from Meta-inf!");
338: }
339:
340: private Object createInstance(String service) {
341: try {
342: ClassLoader cl = org.openide.util.Lookup.getDefault()
343: .lookup(ClassLoader.class);
344: String method = null;
345: if (service.endsWith("()")) {
346: int lastdot = service.lastIndexOf('.');
347: if (lastdot < 0) {
348: ErrorManager.getDefault().log(
349: "Bad service - dot before method name is missing: "
350: + "'" + service + "'.");
351: return null;
352: }
353: method = service.substring(lastdot + 1,
354: service.length() - 2).trim();
355: service = service.substring(0, lastdot);
356: }
357: Class cls = cl.loadClass(service);
358:
359: Object o = null;
360: if (method != null) {
361: Method m = null;
362: if (context != null) {
363: try {
364: m = cls.getDeclaredMethod(method,
365: new Class[] { Lookup.class });
366: } catch (NoSuchMethodException nsmex) {
367: }
368: }
369: if (m == null) {
370: try {
371: m = cls.getDeclaredMethod(method,
372: new Class[] {});
373: } catch (NoSuchMethodException nsmex) {
374: }
375: }
376: if (m != null) {
377: o = m
378: .invoke(
379: null,
380: (m.getParameterTypes().length == 0) ? new Object[] {}
381: : new Object[] { context });
382: }
383: }
384: if (o == null && context != null) {
385: Constructor[] cs = cls.getConstructors();
386: int i, k = cs.length;
387: for (i = 0; i < k; i++) {
388: Constructor c = cs[i];
389: if (c.getParameterTypes().length != 1)
390: continue;
391: try {
392: o = c.newInstance(new Object[] { context });
393: } catch (IllegalAccessException e) {
394: if (verbose) {
395: System.out.println("\nservice: "
396: + service);
397: e.printStackTrace();
398: }
399: } catch (IllegalArgumentException e) {
400: if (verbose) {
401: System.out.println("\nservice: "
402: + service);
403: e.printStackTrace();
404: }
405: }
406: }
407: }
408: if (o == null)
409: o = cls.newInstance();
410: if (verbose)
411: System.out.println("\nR instance " + o
412: + " created");
413: return o;
414: } catch (ClassNotFoundException e) {
415: ErrorManager.getDefault().notify(
416: ErrorManager.getDefault().annotate(
417: e,
418: "The service " + service
419: + " is not found."));
420: } catch (InstantiationException e) {
421: ErrorManager.getDefault().notify(
422: ErrorManager.getDefault().annotate(
423: e,
424: "The service " + service
425: + " can not be instantiated."));
426: } catch (IllegalAccessException e) {
427: ErrorManager.getDefault().notify(
428: ErrorManager.getDefault().annotate(
429: e,
430: "The service " + service
431: + " can not be accessed."));
432: } catch (InvocationTargetException ex) {
433: ErrorManager.getDefault().notify(
434: ErrorManager.getDefault().annotate(
435: ex,
436: "The service " + service
437: + " can not be created."));
438: } catch (ExceptionInInitializerError ex) {
439: ErrorManager.getDefault().notify(
440: ErrorManager.getDefault().annotate(
441: ex,
442: "The service " + service
443: + " can not be initialized."));
444: }
445: return null;
446: }
447:
448: private void listenOn(ClassLoader cl) {
449: synchronized (moduleChangeListeners) {
450: if (!moduleChangeListeners.containsKey(cl)) {
451: for (ModuleInfo mi : moduleLookupResult
452: .allInstances()) {
453: if (mi.isEnabled() && mi.getClassLoader() == cl) {
454: ModuleChangeListener l = new ModuleChangeListener(
455: cl);
456: mi.addPropertyChangeListener(WeakListeners
457: .propertyChange(l, mi));
458: moduleChangeListeners.put(cl, l);
459: }
460: }
461: }
462: }
463: }
464:
465: private void listenOnDisabledModules() {
466: synchronized (moduleChangeListeners) {
467: for (ModuleInfo mi : moduleLookupResult.allInstances()) {
468: if (!mi.isEnabled()
469: && !disabledModuleChangeListeners
470: .containsKey(mi)) {
471: ModuleChangeListener l = new ModuleChangeListener(
472: null);
473: mi.addPropertyChangeListener(WeakListeners
474: .propertyChange(l, mi));
475: disabledModuleChangeListeners.put(mi, l);
476: }
477: }
478: }
479: }
480:
481: private final class ModuleChangeListener implements
482: PropertyChangeListener, org.openide.util.LookupListener {
483:
484: private ClassLoader cl;
485: private RequestProcessor.Task refreshListEnabled;
486: private RequestProcessor.Task refreshListDisabled;
487: private RequestProcessor rp;
488:
489: public ModuleChangeListener(ClassLoader cl) {
490: this .cl = cl;
491: }
492:
493: // Some module enabled or disabled
494: public void propertyChange(PropertyChangeEvent evt) {
495: //System.err.println("ModuleChangeListener.propertyChange("+evt+")");
496: //System.err.println(" getPropertyName = "+evt.getPropertyName()+", source = "+evt.getSource());
497: if (!ModuleInfo.PROP_ENABLED.equals(evt
498: .getPropertyName())) {
499: return;
500: }
501: clearCaches(cl);
502: ModuleInfo mi = (ModuleInfo) evt.getSource();
503: if (!mi.isEnabled() && cl != null) {
504: synchronized (moduleChangeListeners) {
505: moduleChangeListeners.remove(cl);
506: disabledModuleChangeListeners.put(mi, this );
507: }
508: cl = null;
509: } else if (mi.isEnabled()) {
510: cl = mi.getClassLoader();
511: synchronized (moduleChangeListeners) {
512: disabledModuleChangeListeners.remove(mi);
513: moduleChangeListeners.put(cl, this );
514: }
515: }
516: synchronized (this ) {
517: if (mi.isEnabled()) {
518: if (refreshListEnabled == null) {
519: if (rp == null) {
520: rp = new RequestProcessor(
521: "Debugger Services Refresh", 1);
522: }
523: refreshListEnabled = rp
524: .create(new Runnable() {
525: public void run() {
526: refreshLists(true);
527: }
528: });
529: }
530: refreshListEnabled.schedule(100);
531: } else {
532: if (refreshListDisabled == null) {
533: if (rp == null) {
534: rp = new RequestProcessor(
535: "Debugger Services Refresh", 1);
536: }
537: refreshListDisabled = rp
538: .create(new Runnable() {
539: public void run() {
540: refreshLists(false);
541: }
542: });
543: }
544: refreshListDisabled.schedule(100);
545: }
546: }
547: //refreshLists(mi.isEnabled());
548: }
549:
550: // Some new modules installed or old uninstalled
551: public void resultChanged(LookupEvent ev) {
552: clearCaches(null);
553: synchronized (moduleChangeListeners) {
554: moduleChangeListeners.clear();
555: disabledModuleChangeListeners.clear();
556: }
557: refreshLists(true);
558: listenOnDisabledModules();
559: }
560:
561: private void clearCaches(ClassLoader cl) {
562: //System.err.println("clearCaches("+cl+")");
563: synchronized (registrationCache) {
564: registrationCache.clear();
565: }
566: if (cl != null) {
567: // Release the appropriate instances from the instance cache
568: synchronized (instanceCache) {
569: List<String> classes = new ArrayList<String>(
570: instanceCache.size());
571: classes.addAll(instanceCache.keySet());
572: for (String clazz : classes) {
573: Object instance = instanceCache.get(clazz);
574: if (instance.getClass().getClassLoader() == cl) {
575: instanceCache.remove(clazz);
576: }
577: }
578: }
579: }
580: }
581:
582: private void refreshLists(boolean load) {
583: List<MetaInfLookupList> ll;
584: synchronized (lookupLists) {
585: ll = new ArrayList<MetaInfLookupList>(lookupLists
586: .size());
587: //System.err.println("\nRefreshing lookup lists ("+load+"):\n");
588: //System.err.println(" unsorted: "+lookupLists+"\n");
589: ll.addAll(lookupLists);
590: }
591: Collections.sort(ll,
592: getMetaInfLookupListComparator(load));
593: //System.err.println(" sorted: "+ll+"\n");
594: for (MetaInfLookupList mll : ll) {
595: mll.refreshContent();
596: }
597: }
598:
599: }
600:
601: public static Comparator<MetaInfLookupList> getMetaInfLookupListComparator(
602: final boolean load) {
603: return new Comparator<MetaInfLookupList>() {
604: public int compare(MetaInfLookupList l1,
605: MetaInfLookupList l2) {
606: if (load) {
607: return l1.notifyLoadOrder - l2.notifyLoadOrder;
608: } else {
609: return l1.notifyUnloadOrder
610: - l2.notifyUnloadOrder;
611: }
612: }
613: };
614: }
615:
616: /**
617: * A special List implementation, which ensures that hidden elements
618: * are removed when adding items into the list.
619: * Also it can refresh itself when the services change.
620: * The refreshing is performed under a lock on this list object so that
621: * clients have consistent data under synchronization on this.
622: */
623: private final class MetaInfLookupList<T> extends LookupList<T>
624: implements Customizer {
625:
626: private String folder;
627: private final Class<T> service;
628: private List<PropertyChangeListener> propertyChangeListeners;
629: public int notifyLoadOrder = 0;
630: public int notifyUnloadOrder = 0;
631:
632: public MetaInfLookupList(String folder, Class<T> service) {
633: this (list(folder, service), service);
634: this .folder = folder;
635: }
636:
637: private MetaInfLookupList(List<String> l, Class<T> service) {
638: this (l, getHiddenClassNames(l), service);
639: }
640:
641: private MetaInfLookupList(List<String> l, Set<String> s,
642: Class<T> service) {
643: super (s);
644: assert service != null;
645: this .service = service;
646: fillInstances(l, s);
647: listenOnDisabledModules();
648: }
649:
650: private void fillInstances(List<String> l, Set<String> s) {
651: for (String className : l) {
652: if (className.endsWith(HIDDEN))
653: continue;
654: if (s != null && s.contains(className))
655: continue;
656: Object instance = null;
657: synchronized (instanceCache) {
658: instance = instanceCache.get(className);
659: if (instance == null) {
660: instance = createInstance(className);
661: instanceCache.put(className, instance);
662: }
663: }
664: if (instance != null) {
665: try {
666: add(service.cast(instance), className);
667: } catch (ClassCastException cce) {
668: Logger.getLogger(Lookup.class.getName())
669: .log(Level.WARNING, null, cce);
670: }
671: listenOn(instance.getClass().getClassLoader());
672: }
673: }
674: }
675:
676: private synchronized void refreshContent() {
677: // Perform changes under a lock so that iterators reading this list
678: // can sync on it
679: clear();
680: List<String> l = list(folder, service);
681: Set<String> s = getHiddenClassNames(l);
682: hiddenClassNames = s;
683: fillInstances(l, s);
684: firePropertyChange();
685: }
686:
687: /* Grrrr can not be static here! :-(((
688: public static Comparator<MetaInfLookupList> getComparator(final boolean load) {
689: return new Comparator<MetaInfLookupList>() {
690: public int compare(MetaInfLookupList l1, MetaInfLookupList l2) {
691: if (load) {
692: return l1.notifyLoadOrder - l2.notifyLoadOrder;
693: } else {
694: return l1.notifyUnloadOrder - l2.notifyUnloadOrder;
695: }
696: }
697: };
698: }*/
699:
700: public void setObject(Object bean) {
701: if (NOTIFY_LOAD_FIRST == bean) {
702: notifyLoadOrder = -1;
703: } else if (NOTIFY_LOAD_LAST == bean) {
704: notifyLoadOrder = +1;
705: } else if (NOTIFY_UNLOAD_FIRST == bean) {
706: notifyUnloadOrder = -1;
707: } else if (NOTIFY_UNLOAD_LAST == bean) {
708: notifyUnloadOrder = +1;
709: } else {
710: throw new IllegalArgumentException(bean.toString());
711: }
712: }
713:
714: public synchronized void addPropertyChangeListener(
715: PropertyChangeListener listener) {
716: if (propertyChangeListeners == null) {
717: propertyChangeListeners = new ArrayList<PropertyChangeListener>();
718: }
719: propertyChangeListeners.add(listener);
720: }
721:
722: public synchronized void removePropertyChangeListener(
723: PropertyChangeListener listener) {
724: propertyChangeListeners.remove(listener);
725: }
726:
727: private void firePropertyChange() {
728: List<PropertyChangeListener> listeners;
729: synchronized (this ) {
730: if (propertyChangeListeners == null) {
731: return;
732: }
733: listeners = new ArrayList<PropertyChangeListener>(
734: propertyChangeListeners);
735: }
736: PropertyChangeEvent evt = new PropertyChangeEvent(this ,
737: "content", null, null);
738: for (PropertyChangeListener l : listeners) {
739: l.propertyChange(evt);
740: }
741: }
742:
743: }
744: }
745:
746: /**
747: * A special List implementation, which ensures that hidden elements
748: * are removed when adding items into the list.
749: */
750: private static class LookupList<T> extends ArrayList<T> {
751:
752: protected Set<String> hiddenClassNames;
753: private LinkedHashMap<T, String> instanceClassNames = new LinkedHashMap<T, String>();
754:
755: public LookupList(Set<String> hiddenClassNames) {
756: this .hiddenClassNames = hiddenClassNames;
757: }
758:
759: void add(T instance, String className) {
760: super .add(instance);
761: instanceClassNames.put(instance, className);
762: }
763:
764: @Override
765: public boolean addAll(Collection<? extends T> c) {
766: if (c instanceof LookupList) {
767: @SuppressWarnings("unchecked")
768: // XXX possible to remove using more clever pattern with Class.cast
769: LookupList<? extends T> ll = (LookupList<? extends T>) c;
770: synchronized (ll) {
771: synchronized (this ) {
772: Set<String> newHiddenClassNames = ll.hiddenClassNames;
773: if (newHiddenClassNames != null) {
774: //System.err.println("\nLookupList.addAll("+c+"), hiddenClassNames = "+hiddenClassNames+" + "+newHiddenClassNames);
775: // Check the instances we have and remove the newly hidden ones:
776: for (Iterator it = newHiddenClassNames
777: .iterator(); it.hasNext();) {
778: String className = (String) it.next();
779: if (instanceClassNames
780: .containsValue(className)) {
781: for (Iterator ii = instanceClassNames
782: .keySet().iterator(); it
783: .hasNext();) {
784: Object instance = ii.next();
785: if (className
786: .equals(instanceClassNames
787: .get(instance))) {
788: remove(instance);
789: instanceClassNames
790: .remove(instance);
791: break;
792: }
793: }
794: }
795: }
796: if (hiddenClassNames != null) {
797: hiddenClassNames
798: .addAll(newHiddenClassNames);
799: } else {
800: hiddenClassNames = newHiddenClassNames;
801: }
802: }
803: ensureCapacity(size() + ll.size());
804: boolean addedAnything = false;
805: for (T instance : ll) {
806: String className = ll.instanceClassNames
807: .get(instance);
808: if (hiddenClassNames == null
809: || !hiddenClassNames
810: .contains(className)) {
811: add(instance, className);
812: addedAnything = true;
813: }
814: }
815: return addedAnything;
816: }
817: }
818: } else {
819: return super .addAll(c);
820: }
821: }
822:
823: @Override
824: public void clear() {
825: super.clear();
826: instanceClassNames.clear();
827: }
828:
829: }
830:
831: }
|