blob: 8e9a2e3e791e5dc55f925b4fef91060fe404cfd8 [file] [log] [blame]
/*
* 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.implspi;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
/** Infrastructure provider interface for those who control the overall
* registration of services in the system. The first instance of this interface
* found in {@link Lookup#getDefault()} is consulted when providing answers
* to {@link Lookups#forPath(java.lang.String)} queries. Current implementation
* is not ready for multiple instances of this interface (the first one wins)
* and also changing the instances during runtime.
*
* <div class="nonnormative">
* The basic implementation of this interface is provided in
* <a href="@org-openide-filesystems@/overview-summary.html">Filesystem API</a>
* and recognizes the
* <a href="@org-openide-util@/org/openide/util/doc-files/api.html#instance-folders">.instance files</a>
* registered in XML layers. As such one can rely on
* <a href="@org-openide-util@/org/openide/util/doc-files/api.html#instance-folders">.instance files</a>
* being recognized in unit tests, if the
* <a href="@org-openide-filesystems@/overview-summary.html">Filesystem API</a>
* is included.
* The implementation
* is then refined in
* <a href="@org-netbeans-modules-settings@/overview-summary.html">Settings API</a>
* to handle also <a href="@org-openide-util@/org/openide/util/doc-files/api.html#settings">.settings files</a>.
* Again, including this module in unit tests
* ensures
* <a href="@org-openide-util@/org/openide/util/doc-files/api.html#settings">.settings files</a>
* files are recognized.
* </div>
*
* @author Jaroslav Tulach
* @since 8.1
*/
public abstract class NamedServicesProvider {
private static final Map<String,Reference<Lookup>> namedServicesProviders = Collections.synchronizedMap(new HashMap<String,Reference<Lookup>>());
private static ThreadLocal<Boolean> IN = new ThreadLocal<Boolean>();
public static Lookup forPath(String path) {
Reference<Lookup> ref = namedServicesProviders.get(path);
Lookup lkp = ref == null ? null : ref.get();
if (lkp != null) {
return lkp;
}
NamedServicesProvider prov = Lookup.getDefault().lookup(NamedServicesProvider.class);
if (prov != null && IN.get() == null) {
IN.set(true);
try {
lkp = prov.create(path);
} finally {
IN.set(null);
}
} else {
ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class);
if (l == null) {
l = Thread.currentThread().getContextClassLoader();
if (l == null) {
l = NamedServicesProvider.class.getClassLoader();
}
}
lkp = Lookups.metaInfServices(l, "META-INF/namedservices/" + path);
}
namedServicesProviders.put(path, new WeakReference<Lookup>(lkp));
return lkp;
}
/** Finds a config object under given path.
* @param filePath path to .instance or .settings file
* @param type the requested type for given object
* @return either null or instance of requested type
* @since 8.10
*/
public static <T> T getConfigObject(String filePath, Class<T> type) {
NamedServicesProvider prov = Lookup.getDefault().lookup(NamedServicesProvider.class);
return prov != null ? prov.lookupObject(filePath, type) : null;
}
/** Allows the providers to donate a special lookup for a given object.
* Right now it is used only for obtaining <code>FileObject.getLookup</code>.
*
* @param obj the object to find lookup for
* @return <code>null</code> or new lookup to be associated with the <code>obj</code>
* @since 8.17
*/
public static Lookup createLookupFor(Object obj) {
NamedServicesProvider prov = Lookup.getDefault().lookup(NamedServicesProvider.class);
return prov != null ? prov.lookupFor(obj) : null;
}
static void clearCache() {
boolean assertsOn = false;
assert assertsOn = true;
if (!assertsOn) {
throw new IllegalStateException();
}
namedServicesProviders.clear();
}
/** Throws an exception. Prevents unwanted instantiation of this class
* by unknown subclasses.
*/
protected NamedServicesProvider() {
if (getClass().getName().equals("org.openide.util.lookup.PathInLookupTest$P")) { // NOI18N
// OK for tests
return;
}
if (getClass().getName().equals("org.openide.util.UtilitiesTest$NamedServicesProviderImpl")) { // NOI18N
// OK for tests
return;
}
if (getClass().getName().equals("org.netbeans.modules.openide.filesystems.RecognizeInstanceFiles")) { // NOI18N
// OK for openide.filesystems
return;
}
if (getClass().getName().equals("org.netbeans.modules.settings.RecognizeInstanceObjects")) { // NOI18N
// OK for settings
return;
}
throw new IllegalStateException();
}
/** Create the lookup for given path. Called as a result of query to
* {@link Lookups#forPath(java.lang.String)}.
*
* @param path the identification of the path
* @return the lookup representing objects in this path.
*/
protected abstract Lookup create(String path);
/** Finds a config object under given path. Called from {@link FileUtil#getConfigObject}.
* @param filePath path to .instance or .settings file
* @param type the requested type for given object
* @return either null or instance of requested type
* @since 8.10
*/
protected <T> T lookupObject(String path, Class<T> type) {
return create(path).lookup(type);
}
/** Method for providers to work in orchestration with {@link #createLookupFor(java.lang.Object)}.
* By default return <code>null</code>.
*
* @param obj the object to find lookup for
* @return <code>null</code> or new lookup to be associated with the <code>obj</code>
* @since 8.17
*/
protected Lookup lookupFor(Object obj) {
return null;
}
}