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: package org.openide.util.lookup;
042:
043: import java.lang.ref.Reference;
044: import java.lang.ref.WeakReference;
045: import org.openide.util.Lookup;
046: import org.openide.util.LookupListener;
047:
048: import java.util.*;
049: import org.openide.util.LookupEvent;
050:
051: /** Allows exclusion of certain instances from lookup.
052: *
053: * @author Jaroslav Tulach
054: */
055: final class ExcludingLookup extends org.openide.util.Lookup {
056: /** the other lookup that we delegate to */
057: private Lookup delegate;
058:
059: /** classes to exclude (Class[]) or just one class (Class) */
060: private Object classes;
061:
062: /**
063: * Creates new Result object with supplied instances parameter.
064: * @param instances to be used to return from the lookup
065: */
066: ExcludingLookup(Lookup delegate, Class[] classes) {
067: this .delegate = delegate;
068:
069: if (classes.length == 1) {
070: this .classes = classes[0];
071: } else {
072: this .classes = classes;
073: }
074: }
075:
076: @Override
077: public String toString() {
078: return "ExcludingLookup: " + delegate + " excludes: "
079: + Arrays.asList(classes()); // NOI18N
080: }
081:
082: public <T> Result<T> lookup(Template<T> template) {
083: if (template == null) {
084: throw new NullPointerException();
085: }
086:
087: if (areSubclassesOfThisClassAlwaysExcluded(template.getType())) {
088: // empty result
089: return Lookup.EMPTY.lookup(template);
090: }
091:
092: return new R<T>(template.getType(), delegate.lookup(template));
093: }
094:
095: public <T> T lookup(Class<T> clazz) {
096: if (areSubclassesOfThisClassAlwaysExcluded(clazz)) {
097: return null;
098: }
099:
100: T res = delegate.lookup(clazz);
101:
102: if (isObjectAccessible(clazz, res, 0)) {
103: return res;
104: } else {
105: return null;
106: }
107: }
108:
109: @Override
110: public <T> Lookup.Item<T> lookupItem(Lookup.Template<T> template) {
111: if (areSubclassesOfThisClassAlwaysExcluded(template.getType())) {
112: return null;
113: }
114:
115: Lookup.Item<T> retValue = delegate.lookupItem(template);
116:
117: if (isObjectAccessible(template.getType(), retValue, 2)) {
118: return retValue;
119: } else {
120: return null;
121: }
122: }
123:
124: /** @return true if the instance of class c shall never be returned from this lookup
125: */
126: private boolean areSubclassesOfThisClassAlwaysExcluded(Class<?> c) {
127: Class<?>[] arr = classes();
128:
129: for (int i = 0; i < arr.length; i++) {
130: if (arr[i].isAssignableFrom(c)) {
131: return true;
132: }
133: }
134:
135: return false;
136: }
137:
138: /** Returns the array of classes this lookup filters.
139: */
140: final Class<?>[] classes() {
141: if (classes instanceof Class[]) {
142: return (Class[]) classes;
143: } else {
144: return new Class[] { (Class) classes };
145: }
146: }
147:
148: /** Does a check whether two classes are accessible (in the super/sub class)
149: * releation ship without walking thru any of the classes mentioned in the
150: * barrier.
151: */
152: private static boolean isAccessible(Class<?>[] barriers,
153: Class<?> from, Class<?> to) {
154: if ((to == null) || !from.isAssignableFrom(to)) {
155: // no way to reach each other by walking up
156: return false;
157: }
158:
159: for (int i = 0; i < barriers.length; i++) {
160: if (to == barriers[i]) {
161: return false;
162: }
163: }
164:
165: if (from == to) {
166: return true;
167: }
168:
169: //
170: // depth first search
171: //
172: if (isAccessible(barriers, from, to.getSuperclass())) {
173: return true;
174: }
175:
176: Class[] interfaces = to.getInterfaces();
177:
178: for (int i = 0; i < interfaces.length; i++) {
179: if (isAccessible(barriers, from, interfaces[i])) {
180: return true;
181: }
182: }
183:
184: return false;
185: }
186:
187: /** based on type decides whether the class accepts or not anObject
188: * @param from the base type of the query
189: * @param to depending on value of type either Object, Class or Item
190: * @param type 0,1,2 for Object, Class or Item
191: * @return true if we can access the to from from by walking around the bariers
192: */
193: private final boolean isObjectAccessible(Class from, Object to,
194: int type) {
195: if (to == null) {
196: return false;
197: }
198:
199: return isObjectAccessible(classes(), from, to, type);
200: }
201:
202: /** based on type decides whether the class accepts or not anObject
203: * @param barriers classes to avoid when testing reachability
204: * @param from the base type of the query
205: * @param to depending on value of type either Object, Class or Item
206: * @param type 0,1,2 for Object, Class or Item
207: * @return true if we can access the to from from by walking around the bariers
208: */
209: static final boolean isObjectAccessible(Class[] barriers,
210: Class from, Object to, int type) {
211: if (to == null) {
212: return false;
213: }
214:
215: switch (type) {
216: case 0:
217: return isAccessible(barriers, from, to.getClass());
218:
219: case 1:
220: return isAccessible(barriers, from, (Class) to);
221:
222: case 2: {
223: Item item = (Item) to;
224:
225: return isAccessible(barriers, from, item.getType());
226: }
227:
228: default:
229: throw new IllegalStateException("Type: " + type);
230: }
231: }
232:
233: /** Filters collection accroding to set of given filters.
234: */
235: final <E, T extends Collection<E>> T filter(Class<?>[] arr,
236: Class<?> from, T c, int type, T prototype) {
237: T ret = null;
238:
239: // optimistic strategy expecting we will not need to filter
240: TWICE: for (;;) {
241: Iterator<E> it = c.iterator();
242: BIG: while (it.hasNext()) {
243: E res = it.next();
244:
245: if (!isObjectAccessible(arr, from, res, type)) {
246: if (ret == null) {
247: // we need to restart the scanning again
248: // as there is an active filter
249: ret = prototype;
250: continue TWICE;
251: }
252:
253: continue BIG;
254: }
255:
256: if (ret != null) {
257: // if we are running the second round from TWICE
258: ret.add(res);
259: }
260: }
261:
262: // ok, processed
263: break TWICE;
264: }
265:
266: return (ret != null) ? ret : c;
267: }
268:
269: /** Delegating result that filters unwanted items and instances.
270: */
271: private final class R<T> extends WaitableResult<T> implements
272: LookupListener {
273: private Result<T> result;
274: private WeakResult<T> weak;
275: private Object listeners;
276: private Class<?> from;
277:
278: R(Class<?> from, Result<T> delegate) {
279: this .from = from;
280: this .result = delegate;
281: this .weak = new WeakResult<T>(this , delegate);
282: }
283:
284: protected void beforeLookup(Template t) {
285: if (result instanceof WaitableResult) {
286: ((WaitableResult) result).beforeLookup(t);
287: }
288: }
289:
290: public void addLookupListener(LookupListener l) {
291: boolean add;
292:
293: synchronized (this ) {
294: listeners = AbstractLookup.modifyListenerList(true, l,
295: listeners);
296: add = listeners != null;
297: }
298:
299: if (add) {
300: result.addLookupListener(weak);
301: }
302: }
303:
304: public void removeLookupListener(LookupListener l) {
305: boolean remove;
306:
307: synchronized (this ) {
308: listeners = AbstractLookup.modifyListenerList(false, l,
309: listeners);
310: remove = listeners == null;
311: }
312:
313: if (remove) {
314: result.removeLookupListener(weak);
315: }
316: }
317:
318: public Collection<? extends T> allInstances() {
319: return openCol(result.allInstances(), 0);
320: }
321:
322: private <S> Collection<S> openCol(Collection<S> c, int type) {
323: return filter(classes(), from, c, type, new ArrayList<S>(c
324: .size()));
325: }
326:
327: @Override
328: public Set<Class<? extends T>> allClasses() {
329: return filter(classes(), from, result.allClasses(), 1,
330: new HashSet<Class<? extends T>>());
331: }
332:
333: @Override
334: public Collection<? extends Item<T>> allItems() {
335: return openCol(result.allItems(), 2);
336: }
337:
338: public void resultChanged(org.openide.util.LookupEvent ev) {
339: if (ev.getSource() == result) {
340: collectFires(null);
341: }
342: }
343:
344: protected void collectFires(Collection<Object> evAndListeners) {
345: LookupListener[] arr;
346:
347: synchronized (this ) {
348: if (listeners == null) {
349: return;
350: }
351:
352: if (listeners instanceof LookupListener) {
353: arr = new LookupListener[] { (LookupListener) listeners };
354: } else {
355: ArrayList<?> l = (ArrayList<?>) listeners;
356: arr = l.toArray(new LookupListener[l.size()]);
357: }
358: }
359:
360: final LookupListener[] ll = arr;
361: final org.openide.util.LookupEvent newev = new org.openide.util.LookupEvent(
362: this );
363: AbstractLookup.notifyListeners(ll, newev, evAndListeners);
364: }
365: } // end of R
366:
367: private final class WeakResult<T> extends WaitableResult<T>
368: implements LookupListener {
369: private Lookup.Result source;
370: private Reference<R<T>> result;
371:
372: public WeakResult(R<T> r, Lookup.Result<T> s) {
373: this .result = new WeakReference<R<T>>(r);
374: this .source = s;
375: }
376:
377: protected void beforeLookup(Lookup.Template t) {
378: R r = (R) result.get();
379: if (r != null) {
380: r.beforeLookup(t);
381: } else {
382: source.removeLookupListener(this );
383: }
384: }
385:
386: protected void collectFires(Collection<Object> evAndListeners) {
387: R<T> r = result.get();
388: if (r != null) {
389: r.collectFires(evAndListeners);
390: } else {
391: source.removeLookupListener(this );
392: }
393: }
394:
395: public void addLookupListener(LookupListener l) {
396: assert false;
397: }
398:
399: public void removeLookupListener(LookupListener l) {
400: assert false;
401: }
402:
403: public Collection<T> allInstances() {
404: assert false;
405: return null;
406: }
407:
408: public void resultChanged(LookupEvent ev) {
409: R r = (R) result.get();
410: if (r != null) {
411: r.resultChanged(ev);
412: } else {
413: source.removeLookupListener(this );
414: }
415: }
416:
417: @Override
418: public Collection<? extends Item<T>> allItems() {
419: assert false;
420: return null;
421: }
422:
423: @Override
424: public Set<Class<? extends T>> allClasses() {
425: assert false;
426: return null;
427: }
428: } // end of WeakResult
429: }
|