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:
042: package org.openide.util;
043:
044: import java.util.Collection;
045: import java.util.Collections;
046: import java.util.Iterator;
047: import java.util.Set;
048: import org.openide.util.lookup.Lookups;
049: import org.openide.util.lookup.ProxyLookup;
050:
051: /**
052: * A general registry permitting clients to find instances of services
053: * (implementation of a given interface).
054: * This class is inspired by the
055: * <a href="http://www.jini.org/">Jini</a>
056: * registration and lookup mechanism. The difference is that the methods do
057: * not throw checked exceptions (as they usually work only locally and not over the network)
058: * and that the Lookup API concentrates on the lookup, not on the registration
059: * (although {@link Lookup#getDefault} is strongly encouraged to support
060: * {@link Lookups#metaInfServices} for registration in addition to whatever
061: * else it decides to support).
062: * <p>
063: * For a general talk about the idea behind the lookup pattern please see
064: * <UL>
065: * <LI><a href="lookup/doc-files/index.html">The Solution to Communication Between Components</a>
066: * page
067: * <LI>the introduction to the <a href="lookup/doc-files/lookup-api.html">lookup API via
068: * use cases</a>
069: * <LI>the examples of <a href="lookup/doc-files/lookup-spi.html">how to write your own lookup</a>
070: * </UL>
071: *
072: * @see org.openide.util.lookup.AbstractLookup
073: * @see Lookups
074: * @see LookupListener
075: * @see LookupEvent
076: * @author Jaroslav Tulach
077: */
078: public abstract class Lookup {
079: /** A dummy lookup that never returns any results.
080: */
081: public static final Lookup EMPTY = new Empty();
082:
083: /** default instance */
084: private static Lookup defaultLookup;
085:
086: /** Empty constructor for use by subclasses. */
087: public Lookup() {
088: }
089:
090: /** Static method to obtain the global lookup in the whole system.
091: * The actual returned implementation can be different in different
092: * systems, but the default one is based on
093: * {@link org.openide.util.lookup.Lookups#metaInfServices}
094: * with the context classloader of the first caller. Each system is
095: * adviced to honor this and include some form of <code>metaInfServices</code>
096: * implementation in the returned lookup as usage of <code>META-INF/services</code>
097: * is a JDK standard.
098: *
099: * @return the global lookup in the system
100: */
101: public static synchronized Lookup getDefault() {
102: if (defaultLookup != null) {
103: return defaultLookup;
104: }
105:
106: // You can specify a Lookup impl using a system property if you like.
107: String className = System.getProperty("org.openide.util.Lookup" // NOI18N
108: );
109:
110: if ("-".equals(className)) { // NOI18N
111:
112: // Suppress even MetaInfServicesLookup.
113: return EMPTY;
114: }
115:
116: ClassLoader l = Thread.currentThread().getContextClassLoader();
117:
118: try {
119: if (className != null) {
120: defaultLookup = (Lookup) Class.forName(className, true,
121: l).newInstance();
122:
123: return defaultLookup;
124: }
125: } catch (Exception e) {
126: // do not use ErrorManager because we are in the startup code
127: // and ErrorManager might not be ready
128: e.printStackTrace();
129: }
130:
131: // OK, none specified (successfully) in a system property.
132: // Try MetaInfServicesLookup as a default, which may also
133: // have a org.openide.util.Lookup line specifying the lookup.
134: Lookup misl = Lookups.metaInfServices(l);
135: defaultLookup = misl.lookup(Lookup.class);
136:
137: if (defaultLookup != null) {
138: return defaultLookup;
139: }
140:
141: // You may also specify a Lookup.Provider.
142: Lookup.Provider prov = misl.lookup(Lookup.Provider.class);
143:
144: if (prov != null) {
145: defaultLookup = Lookups.proxy(prov);
146:
147: return defaultLookup;
148: }
149:
150: DefLookup def = new DefLookup();
151: def.init(l, misl);
152: return defaultLookup = def;
153: }
154:
155: private static final class DefLookup extends ProxyLookup {
156: public DefLookup() {
157: super (new Lookup[0]);
158: }
159:
160: public void init(ClassLoader loader, Lookup metaInfLookup) {
161: // Had no such line, use simple impl.
162: // It does however need to have ClassLoader available or many things will break.
163: // Use the thread context classloader in effect now.
164: Lookup clLookup = Lookups.singleton(loader);
165: setLookups(new Lookup[] { metaInfLookup, clLookup });
166: }
167: }
168:
169: /** Called from MockServices to reset default lookup in case services change
170: */
171: private static void resetDefaultLookup() {
172: if (defaultLookup instanceof DefLookup) {
173: DefLookup def = (DefLookup) defaultLookup;
174: ClassLoader l = Thread.currentThread()
175: .getContextClassLoader();
176: def.init(l, Lookups.metaInfServices(l));
177: }
178: }
179:
180: /** Look up an object matching a given interface.
181: * This is the simplest method to use.
182: * If more than one object matches, the first will be returned.
183: * The template class may be a class or interface; the instance is
184: * guaranteed to be assignable to it.
185: *
186: * @param clazz class of the object we are searching for
187: * @return an object implementing the given class or <code>null</code> if no such
188: * implementation is found
189: */
190: public abstract <T> T lookup(Class<T> clazz);
191:
192: /** The general lookup method. Callers can get list of all instances and classes
193: * that match the given <code>template</code>, request more info about
194: * them in form of {@link Lookup.Item} and attach a listener to
195: * this be notified about changes. The general interface does not
196: * specify whether subsequent calls with the same template produce new
197: * instance of the {@link Lookup.Result} or return shared instance. The
198: * prefered behaviour however is to return shared one.
199: *
200: * @param template a template describing the services to look for
201: * @return an object containing the results
202: */
203: public abstract <T> Result<T> lookup(Template<T> template);
204:
205: /** Look up the first item matching a given template.
206: * Includes not only the instance but other associated information.
207: * @param template the template to check
208: * @return a matching item or <code>null</code>
209: *
210: * @since 1.8
211: */
212: public <T> Item<T> lookupItem(Template<T> template) {
213: Result<T> res = lookup(template);
214: Iterator<? extends Item<T>> it = res.allItems().iterator();
215: return it.hasNext() ? it.next() : null;
216: }
217:
218: /**
219: * Find a result corresponding to a given class.
220: * Equivalent to calling {@link #lookup(Lookup.Template)} but slightly more convenient.
221: * Subclasses may override this method to produce the same semantics more efficiently.
222: * @param clazz the supertype of the result
223: * @return a live object representing instances of that type
224: * @since org.openide.util 6.10
225: */
226: public <T> Lookup.Result<T> lookupResult(Class<T> clazz) {
227: return lookup(new Lookup.Template<T>(clazz));
228: }
229:
230: /**
231: * Find all instances corresponding to a given class.
232: * Equivalent to calling {@link #lookupResult} and asking for {@link Lookup.Result#allInstances} but slightly more convenient.
233: * Subclasses may override this method to produce the same semantics more efficiently.
234: * @param clazz the supertype of the result
235: * @return all currently available instances of that type
236: * @since org.openide.util 6.10
237: */
238: public <T> Collection<? extends T> lookupAll(Class<T> clazz) {
239: return lookupResult(clazz).allInstances();
240: }
241:
242: /**
243: * Objects implementing interface Lookup.Provider are capable of
244: * and willing to provide a lookup (usually bound to the object).
245: * @since 3.6
246: */
247: public interface Provider {
248: /**
249: * Returns lookup associated with the object.
250: * @return fully initialized lookup instance provided by this object
251: */
252: Lookup getLookup();
253: }
254:
255: /*
256: * I expect this class to grow in the future, but for now, it is
257: * enough to start with something simple.
258: */
259:
260: /** Template defining a pattern to filter instances by.
261: */
262: public static final class Template<T> extends Object {
263: /** cached hash code */
264: private int hashCode;
265:
266: /** type of the service */
267: private Class<T> type;
268:
269: /** identity to search for */
270: private String id;
271:
272: /** instance to search for */
273: private T instance;
274:
275: /** General template to find all possible instances.
276: * @deprecated Use <code>new Template (Object.class)</code> which
277: * is going to be better typed with JDK1.5 templates and should produce
278: * the same result.
279: */
280: @Deprecated
281: public Template() {
282: this (null);
283: }
284:
285: /** Create a simple template matching by class.
286: * @param type the class of service we are looking for (subclasses will match)
287: */
288: public Template(Class<T> type) {
289: this (type, null, null);
290: }
291:
292: /** Constructor to create new template.
293: * @param type the class of service we are looking for or <code>null</code> to leave unspecified
294: * @param id the ID of the item/service we are looking for or <code>null</code> to leave unspecified
295: * @param instance a specific known instance to look for or <code>null</code> to leave unspecified
296: */
297: public Template(Class<T> type, String id, T instance) {
298: this .type = extractType(type);
299: this .id = id;
300: this .instance = instance;
301: }
302:
303: @SuppressWarnings("unchecked")
304: private Class<T> extractType(Class<T> type) {
305: return (type == null) ? (Class<T>) Object.class : type;
306: }
307:
308: /** Get the class (or superclass or interface) to search for.
309: * If it was not specified in the constructor, <code>Object</code> is used as
310: * this will match any instance.
311: * @return the class to search for
312: */
313: public Class<T> getType() {
314: return type;
315: }
316:
317: /** Get the persistent identifier being searched for, if any.
318: * @return the ID or <code>null</code>
319: * @see Lookup.Item#getId
320: *
321: * @since 1.8
322: */
323: public String getId() {
324: return id;
325: }
326:
327: /** Get the specific instance being searched for, if any.
328: * Most useful for finding an <code>Item</code> when the instance
329: * is already known.
330: *
331: * @return the object to find or <code>null</code>
332: *
333: * @since 1.8
334: */
335: public T getInstance() {
336: return instance;
337: }
338:
339: /* Computes hashcode for this template. The hashcode is cached.
340: * @return hashcode
341: */
342: public int hashCode() {
343: if (hashCode != 0) {
344: return hashCode;
345: }
346:
347: hashCode = ((type == null) ? 1 : type.hashCode())
348: + ((id == null) ? 2 : id.hashCode())
349: + ((instance == null) ? 3 : 0);
350:
351: return hashCode;
352: }
353:
354: /* Checks whether two templates represent the same query.
355: * @param obj another template to check
356: * @return true if so, false otherwise
357: */
358: public boolean equals(Object obj) {
359: if (!(obj instanceof Template)) {
360: return false;
361: }
362:
363: Template t = (Template) obj;
364:
365: if (hashCode() != t.hashCode()) {
366: // this is an optimalization - the hashCodes should have been
367: // precomputed
368: return false;
369: }
370:
371: if (type != t.type) {
372: return false;
373: }
374:
375: if (id == null) {
376: if (t.id != null) {
377: return false;
378: }
379: } else {
380: if (!id.equals(t.id)) {
381: return false;
382: }
383: }
384:
385: if (instance == null) {
386: return (t.instance == null);
387: } else {
388: return instance.equals(t.instance);
389: }
390: }
391:
392: /* for debugging */
393: public String toString() {
394: return "Lookup.Template[type=" + type + ",id=" + id
395: + ",instance=" + instance + "]"; // NOI18N
396: }
397: }
398:
399: /** Result of a lookup request.
400: * Allows access to all matching instances at once.
401: * Also permits listening to changes in the result.
402: * Result can contain duplicate items.
403: */
404: public static abstract class Result<T> extends Object {
405: /** Registers a listener that is invoked when there is a possible
406: * change in this result.
407: *
408: * @param l the listener to add
409: */
410: public abstract void addLookupListener(LookupListener l);
411:
412: /** Unregisters a listener previously added.
413: * @param l the listener to remove
414: */
415: public abstract void removeLookupListener(LookupListener l);
416:
417: /** Get all instances in the result. The return value type
418: * should be List instead of Collection, but it is too late to change it.
419: * @return unmodifiable collection of all instances that will never change its content
420: */
421: public abstract Collection<? extends T> allInstances();
422:
423: /** Get all classes represented in the result.
424: * That is, the set of concrete classes
425: * used by instances present in the result.
426: * All duplicate classes will be omitted.
427: * @return unmodifiable set of <code>Class</code> objects that will never change its content
428: *
429: * @since 1.8
430: */
431: public Set<Class<? extends T>> allClasses() {
432: return Collections.emptySet();
433: }
434:
435: /** Get all registered items.
436: * This should include all pairs of instances together
437: * with their classes, IDs, and so on. The return value type
438: * should be List instead of Collection, but it is too late to change it.
439: * @return unmodifiable collection of {@link Lookup.Item} that will never change its content
440: *
441: * @since 1.8
442: */
443: public Collection<? extends Item<T>> allItems() {
444: return Collections.emptyList();
445: }
446: }
447:
448: /** A single item in a lookup result.
449: * This wrapper provides unified access to not just the instance,
450: * but its class, a possible persistent identifier, and so on.
451: *
452: * @since 1.25
453: */
454: public static abstract class Item<T> extends Object {
455: /** Get the instance itself.
456: * @return the instance or null if the instance cannot be created
457: */
458: public abstract T getInstance();
459:
460: /** Get the implementing class of the instance.
461: * @return the class of the item
462: */
463: public abstract Class<? extends T> getType();
464:
465: // XXX can it be null??
466:
467: /** Get a persistent indentifier for the item.
468: * This identifier should uniquely represent the item
469: * within its containing lookup (and if possible within the
470: * global lookup as a whole). For example, it might represent
471: * the source of the instance as a file name. The ID may be
472: * persisted and in a later session used to find the same instance
473: * as was encountered earlier, by means of passing it into a
474: * lookup template.
475: *
476: * @return a string ID of the item
477: */
478: public abstract String getId();
479:
480: /** Get a human presentable name for the item.
481: * This might be used when summarizing all the items found in a
482: * lookup result in some part of a GUI.
483: * @return the string suitable for presenting the object to a user
484: */
485: public abstract String getDisplayName();
486:
487: /* show ID for debugging */
488: public String toString() {
489: return getId();
490: }
491: }
492:
493: //
494: // Implementation of the default lookup
495: //
496: private static final class Empty extends Lookup {
497: private static final Result NO_RESULT = new Result() {
498: public void addLookupListener(LookupListener l) {
499: }
500:
501: public void removeLookupListener(LookupListener l) {
502: }
503:
504: public Collection allInstances() {
505: return Collections.EMPTY_SET;
506: }
507: };
508:
509: Empty() {
510: }
511:
512: public <T> T lookup(Class<T> clazz) {
513: return null;
514: }
515:
516: @SuppressWarnings("unchecked")
517: public <T> Result<T> lookup(Template<T> template) {
518: return NO_RESULT;
519: }
520: }
521: }
|