| /* |
| * 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.util.Arrays; |
| import org.netbeans.modules.openide.util.GlobalLookup; |
| import org.openide.util.Lookup; |
| import org.openide.util.lookup.implspi.NamedServicesProvider; |
| |
| /** |
| * Static factory methods for creating common lookup implementations. |
| * |
| * @author David Strupl |
| * @since 2.21 |
| */ |
| public class Lookups { |
| |
| /** static methods only */ |
| private Lookups() {} |
| |
| /** |
| * Creates a singleton lookup. It means lookup that contains only |
| * one object specified via the supplied parameter. The lookup will |
| * either return the object or null if the supplied template does |
| * not match the class. If the specified argument is null the method |
| * will end with NullPointerException. |
| * @return Fully initialized lookup object ready to use |
| * @throws NullPointerException if the supplied argument is null |
| * @since 2.21 |
| */ |
| public static Lookup singleton(Object objectToLookup) { |
| if (objectToLookup == null) { |
| throw new NullPointerException(); |
| } |
| |
| return new SingletonLookup(objectToLookup); |
| } |
| |
| /** |
| * Creates a lookup that contains an array of objects specified via the |
| * parameter. The resulting lookup is fixed in the following sense: it |
| * contains only fixed set of objects passed in by the array parameter. |
| * Its contents never changes so registering listeners on such lookup |
| * does not have any observable effect (the listeners are never called). |
| * |
| * @param objectsToLookup list of objects to include |
| * @return Fully initialized lookup object ready to use |
| * @throws NullPointerException if the supplied argument is null |
| * @since 2.21 |
| * |
| */ |
| public static Lookup fixed(Object... objectsToLookup) { |
| if (objectsToLookup == null) { |
| throw new NullPointerException(); |
| } |
| |
| if (objectsToLookup.length == 0) { |
| return Lookup.EMPTY; |
| } |
| |
| if (objectsToLookup.length == 1) { |
| return singleton(objectsToLookup[0]); |
| } |
| |
| return new SimpleLookup(Arrays.asList(objectsToLookup)); |
| } |
| |
| /** |
| * Creates a lookup that contains an array of objects specified via the |
| * parameter. The resulting lookup is fixed in the following sense: it |
| * contains only fixed set of objects passed in by the array parameter. |
| * The objects returned from this lookup are converted to real objects |
| * before they are returned by the lookup. |
| * Its contents never changes so registering listeners on such lookup |
| * does not have any observable effect (the listeners are never called). |
| * |
| * @return Fully initialized lookup object ready to use |
| * @throws NullPointerException if the any of the arguments is null |
| * @since 2.21 |
| * |
| */ |
| public static <T,R> Lookup fixed(T[] keys, InstanceContent.Convertor<? super T,R> convertor) { |
| if (keys == null) { |
| throw new NullPointerException(); |
| } |
| |
| if (convertor == null) { |
| throw new NullPointerException(); |
| } |
| |
| return new SimpleLookup(Arrays.asList(keys), convertor); |
| } |
| |
| /** Creates a lookup that delegates to another one but that one can change |
| * from time to time. The returned lookup checks every time somebody calls |
| * <code>lookup</code> or <code>lookupItem</code> method whether the |
| * provider still returns the same lookup. If not, it updates state of |
| * all {@link org.openide.util.Lookup.Result}s |
| * that it created (and that still exists). |
| * <P> |
| * The user of this method has to implement its provider's <code>getLookup</code> |
| * method (must be thread safe and fast, will be called often and from any thread) |
| * pass it to this method and use the returned lookup. Whenever the user |
| * changes the return value from the <code>getLookup</code> method and wants |
| * to notify listeners on the lookup about that it should trigger the event |
| * firing, for example by calling <code>lookup.lookup (Object.class)</code> |
| * directly on the lookup returned by this method |
| * that forces a check of the return value of {@link org.openide.util.Lookup.Provider#getLookup}</code>. |
| * |
| * @param provider the provider that returns a lookup to delegate to |
| * @return lookup delegating to the lookup returned by the provider |
| * @since 3.9 |
| */ |
| public static Lookup proxy(Lookup.Provider provider) { |
| return new SimpleProxyLookup(provider); |
| } |
| |
| /** Returns a lookup that implements the JDK1.3 JAR services mechanism and delegates |
| * to META-INF/services/name.of.class files. |
| * <p>Some extensions to the JAR services specification are implemented: |
| * <ol> |
| * <li>An entry may be followed by a line of the form <code>#position=<i>integer</i></code> |
| * to specify ordering. (Smaller numbers first, entries with unspecified position last.) |
| * <li>A line of the form <code>#-<i>classname</i></code> suppresses an entry registered |
| * in another file, so can be used to supersede one implementation with another. |
| * </ol> |
| * <p>Note: It is not dynamic - so if you need to change the classloader or JARs, |
| * wrap it in a {@link ProxyLookup} and change the delegate when necessary. |
| * Existing instances will be kept if the implementation classes are unchanged, |
| * so there is "stability" in doing this provided some parent loaders are the same |
| * as the previous ones. |
| * @since 3.35 |
| * @see ServiceProvider |
| */ |
| public static Lookup metaInfServices(ClassLoader classLoader) { |
| return new MetaInfServicesLookup(classLoader, "META-INF/services/"); // NOI18N |
| } |
| |
| /** Returns a lookup that behaves exactly like {@link #metaInfServices(ClassLoader)} |
| * except that it does not read data from <code>META-INF/services/</code>, but instead |
| * from the specified prefix. |
| * @param classLoader class loader to use for loading |
| * @param prefix prefix to prepend to the class name when searching |
| * @since 7.9 |
| * @see ServiceProvider#path |
| */ |
| public static Lookup metaInfServices(ClassLoader classLoader, String prefix) { |
| return new MetaInfServicesLookup(classLoader, prefix); |
| } |
| |
| /** Creates a <q>named</q> lookup. |
| * It is a lookup identified by a given path. |
| * Two lookups with the same path should have the same content. |
| * <p>It is expected that each <q>named</q> lookup |
| * will contain a superset of what would be created by: |
| * <code>{@linkplain #metaInfServices(ClassLoader,String) metaInfServices}(theRightLoader, "META-INF/namedservices/" + path + "/")</code> |
| * |
| * <p class="nonnormative">Various environments can add their own |
| * extensions to its content. As such |
| * {@link Lookups#forPath(java.lang.String)} can combine lookups |
| * from several sources. In current NetBeans Runtime Container, two lookups are used: |
| * </p> |
| * <ul class="nonnormative"> |
| * <li><code>Lookups.metaInfServices("META-INF/namedservices/" + path)</code></li> |
| * <li><code>org.openide.loaders.FolderLookup(path)</code></li> |
| * </ul> |
| * <p class="nonnormative"> |
| * Please note that these lookups differ in the way they inspect sub-folders. |
| * The first lookup just returns instances from the given path, ignoring |
| * sub-folders, the second one retrieves instances from the whole sub-tree. |
| * </p> |
| * <p> |
| * Read more about the <a href="@org-openide-util@/org/openide/util/doc-files/api.html#folderlookup">usage of this method</a>. |
| * |
| * @param path the path identifying the lookup, e.g. <code>Servers/J2EEWrapper</code> |
| * @return lookup associated with this path |
| * @since 7.9 |
| * @see NamedServiceDefinition |
| */ |
| public static Lookup forPath(String path) { |
| if (!path.endsWith("/")) { |
| path = path + "/"; |
| } |
| return NamedServicesProvider.forPath(path); |
| } |
| |
| /** Creates a lookup that wraps another one and filters out instances |
| * of specified classes. If you have a lookup and |
| * you want to remove all instances of ActionMap you can use: |
| * <pre> |
| * l = Lookups.exclude(lookup, ActionMap.class); |
| * </pre> |
| * Then anybody who asks for <code>l.lookup(ActionMap.class)</code> or |
| * subclass will get <code>null</code>. Even if the original lookup contains the |
| * value. |
| * To create empty lookup (well, just an example, otherwise use {@link Lookup#EMPTY}) one could use: |
| * <pre> |
| * Lookup.exclude(anyLookup, Object.class); |
| * </pre> |
| * as any instance in any lookup is of type Object and thus would be excluded. |
| * <p> |
| * The complete behavior can be described as <code>classes</code> being |
| * a barrier. For an object not to be excluded, there has to be an inheritance |
| * path between the queried class and the actual class of the instance, |
| * that is not blocked by any of the excluded classes and |
| * the queried class cannot be subclass of an excluded class: |
| * <pre> |
| * interface A {} |
| * interface B {} |
| * class C implements A, B {} |
| * Object c = new C(); |
| * Lookup l1 = Lookups.singleton(c); |
| * Lookup l2 = Lookups.exclude(l1, A.class); |
| * assertNull("A is directly excluded", l2.lookup(A.class)); |
| * assertEquals("Returns C as A.class is not between B and C", c, l2.lookup(B.class)); |
| * </pre> |
| * For more info check the |
| * <a href="http://hg.netbeans.org/main-golden/annotate/4883eaeda744/openide.util/test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java"> |
| * excluding lookup tests</a> and the discussion in issue |
| * <a href="http://openide.netbeans.org/issues/show_bug.cgi?id=53058">53058</a>. |
| * |
| * @param lookup the original lookup that should be filtered |
| * @param classes array of classes those instances should be excluded |
| * @since 5.4 |
| */ |
| public static Lookup exclude(Lookup lookup, Class... classes) { |
| return new ExcludingLookup(lookup, classes); |
| } |
| |
| /** Creates <code>Lookup.Item</code> representing the instance passed in. |
| * |
| * @param instance the object for which Lookup.Item should be creted |
| * @param id unique identification of the object, for details see {@link org.openide.util.Lookup.Item#getId}, |
| * can be <code>null</code> |
| * @return lookup item representing instance |
| * @since 4.8 |
| */ |
| public static <T> Lookup.Item<T> lookupItem(T instance, String id) { |
| return new LookupItem<T>(instance, id); |
| } |
| |
| /** Temporarily (while the <code>code</code> is running) changes value |
| * of {@link Lookup#getDefault()} to here-in provided lookup. Useful in a |
| * multi user environment where different users and their requests should |
| * be associated with different content of default lookup. |
| * <p/> |
| * As a special case, {@code executeWith} will execute the Runnable with |
| * the system global lookup (the one effective during system bootstrap), when |
| * the passed {@code defaultLookup} parameter is {@code null}. This feature may |
| * be useful to switch from a specialized Lookup back to a default one for |
| * some limited processing, or when the caller needs to bypass potential |
| * execution-local content temporary effective in the default Lookup and |
| * work with system-wide services only. |
| * |
| * @param defaultLookup the lookup to be come default while code is running |
| * @param code the code to execute (synchronously) before the method returns |
| * @since 8.30 |
| * @since 8.31 can delegate to the system Lookup |
| */ |
| public static void executeWith(Lookup defaultLookup, Runnable code) { |
| if (!GlobalLookup.execute(defaultLookup, code)) { |
| code.run(); |
| } |
| } |
| |
| private static class LookupItem<T> extends Lookup.Item<T> { |
| private String id; |
| private T instance; |
| |
| public LookupItem(T instance) { |
| this(instance, null); |
| } |
| |
| public LookupItem(T instance, String id) { |
| this.id = id; |
| this.instance = instance; |
| } |
| |
| public String getDisplayName() { |
| return getId(); |
| } |
| |
| public String getId() { |
| return (id == null) ? instance.toString() : id; |
| } |
| |
| public T getInstance() { |
| return instance; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Class<? extends T> getType() { |
| return (Class<? extends T>)instance.getClass(); |
| } |
| |
| public @Override boolean equals(Object object) { |
| if (object instanceof LookupItem) { |
| return instance == ((LookupItem) object).getInstance(); |
| } |
| |
| return false; |
| } |
| |
| public @Override int hashCode() { |
| return instance.hashCode(); |
| } |
| } |
| // End of LookupItem class |
| } |