/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.util;
import java.util.*;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
/*
* I admit that the class is inspirated by JINI(tm). The difference
* is that methods are not allowed to throw exceptions and also the license.
* Also we are mostly concentrated on the lookup, not on the registration.
* And last difference that comes to my mind is that our classes are not
* serializable (we are not distributed).
*/
/** A general registrar permitting clients to find instances of services.
*
* @author Jaroslav Tulach
*/
public abstract class Lookup {
/** A dummy lookup that never returns any results.
*/
public static final Lookup EMPTY = new Empty ();
/** default instance */
private static Lookup defaultLookup;
/** Static method to obtain the global lookup in the whole system.
* @return the global lookup in the system
*/
public static synchronized Lookup getDefault() {
if (defaultLookup != null) {
return defaultLookup;
}
// You can specify a Lookup impl using a system property if you like.
String className = System.getProperty(
"org.openide.util.Lookup" // NOI18N
);
if ("-".equals(className)) { // NOI18N
// Suppress even MetaInfServicesLookup.
return EMPTY;
}
ClassLoader l = Thread.currentThread().getContextClassLoader();
try {
if (className != null) {
defaultLookup = (Lookup)Class.forName(className, true, l).newInstance();
return defaultLookup;
}
} catch (Exception e) {
// do not use ErrorManager because we are in the startup code
// and ErrorManager might not be ready
e.printStackTrace();
}
// 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 = (Lookup)misl.lookup(Lookup.class);
if (defaultLookup != null) {
return defaultLookup;
}
// You may also specify a Lookup.Provider.
Lookup.Provider prov = (Lookup.Provider)misl.lookup(Lookup.Provider.class);
if (prov != null) {
defaultLookup = Lookups.proxy(prov);
return defaultLookup;
}
// 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(l);
defaultLookup = new ProxyLookup(new Lookup[] {misl, clLookup});
return defaultLookup;
}
/** Empty constructor for use by subclasses. */
public Lookup() {}
/** Look up an object matching a given interface.
* This is the simplest method to use.
* If more than one object matches, one will be returned arbitrarily.
* The template class may be a class or interface; the instance is
* guaranteed to be assignable to it.
*
* @param clazz class of the object we are searching for
* @return an object implementing the given class or <code>null</code> if no such
* implementation is found
*/
public abstract Object lookup (Class clazz);
/** The general lookup method.
* @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 template the template to check
* @return a matching item or <code>null</code>
*
* @since 1.8
*/
public Item lookupItem (Template template) {
Result res = lookup (template);
Iterator it = res.allItems ().iterator ();
return it.hasNext () ? (Item)it.next () : null;
}
/*
* 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 Object instance;
/** General template to find all possible instances.
*/
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 <code>null</code> to leave unspecified
* @param id the ID of the item/service we are looking for or <code>null</code> to leave unspecified
* @param instance a specific known instance to look for or <code>null</code> to leave unspecified
*/
public Template (Class type, String id, Object instance) {
this.type = type == null ? Object.class : type;
this.id = id;
this.instance = instance;
}
/** Get the class (or superclass or interface) to search for.
* If it was not specified in the constructor, <code>Object</code> 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 <code>null</code>
* @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 <code>Item</code> when the instance
* is already known.
*
* @return the object to find or <code>null</code>
*
* @since 1.8
*/
public Object getInstance () {
return instance;
}
/* Computes hashcode for this template. The hashcode is cached.
* @return hashcode
*/
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
*/
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 */
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.
*/
public static abstract class Result extends Object {
/** Registers a listener that is invoked when there is a possible
* change in this result.
*
* @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.
* @return collection of all instances
*/
public abstract java.util.Collection allInstances ();
/** Get all classes represented in the result.
* That is, the set of concrete classes
* used by instances present in the result.
* @return set of <code>Class</code> objects
*
* @since 1.8
*/
public java.util.Set allClasses () {
return java.util.Collections.EMPTY_SET;
}
/** Get all registered items.
* This should include all pairs of instances together
* with their classes, IDs, and so on.
* @return collection of {@link Lookup.Item}
*
* @since 1.8
*/
public java.util.Collection allItems () {
return java.util.Collections.EMPTY_SET;
}
}
/** 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 static abstract class Item extends Object {
/** Get the instance itself.
* @return the instance or null if the instance cannot be created
*/
public abstract Object 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 indentifier 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 */
public String toString () {
return getId ();
}
}
/**
* 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();
}
//
// Implementation of the default lookup
//
private static final class Empty extends Lookup {
Empty() {}
private static final Result NO_RESULT = new Result () {
/** Registers a listener that is invoked when there is a possible
* change in this result.
*
* @param l listener to invoke when there is an change
*/
public void addLookupListener (LookupListener l) {
}
/** Unregisters a listener previously added by addChangeListener.
* @param l the listener
*/
public void removeLookupListener (LookupListener l) {
}
/** Access to all instances in the result.
* @return collection of all instances
*/
public java.util.Collection allInstances () {
return java.util.Collections.EMPTY_LIST;
}
};
/** Lookups an object of given interface. This is the simplest method
* for the lookuping, if more registered objects implement the given
* class any of them can be returned.
*
* @param clazz class of the object we are searching for
* @return the object implementing given class or null if no such
* has been found
*/
public Object lookup(Class clazz) {
return null;
}
/** The general lookup method.
* @param template the template describing the services we are looking for
* @return object containing the results
*/
public Result lookup(Template template) {
return NO_RESULT;
}
}
}