org.openide.util.lookup.ExcludingLookup Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.openide.util.lookup;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import org.openide.util.Lookup;
import org.openide.util.LookupListener;
import java.util.*;
import org.openide.util.LookupEvent;
/** Allows exclusion of certain instances from lookup.
*
* @author Jaroslav Tulach
*/
final class ExcludingLookup extends org.openide.util.Lookup {
/** the other lookup that we delegate to */
private final Lookup delegate;
/** classes to exclude (Class[]) or just one class (Class) */
private final Object classes;
/** results */
private WeakResult> results;
/**
* Creates new Result object with supplied instances parameter.
* @param classes to be used to return from the lookup
*/
ExcludingLookup(Lookup delegate, Class[] classes) {
this.delegate = delegate;
for (Class c : classes) {
if (c == null) {
throw new NullPointerException();
}
}
if (classes.length == 1) {
this.classes = classes[0];
} else {
this.classes = classes;
}
}
@Override
public String toString() {
return "ExcludingLookup: " + delegate + " excludes: " + Arrays.asList(classes()); // NOI18N
}
public Result lookup(Template template) {
if (template == null) {
throw new NullPointerException();
}
if (areSubclassesOfThisClassAlwaysExcluded(template.getType())) {
// empty result
return Lookup.EMPTY.lookup(template);
}
R ret = null;
for (;;) { // at most twice
synchronized (this) {
WeakResult> r = results;
WeakResult> prev = null;
while (r != null) {
R> res = r.result.get();
if (res == null) {
if (prev != null) {
prev.next = r.next;
} else {
results = r.next;
}
} else {
if (template.equals(res.from)) {
@SuppressWarnings("unchecked")
Result old = (Result) res;
return old;
}
}
prev = r;
r = r.next;
}
if (ret != null) {
ret.weak.next = results;
results = ret.weak;
return ret;
}
}
ret = new R(template, delegate.lookup(template));
}
}
public T lookup(Class clazz) {
if (areSubclassesOfThisClassAlwaysExcluded(clazz)) {
return null;
}
T res = delegate.lookup(clazz);
if (isObjectAccessible(clazz, res, 0)) {
return res;
} else {
return null;
}
}
@Override
public Lookup.Item lookupItem(Lookup.Template template) {
if (areSubclassesOfThisClassAlwaysExcluded(template.getType())) {
return null;
}
Lookup.Item retValue = delegate.lookupItem(template);
if (isObjectAccessible(template.getType(), retValue, 2)) {
return retValue;
} else {
return null;
}
}
/** @return true if the instance of class c shall never be returned from this lookup
*/
private boolean areSubclassesOfThisClassAlwaysExcluded(Class> c) {
Class>[] arr = classes();
for (int i = 0; i < arr.length; i++) {
if (arr[i].isAssignableFrom(c)) {
return true;
}
}
return false;
}
/** Returns the array of classes this lookup filters.
*/
final Class>[] classes() {
if (classes instanceof Class[]) {
return (Class[]) classes;
} else {
return new Class[] { (Class) classes };
}
}
/** Does a check whether two classes are accessible (in the super/sub class)
* releation ship without walking thru any of the classes mentioned in the
* barrier.
*/
private static boolean isAccessible(Class>[] barriers, Class> from, Class> to) {
if ((to == null) || !from.isAssignableFrom(to)) {
// no way to reach each other by walking up
return false;
}
for (int i = 0; i < barriers.length; i++) {
if (to == barriers[i]) {
return false;
}
}
if (from == to) {
return true;
}
//
// depth first search
//
if (isAccessible(barriers, from, to.getSuperclass())) {
return true;
}
Class[] interfaces = to.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (isAccessible(barriers, from, interfaces[i])) {
return true;
}
}
return false;
}
/** based on type decides whether the class accepts or not anObject
* @param from the base type of the query
* @param to depending on value of type either Object, Class or Item
* @param type 0,1,2 for Object, Class or Item
* @return true if we can access the to from from by walking around the bariers
*/
private final boolean isObjectAccessible(Class from, Object to, int type) {
if (to == null) {
return false;
}
return isObjectAccessible(classes(), from, to, type);
}
/** based on type decides whether the class accepts or not anObject
* @param barriers classes to avoid when testing reachability
* @param from the base type of the query
* @param to depending on value of type either Object, Class or Item
* @param type 0,1,2 for Object, Class or Item
* @return true if we can access the to from from by walking around the bariers
*/
static final boolean isObjectAccessible(Class[] barriers, Class from, Object to, int type) {
if (to == null) {
return false;
}
switch (type) {
case 0:
return isAccessible(barriers, from, to.getClass());
case 1:
return isAccessible(barriers, from, (Class) to);
case 2: {
Item item = (Item) to;
return isAccessible(barriers, from, item.getType());
}
default:
throw new IllegalStateException("Type: " + type);
}
}
/** Filters collection accroding to set of given filters.
*/
final > T filter(
Class>[] arr, Class> from, T c, int type, T prototype
) {
T ret = null;
// optimistic strategy expecting we will not need to filter
TWICE:
for (;;) {
Iterator it = c.iterator();
BIG:
while (it.hasNext()) {
E res = it.next();
if (!isObjectAccessible(arr, from, res, type)) {
if (ret == null) {
// we need to restart the scanning again
// as there is an active filter
ret = prototype;
continue TWICE;
}
continue BIG;
}
if (ret != null) {
// if we are running the second round from TWICE
ret.add(res);
}
}
// ok, processed
break TWICE;
}
return (ret != null) ? ret : c;
}
/** Delegating result that filters unwanted items and instances.
*/
private final class R extends WaitableResult implements LookupListener {
private Result result;
private WeakResult weak;
private Object listeners;
private Template from;
R(Template from, Result delegate) {
this.from = from;
this.result = delegate;
this.weak = new WeakResult(this, delegate);
}
protected void beforeLookup(Template t) {
if (result instanceof WaitableResult) {
((WaitableResult) result).beforeLookup(t);
}
}
public void addLookupListener(LookupListener l) {
boolean add;
synchronized (this) {
listeners = AbstractLookup.modifyListenerList(true, l, listeners);
add = listeners != null;
}
if (add) {
result.addLookupListener(weak);
}
}
public void removeLookupListener(LookupListener l) {
boolean remove;
synchronized (this) {
listeners = AbstractLookup.modifyListenerList(false, l, listeners);
remove = listeners == null;
}
if (remove) {
result.removeLookupListener(weak);
}
}
@Override
public Collection extends T> allInstances() {
return openCol(result.allInstances(), 0);
}
@Override
protected Collection extends T> allInstances(boolean ignore) {
return allInstances();
}
private Collection openCol(Collection c, int type) {
return filter(classes(), from.getType(), c, type, new ArrayList(c.size()));
}
@Override
public Set> allClasses() {
return filter(classes(), from.getType(), result.allClasses(), 1, new HashSet>());
}
@Override
public Collection extends Item> allItems() {
return openCol(result.allItems(), 2);
}
@Override
public Collection extends Item> allItems(boolean ignore) {
return allItems();
}
public void resultChanged(org.openide.util.LookupEvent ev) {
if (ev.getSource() == result) {
collectFires(null);
}
}
protected void collectFires(Collection
© 2015 - 2024 Weber Informatics LLC | Privacy Policy