All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.util.Lookup Maven / Gradle / Ivy

The newest version!
/*
 * 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;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.modules.openide.util.GlobalLookup;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
import org.openide.util.lookup.ServiceProvider;

/**
 * A general registry permitting clients to find instances of services
 * (implementation of a given interface).
 * This class is inspired by the
 * Jini
 * registration and lookup mechanism. The difference is that the methods do
 * not throw checked exceptions (as they usually work only locally and not over the network)
 * and that the Lookup API concentrates on the lookup, not on the registration
 * (although {@link Lookup#getDefault()} is strongly encouraged to support
 * {@link Lookups#metaInfServices(java.lang.ClassLoader) } for registration in addition to whatever
 * else it decides to support).
 * 

* For a general talk about the idea behind the lookup pattern please see *

* * @see org.openide.util.lookup.AbstractLookup * @see Lookups * @see LookupListener * @see LookupEvent * @author Jaroslav Tulach */ public abstract class Lookup { private static final Logger LOG = Logger.getLogger("org.openide.util.lookup.init"); // NOI18N /** A dummy lookup that never returns any results. */ public static final Lookup EMPTY = new Empty(); /** default instance */ private static Lookup defaultLookup; /** * Default instance's provider */ private static Lookup.Provider defaultLookupProvider; /** Empty constructor for use by subclasses. */ public Lookup() { } /** Static method to obtain the global lookup in the whole system. * The actual returned implementation can be different in different * systems, but the default one is based on * {@link org.openide.util.lookup.Lookups#metaInfServices} * with the context classloader of the first caller. Each system is * adviced to honor this and include some form of metaInfServices * implementation in the returned lookup as usage of META-INF/services * is a JDK standard. * * @return the global lookup in the system * @see ServiceProvider */ public static synchronized Lookup getDefault() { Lookup gLpk = GlobalLookup.current(); if (gLpk != null) { return gLpk; } if (defaultLookup != null || defaultLookupProvider != null) { if (defaultLookupProvider != null) { Lookup lkp = defaultLookupProvider.getLookup(); if (lkp != null) { return lkp; } } return defaultLookup; } LOG.log(Level.FINER, "About to initialize Lookup@{0}.getDefault() by {1}", new Object[] { Lookup.class.getClassLoader(), Thread.currentThread() } ); // You can specify a Lookup impl using a system property if you like. String className = System.getProperty("org.openide.util.Lookup"); // NOI18N LOG.log(Level.FINER, "Specified by property? Value: {0}", className); if ("-".equals(className)) { // NOI18N // Suppress even MetaInfServicesLookup. return EMPTY; } ClassLoader l = Thread.currentThread().getContextClassLoader(); LOG.log(Level.FINER, "Searching in classloader {0}", l); try { if (className != null) { Object o = Class.forName(className, true, l).getDeclaredConstructor().newInstance(); defaultLookup = (Lookup)o; // set the global global Lookuo GlobalLookup.setSystemLookup(defaultLookup); LOG.log(Level.FINE, "Default lookup initialized {0}", defaultLookup); // for testing purposes, tests may setup a class implementing both interfaces if (o instanceof Lookup.Provider) { defaultLookupProvider = (Lookup.Provider)o; Lookup lkp = defaultLookupProvider.getLookup(); if (lkp != null) { return lkp; } } return defaultLookup; } } catch (Exception e) { LOG.log(Level.WARNING, "Constuction of " + className + " via " + l + " failed", e); } // OK, none specified (successfully) in a system property. // Try MetaInfServicesLookup as a default, which may also // have a org.openide.util.Lookup line specifying the lookup. Lookup misl = Lookups.metaInfServices(l); defaultLookup = misl.lookup(Lookup.class); LOG.log(Level.FINER, "Searching for {0} in {1} yields {2}", new Object[]{Lookup.class, misl, defaultLookup}); if (defaultLookup != null) { if (defaultLookup instanceof Lookup.Provider) { defaultLookupProvider = (Lookup.Provider)defaultLookup; Lookup lkp = defaultLookupProvider.getLookup(); if (lkp != null) { return lkp; } } LOG.log(Level.FINE, "Default lookup initialized {0}", defaultLookup); return defaultLookup; } // You may also specify a Lookup.Provider. Lookup.Provider prov = misl.lookup(Lookup.Provider.class); LOG.log(Level.FINER, "Searching for {0} in {1} yields {2}", new Object[]{Lookup.Provider.class, misl, defaultLookup}); if (prov != null) { defaultLookup = Lookups.proxy(prov); LOG.log(Level.FINE, "Default lookup initialized {0}", defaultLookup); return defaultLookup; } DefLookup def = new DefLookup(); def.init(l, misl, false); defaultLookup = def; def.init(l, misl, true); LOG.log(Level.FINE, "Default lookup initialized {0}", defaultLookup); return defaultLookup; } private static final class DefLookup extends ProxyLookup { public DefLookup() { super(new Lookup[0]); } public void init(ClassLoader loader, Lookup metaInfLookup, boolean addPath) { // Had no such line, use simple impl. // It does however need to have ClassLoader available or many things will break. // Use the thread context classloader in effect now. Lookup clLookup = Lookups.singleton(loader); List arr = new ArrayList(); arr.add(metaInfLookup); arr.add(clLookup); String paths = System.getProperty("org.openide.util.Lookup.paths"); // NOI18N if (addPath && paths != null) { LOG.log(Level.FINE, "Adding search paths {0}", paths); for (String p : paths.split(":")) { // NOI18N arr.add(Lookups.forPath(p)); } } LOG.log(Level.FINER, "Setting DefLookup delegates {0}", arr); setLookups(arr.toArray(new Lookup[0])); } } /** Called from MockServices to reset default lookup in case services change */ private static synchronized void resetDefaultLookup() { if (defaultLookup == null || defaultLookup instanceof DefLookup) { DefLookup def = (DefLookup)defaultLookup; ClassLoader l = Thread.currentThread().getContextClassLoader(); Lookup misl = Lookups.metaInfServices(l); if (def == null) { def = new DefLookup(); def.init(l, misl, false); defaultLookup = def; } def.init(l, misl, true); } } /** Look up an object matching a given interface. * This is the simplest method to use. * If more than one object matches, the first will be returned. * The template class may be a class or interface; the instance is * guaranteed to be assignable to it. * * @param type of interface we are searching for * @param clazz class of the object we are searching for * @return an object implementing the given class or null if no such * implementation is found */ public abstract T lookup(Class clazz); /** The general lookup method. Callers can get list of all instances and classes * that match the given template, request more info about * them in form of {@link Lookup.Item} and attach a listener to * this be notified about changes. The general interface does not * specify whether subsequent calls with the same template produce new * instance of the {@link Lookup.Result} or return shared instance. The * prefered behaviour however is to return shared one. * * @param type of interface we are searching for * @param template a template describing the services to look for * @return an object containing the results */ public abstract Result lookup(Template template); /** Look up the first item matching a given template. * Includes not only the instance but other associated information. * * @param type of interface we are searching for * @param template the template to check * @return a matching item or null * * @since 1.8 */ public Item lookupItem(Template template) { Result res = lookup(template); Iterator> it = res.allItems().iterator(); return it.hasNext() ? it.next() : null; } /** * Find a result corresponding to a given class. * Equivalent to calling {@link #lookup(Lookup.Template)} but slightly more convenient. * Subclasses may override this method to produce the same semantics more efficiently. * * @param type of interface we are searching for * @param clazz the supertype of the result * @return a live object representing instances of that type * @since org.openide.util 6.10 */ public Lookup.Result lookupResult(Class clazz) { return lookup(new Lookup.Template(clazz)); } /** * Find all instances corresponding to a given class. * Equivalent to calling {@link #lookupResult} and asking for {@link Lookup.Result#allInstances} but slightly more convenient. * Subclasses may override this method to produce the same semantics more efficiently. *

Example usage:

* {@snippet file="org/openide/util/lookup/SampleLookupUsages.java" region="iterate"} * @param clazz the supertype of the result * @return all currently available instances of that type * @since org.openide.util 6.10 */ public Collection lookupAll(Class clazz) { return lookupResult(clazz).allInstances(); } /** * Objects implementing interface Lookup.Provider are capable of * and willing to provide a lookup (usually bound to the object). * @since 3.6 */ public interface Provider { /** * Returns lookup associated with the object. * @return fully initialized lookup instance provided by this object */ Lookup getLookup(); } /* * I expect this class to grow in the future, but for now, it is * enough to start with something simple. */ /** Template defining a pattern to filter instances by. */ public static final class Template extends Object { /** cached hash code */ private int hashCode; /** type of the service */ private Class type; /** identity to search for */ private String id; /** instance to search for */ private T instance; /** General template to find all possible instances. * @deprecated Use new Template (Object.class) which * is going to be better typed with JDK1.5 templates and should produce * the same result. */ @Deprecated public Template() { this(null); } /** Create a simple template matching by class. * @param type the class of service we are looking for (subclasses will match) */ public Template(Class type) { this(type, null, null); } /** Constructor to create new template. * @param type the class of service we are looking for or null to leave unspecified * @param id the ID of the item/service we are looking for or null to leave unspecified * @param instance a specific known instance to look for or null to leave unspecified */ public Template(Class type, String id, T instance) { this.type = extractType(type); this.id = id; this.instance = instance; } @SuppressWarnings("unchecked") private Class extractType(Class type) { return (type == null) ? (Class)Object.class : type; } /** Get the class (or superclass or interface) to search for. * If it was not specified in the constructor, Object is used as * this will match any instance. * @return the class to search for */ public Class getType() { return type; } /** Get the persistent identifier being searched for, if any. * @return the ID or null * @see Lookup.Item#getId * * @since 1.8 */ public String getId() { return id; } /** Get the specific instance being searched for, if any. * Most useful for finding an Item when the instance * is already known. * * @return the object to find or null * * @since 1.8 */ public T getInstance() { return instance; } /* Computes hashcode for this template. The hashcode is cached. * @return hashcode */ @Override public int hashCode() { if (hashCode != 0) { return hashCode; } hashCode = ((type == null) ? 1 : type.hashCode()) + ((id == null) ? 2 : id.hashCode()) + ((instance == null) ? 3 : 0); return hashCode; } /* Checks whether two templates represent the same query. * @param obj another template to check * @return true if so, false otherwise */ @Override public boolean equals(Object obj) { if (!(obj instanceof Template)) { return false; } Template t = (Template) obj; if (hashCode() != t.hashCode()) { // this is an optimalization - the hashCodes should have been // precomputed return false; } if (type != t.type) { return false; } if (id == null) { if (t.id != null) { return false; } } else { if (!id.equals(t.id)) { return false; } } if (instance == null) { return (t.instance == null); } else { return instance.equals(t.instance); } } /* for debugging */ @Override public String toString() { return "Lookup.Template[type=" + type + ",id=" + id + ",instance=" + instance + "]"; // NOI18N } } /** Result of a lookup request. * Allows access to all matching instances at once. * Also permits listening to changes in the result. * Result can contain duplicate items. */ public abstract static class Result extends Object { /** Registers a listener that is invoked when there is a possible * change in this result. * *
* Sometimes people report that their listener is not receiving * events (for example IZ 191471) * or that the listener receives few events, but then it stops * listening. * Such behavior is often caused by not keeping strong reference to * the {@link Result} object. When it gets garbage collected * it can no longer deliver events. Thus remember to keep reference * to the object you are attaching listener to. *
* * @param l the listener to add */ public abstract void addLookupListener(LookupListener l); /** Unregisters a listener previously added. * @param l the listener to remove */ public abstract void removeLookupListener(LookupListener l); /** Get all instances in the result. The return value is an * unmodifiable list (hence the type * should be {@link List} as the order matters, but the {@link Collection} * is kept for compatibility reasons) of all instances present in * the {@link Result} right now that will never change its content. * *
* While the returned collection never changes its content, some * implementation like {@link ProxyLookup} may * compute the content * lazily. At least * once * such behavior resulted in a deadlock. *
* @return unmodifiable collection of all instances that will never change its content */ public abstract Collection allInstances(); /** Get all classes represented in the result. * That is, the set of concrete classes * used by instances present in the result. * All duplicate classes will be omitted. * @return unmodifiable set of Class objects that will never change its content * * @since 1.8 */ public Set> allClasses() { return Collections.emptySet(); } /** Get all registered items. * This includes all pairs of instances together * with their classes, {@link Item#getId() IDs}, and so on. * The return value is an unmodifiable list (hence the type * should be {@link List} as the order matters, but the {@link Collection} * is kept for compatibility reasons) of all {@link Item items} present in * the {@link Result} right now that will never change its content. * *
* While the returned collection never changes its content, some * implementation like {@link ProxyLookup} may * compute the content * lazily. *
* * @return unmodifiable collection of {@link Lookup.Item} that will never change its content * * @since 1.8 */ public Collection> allItems() { return Collections.emptyList(); } } /** A single item in a lookup result. * This wrapper provides unified access to not just the instance, * but its class, a possible persistent identifier, and so on. * * @since 1.25 */ public abstract static class Item extends Object { /** Get the instance itself. * @return the instance or null if the instance cannot be created */ public abstract T getInstance(); /** Get the implementing class of the instance. * @return the class of the item */ public abstract Class getType(); // XXX can it be null?? /** Get a persistent identifier for the item. * This identifier should uniquely represent the item * within its containing lookup (and if possible within the * global lookup as a whole). For example, it might represent * the source of the instance as a file name. The ID may be * persisted and in a later session used to find the same instance * as was encountered earlier, by means of passing it into a * lookup template. * * @return a string ID of the item */ public abstract String getId(); /** Get a human presentable name for the item. * This might be used when summarizing all the items found in a * lookup result in some part of a GUI. * @return the string suitable for presenting the object to a user */ public abstract String getDisplayName(); /* show ID for debugging */ @Override public String toString() { return getId(); } } // // Implementation of the default lookup // private static final class Empty extends Lookup { private static final Result NO_RESULT = new Result() { public void addLookupListener(LookupListener l) { } public void removeLookupListener(LookupListener l) { } public Collection allInstances() { return Collections.EMPTY_SET; } }; Empty() { } public T lookup(Class clazz) { return null; } @SuppressWarnings("unchecked") public Result lookup(Template template) { return NO_RESULT; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy