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.lookup;
043:
044: import java.util.Arrays;
045: import java.util.Collections;
046: import org.netbeans.modules.openide.util.NamedServicesProvider;
047: import org.openide.util.Lookup;
048:
049: /**
050: * Static factory methods for creating common lookup implementations.
051: *
052: * @author David Strupl
053: * @since 2.21
054: */
055: public class Lookups {
056:
057: /** static methods only */
058: private Lookups() {
059: }
060:
061: /**
062: * Creates a singleton lookup. It means lookup that contains only
063: * one object specified via the supplied parameter. The lookup will
064: * either return the object or null if the supplied template does
065: * not match the class. If the specified argument is null the method
066: * will end with NullPointerException.
067: * @return Fully initialized lookup object ready to use
068: * @throws NullPointerException if the supplied argument is null
069: * @since 2.21
070: */
071: public static Lookup singleton(Object objectToLookup) {
072: if (objectToLookup == null) {
073: throw new NullPointerException();
074: }
075:
076: // performance of the resulting lookup might be further
077: // improved by providing specialized singleton result (and lookup)
078: // instead of using SimpleResult
079: return new SimpleLookup(Collections.singleton(objectToLookup));
080: }
081:
082: /**
083: * Creates a lookup that contains an array of objects specified via the
084: * parameter. The resulting lookup is fixed in the following sense: it
085: * contains only fixed set of objects passed in by the array parameter.
086: * Its contents never changes so registering listeners on such lookup
087: * does not have any observable effect (the listeners are never called).
088: *
089: * @param objectsToLookup list of objects to include
090: * @return Fully initialized lookup object ready to use
091: * @throws NullPointerException if the supplied argument is null
092: * @since 2.21
093: *
094: */
095: public static Lookup fixed(Object... objectsToLookup) {
096: if (objectsToLookup == null) {
097: throw new NullPointerException();
098: }
099:
100: return new SimpleLookup(Arrays.asList(objectsToLookup));
101: }
102:
103: /**
104: * Creates a lookup that contains an array of objects specified via the
105: * parameter. The resulting lookup is fixed in the following sense: it
106: * contains only fixed set of objects passed in by the array parameter.
107: * The objects returned from this lookup are converted to real objects
108: * before they are returned by the lookup.
109: * Its contents never changes so registering listeners on such lookup
110: * does not have any observable effect (the listeners are never called).
111: *
112: * @return Fully initialized lookup object ready to use
113: * @throws NullPointerException if the any of the arguments is null
114: * @since 2.21
115: *
116: */
117: public static <T, R> Lookup fixed(T[] keys,
118: InstanceContent.Convertor<? super T, R> convertor) {
119: if (keys == null) {
120: throw new NullPointerException();
121: }
122:
123: if (convertor == null) {
124: throw new NullPointerException();
125: }
126:
127: return new SimpleLookup(Arrays.asList(keys), convertor);
128: }
129:
130: /** Creates a lookup that delegates to another one but that one can change
131: * from time to time. The returned lookup checks every time somebody calls
132: * <code>lookup</code> or <code>lookupItem</code> method whether the
133: * provider still returns the same lookup. If not, it updates state of
134: * all {@link org.openide.util.Lookup.Result}s
135: * that it created (and that still exists).
136: * <P>
137: * The user of this method has to implement its provider's <code>getLookup</code>
138: * method (must be thread safe and fast, will be called often and from any thread)
139: * pass it to this method and use the returned lookup. Whenever the user
140: * changes the return value from the <code>getLookup</code> method and wants
141: * to notify listeners on the lookup about that it should trigger the event
142: * firing, for example by calling <code>lookup.lookup (Object.class)</code>
143: * directly on the lookup returned by this method
144: * that forces a check of the return value of {@link org.openide.util.Lookup.Provider#getLookup}</code>.
145: *
146: * @param provider the provider that returns a lookup to delegate to
147: * @return lookup delegating to the lookup returned by the provider
148: * @since 3.9
149: */
150: public static Lookup proxy(Lookup.Provider provider) {
151: return new SimpleProxyLookup(provider);
152: }
153:
154: /** Returns a lookup that implements the JDK1.3 JAR services mechanism and delegates
155: * to META-INF/services/name.of.class files.
156: * <p>Note: It is not dynamic - so if you need to change the classloader or JARs,
157: * wrap it in a {@link ProxyLookup} and change the delegate when necessary.
158: * Existing instances will be kept if the implementation classes are unchanged,
159: * so there is "stability" in doing this provided some parent loaders are the same
160: * as the previous ones.
161: * @since 3.35
162: */
163: public static Lookup metaInfServices(ClassLoader classLoader) {
164: return new MetaInfServicesLookup(classLoader,
165: "META-INF/services/"); // NOI18N
166: }
167:
168: /** Returns a lookup that behaves exactly as the one
169: * created <code>metaInfServices(ClassLoader)</code> except that
170: * it does not read data from META-INF/services, but instead
171: * from the specified <code>prefix</code>.
172: * @param classLoader class loader to use for loading
173: * @param prefix prefix to prepend to the class name when searching
174: * @since 7.9
175: */
176: public static Lookup metaInfServices(ClassLoader classLoader,
177: String prefix) {
178: return new MetaInfServicesLookup(classLoader, prefix);
179: }
180:
181: /** Creates a <q>named</q> lookup. It is a lookup identified by a
182: * given path. Two lookups with the same path are going to have
183: * the same content. It is expected that each <q>named</q> lookup
184: * will contain a superset of what would lookup created by
185: * <code>metaInfServices(theRightLoader, "META-INF/namedservices/" + path)</code>
186: * contain. However various environments can add their own
187: * extensions to its content. For example when running inside NetBeans Runtime
188: * Container, the content of system file system under the given
189: * <code>path</code> is also present in the returned lookup.
190: * <p>
191: * Read more about the <a href="../doc-files/api.html#folderlookup">usage of this method...</a>
192: *
193: * @param path the path identifying the lookup, for example <q>Databases/</q>, etc.
194: * @return lookup associated with this path
195: * @since 7.9
196: */
197: public static Lookup forPath(String path) {
198: return NamedServicesProvider.find(path);
199: }
200:
201: /** Creates a lookup that wraps another one and filters out instances
202: * of specified classes. If you have a lookup and
203: * you want to remove all instances of ActionMap you can use:
204: * <pre>
205: * l = Lookups.exclude(lookup, ActionMap.class);
206: * </pre>
207: * Then anybody who asks for <code>l.lookup(ActionMap.class)</code> or
208: * subclass will get <code>null</code>. Even if the original lookup contains the
209: * value.
210: * To create empty lookup (well, just an example, otherwise use {@link Lookup#EMPTY}) one could use:
211: * <pre>
212: * Lookup.exclude(anyLookup, Object.class);
213: * </pre>
214: * as any instance in any lookup is of type Object and thus would be excluded.
215: * <p>
216: * The complete behavior can be described as <code>classes</code> being
217: * a barrier. For an object not to be excluded, there has to be an inheritance
218: * path between the queried class and the actual class of the instance,
219: * that is not blocked by any of the excluded classes:
220: * <pre>
221: * interface A {}
222: * interface B {}
223: * class C implements A, B {}
224: * Object c = new C();
225: * Lookup l1 = Lookups.singleton(c);
226: * Lookup l2 = Lookups.exclude(l1, A.class);
227: * assertNull("A is directly excluded", l2.lookup(A.class));
228: * assertEquals("Returns C as A.class is not between B and C", c, l2.lookup(B.class));
229: * </pre>
230: * For more info check the
231: * <a href="http://www.netbeans.org/source/browse/openide/util/test/unit/src/org/openide/util/lookup/ExcludingLookupTest.java">
232: * excluding lookup tests</a> and the discussion in issue
233: * <a href="http://openide.netbeans.org/issues/show_bug.cgi?id=53058">53058</a>.
234: *
235: * @param lookup the original lookup that should be filtered
236: * @param classes array of classes those instances should be excluded
237: * @since 5.4
238: */
239: public static Lookup exclude(Lookup lookup, Class... classes) {
240: return new ExcludingLookup(lookup, classes);
241: }
242:
243: /** Creates <code>Lookup.Item</code> representing the instance passed in.
244: *
245: * @param instance the object for which Lookup.Item should be creted
246: * @param id unique identification of the object, for details see {@link org.openide.util.Lookup.Item#getId},
247: * can be <code>null</code>
248: * @return lookup item representing instance
249: * @since 4.8
250: */
251: public static <T> Lookup.Item<T> lookupItem(T instance, String id) {
252: return new LookupItem<T>(instance, id);
253: }
254:
255: private static class LookupItem<T> extends Lookup.Item<T> {
256: private String id;
257: private T instance;
258:
259: public LookupItem(T instance) {
260: this (instance, null);
261: }
262:
263: public LookupItem(T instance, String id) {
264: this .id = id;
265: this .instance = instance;
266: }
267:
268: public String getDisplayName() {
269: return getId();
270: }
271:
272: public String getId() {
273: return (id == null) ? instance.toString() : id;
274: }
275:
276: public T getInstance() {
277: return instance;
278: }
279:
280: @SuppressWarnings("unchecked")
281: public Class<? extends T> getType() {
282: return (Class<? extends T>) instance.getClass();
283: }
284:
285: public boolean equals(Object object) {
286: if (object instanceof LookupItem) {
287: return instance == ((LookupItem) object).getInstance();
288: }
289:
290: return false;
291: }
292:
293: public int hashCode() {
294: return instance.hashCode();
295: }
296: }
297: // End of LookupItem class
298: }
|