--- a/openide.filesystems/src/org/netbeans/modules/openide/filesystems/RecognizeInstanceFiles.java +++ a/openide.filesystems/src/org/netbeans/modules/openide/filesystems/RecognizeInstanceFiles.java @@ -68,29 +68,31 @@ private static final Logger LOG = Logger.getLogger(RecognizeInstanceFiles.class.getName()); - public Lookup create(String path) { - return new OverFiles(path); + public Lookup create(String path, Lookups.RecursionMode mode) { + return new OverFiles(path, mode); } private static final class OverFiles extends ProxyLookup implements FileChangeListener { private final String path; + private final Lookups.RecursionMode mode; private final FileChangeListener weakL; private final AbstractLookup.Content content; private final AbstractLookup lkp; - public OverFiles(String path) { - this(path, new ArrayList(), new AbstractLookup.Content()); + public OverFiles(String path, Lookups.RecursionMode mode) { + this(path, mode, new ArrayList(), new AbstractLookup.Content()); } - private OverFiles(String path, List items, AbstractLookup.Content cnt) { - this(path, items, new AbstractLookup(cnt), cnt); + private OverFiles(String path, Lookups.RecursionMode mode, List items, AbstractLookup.Content cnt) { + this(path, mode, items, new AbstractLookup(cnt), cnt); } - private OverFiles(String path, List items, AbstractLookup lkp, AbstractLookup.Content cnt) { - super(computeDelegates(path, items, lkp)); + private OverFiles(String path, Lookups.RecursionMode mode, List items, AbstractLookup lkp, AbstractLookup.Content cnt) { + super(computeDelegates(path, mode, items, lkp)); this.path = path; + this.mode = mode; this.lkp = lkp; this.content = cnt; this.content.setPairs(order(items)); @@ -122,22 +124,30 @@ return; } List items = new ArrayList(); - Lookup[] delegates = computeDelegates(path, items, lkp); + Lookup[] delegates = computeDelegates(path, mode, items, lkp); this.content.setPairs(order(items)); this.setLookups(delegates); } - private static Lookup[] computeDelegates(String p, List items, Lookup lkp) { + private static Lookup[] computeDelegates(String p, Lookups.RecursionMode mode, List items, Lookup lkp) { FileObject fo = FileUtil.getConfigFile(p); List delegates = new LinkedList(); delegates.add(lkp); if (fo != null) { - for (FileObject f : fo.getChildren()) { - if (f.isFolder()) { - delegates.add(new OverFiles(f.getPath())); - } else { - if (f.hasExt("instance") || f.getAttribute("instanceCreate") != null) { - items.add(new FOItem(f)); + switch (mode) { + case FILE: + items.add(new FOItem(fo)); + break; + default: + for (FileObject f : fo.getChildren()) { + if (f.isFolder()) { + if (mode != Lookups.RecursionMode.NONRECURSIVE_FOLDER) { + delegates.add(new OverFiles(f.getPath(), mode)); + } + } else { + if (f.hasExt("instance") || f.getAttribute("instanceCreate") != null) { + items.add(new FOItem(f)); + } } } } --- a/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/RecognizeInstanceFilesTest.java +++ a/openide.filesystems/test/unit/src/org/netbeans/modules/openide/filesystems/RecognizeInstanceFilesTest.java @@ -221,4 +221,25 @@ public static final class Shared extends SharedClassObject {} + public void testLoadSingleFile() throws Exception { + String path = "inst/class/" + Inst.class.getName().replace('.', '-') + ".instance"; + FileUtil.createData(root, path); + Lookup l = Lookups.forPath(path, Lookups.RecursionMode.FILE); + assertNotNull("Instance created", l.lookup(Inst.class)); + assertEquals(1, l.lookupAll(Object.class).size()); + } + + public void testFolderRecursion() throws Exception { + FileUtil.createData(root, "parent/" + Inst.class.getName().replace('.', '-') + ".instance"); + FileUtil.createData(root, "parent/child/" + Shared.class.getName().replace('.', '-') + ".instance"); + Lookup l = Lookups.forPath("parent", Lookups.RecursionMode.NONRECURSIVE_FOLDER); + assertEquals(1, l.lookupAll(Object.class).size()); + l = Lookups.forPath("parent/", Lookups.RecursionMode.NONRECURSIVE_FOLDER); + assertEquals(1, l.lookupAll(Object.class).size()); + l = Lookups.forPath("parent", Lookups.RecursionMode.RECURSIVE_FOLDER); + assertEquals(2, l.lookupAll(Object.class).size()); + l = Lookups.forPath("parent/", Lookups.RecursionMode.RECURSIVE_FOLDER); + assertEquals(2, l.lookupAll(Object.class).size()); + } + } --- a/openide.util/src/org/netbeans/modules/openide/util/NamedServicesProvider.java +++ a/openide.util/src/org/netbeans/modules/openide/util/NamedServicesProvider.java @@ -48,21 +48,22 @@ private static final Map> map = Collections.synchronizedMap(new HashMap>()); - public abstract Lookup create(String path); + public abstract Lookup create(String path, Lookups.RecursionMode mode); - public static Lookup find(String path) { - if (!path.endsWith("/")) { + public static Lookup find(String path, Lookups.RecursionMode mode) { + if (!path.endsWith("/") && mode != Lookups.RecursionMode.FILE) { path = path + "/"; } - Reference ref = map.get(path); + String key = mode + "/" + path; + Reference ref = map.get(key); Lookup lkp = ref == null ? null : ref.get(); if (lkp != null) { return lkp; } NamedServicesProvider prov = Lookup.getDefault().lookup(NamedServicesProvider.class); if (prov != null) { - lkp = prov.create(path); + lkp = prov.create(path, mode); } else { ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class); if (l == null) { @@ -71,10 +72,15 @@ l = NamedServicesProvider.class.getClassLoader(); } } + switch (mode) { + case RECURSIVE_FOLDER: + case FILE: + throw new UnsupportedOperationException("metaInfServices does not support " + mode); + } lkp = Lookups.metaInfServices(l, "META-INF/namedservices/" + path); } - map.put(path, new WeakReference(lkp)); + map.put(key, new WeakReference(lkp)); return lkp; } --- a/openide.util/src/org/openide/util/Utilities.java +++ a/openide.util/src/org/openide/util/Utilities.java @@ -2823,7 +2823,7 @@ */ public static List actionsForPath(String path) { List actions = new ArrayList(); - for (Lookup.Item item : Lookups.forPath(path).lookupResult(Object.class).allItems()) { + for (Lookup.Item item : Lookups.forPath(path, Lookups.RecursionMode.NONRECURSIVE_FOLDER).lookupResult(Object.class).allItems()) { if (Action.class.isAssignableFrom(item.getType())) { Object instance = item.getInstance(); if (instance != null) { --- a/openide.util/src/org/openide/util/lookup/Lookups.java +++ a/openide.util/src/org/openide/util/lookup/Lookups.java @@ -44,6 +44,7 @@ import java.util.Arrays; import org.netbeans.modules.openide.util.NamedServicesProvider; import org.openide.util.Lookup; +import org.openide.util.Parameters; /** * Static factory methods for creating common lookup implementations. @@ -205,6 +206,7 @@ * Please note that these lookups differ in the way they inspect sub-folders. * The first lookup just returns instances from the given path, ignoring * sub-folders, the second one retrieves instances from the whole sub-tree. + * To better control the behavior, use {@link #forPath(String, RecursionMode)}. *

*

* Read more about the usage of this method. @@ -214,7 +216,40 @@ * @since 7.9 */ public static Lookup forPath(String path) { - return NamedServicesProvider.find(path); + return forPath(path, RecursionMode.UNSPECIFIED_FOLDER); + } + + /** + * Creates a named lookup as in {@link #forPath(String)} but specifying a recursion mode. + * @param path a folder or file path + * @param mode how to deal with folders and files + * @return a lookup associated with the path + * @throws UnsupportedOperationException if mode is not {@link Lookups.RecursionMode#UNSPECIFIED_FOLDER} + * and the active provider cannot handle this recursion mode + * @since XXX + */ + public static Lookup forPath(String path, RecursionMode mode) throws UnsupportedOperationException { + Parameters.notNull("path", path); + Parameters.notNull("mode", mode); + return NamedServicesProvider.find(path, mode); + } + + /** + * Controls how files and folders are interpreted by {@link #forPath(String, RecursionMode)}. + * @since XXX + */ + public enum RecursionMode { + /** + * Default mode for compatibility: behavior is dependent on the provider + * (may be like {@link #NONRECURSIVE_FOLDER} or like {@link #RECURSIVE_FOLDER}). + */ + UNSPECIFIED_FOLDER, + /** The path is a folder and instances will be searched for in direct children of the folder only. */ + NONRECURSIVE_FOLDER, + /** The path is a folder and instances will be searched for in all recursive descendants of the folder. */ + RECURSIVE_FOLDER, + /** The path is a single file to be treated as an instance. */ + FILE; } /** Creates a lookup that wraps another one and filters out instances --- a/openide.util/test/unit/src/org/openide/util/UtilitiesTest.java +++ a/openide.util/test/unit/src/org/openide/util/UtilitiesTest.java @@ -284,7 +284,7 @@ public void testActionsForPath() throws Exception { MockLookup.setInstances(new NamedServicesProvider() { - public Lookup create(String path) { + public Lookup create(String path, Lookups.RecursionMode _ignore) { if (!path.equals("stuff/")) { return Lookup.EMPTY; } --- a/openide.util/test/unit/src/org/openide/util/lookup/PathInLookupTest.java +++ a/openide.util/test/unit/src/org/openide/util/lookup/PathInLookupTest.java @@ -94,7 +94,7 @@ @Override - public Lookup create(String path) { + public Lookup create(String path, Lookups.RecursionMode _ignore) { int indx = -1; if (path.equals("MyServices/")) { indx = 0; --- a/settings/src/org/netbeans/modules/settings/RecognizeInstanceObjects.java +++ a/settings/src/org/netbeans/modules/settings/RecognizeInstanceObjects.java @@ -36,13 +36,19 @@ import java.util.Collections; import java.util.logging.Logger; import org.netbeans.modules.openide.util.NamedServicesProvider; +import org.openide.cookies.InstanceCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataFolder; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObject.Container; +import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; import org.openide.util.WeakListeners; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; @@ -55,8 +61,8 @@ private static final Logger LOG = Logger.getLogger(RecognizeInstanceObjects.class.getName()); - public Lookup create(String path) { - return new OverObjects(path); + public Lookup create(String path, Lookups.RecursionMode mode) { + return new OverObjects(path, mode); } @@ -65,15 +71,17 @@ private static Lookup.Result CL = Lookup.getDefault().lookupResult(ClassLoader.class); private final String path; + private final Lookups.RecursionMode mode; - public OverObjects(String path) { - super(delegates(path)); + public OverObjects(String path, Lookups.RecursionMode mode) { + super(delegates(path, mode)); this.path = path; + this.mode = mode; CL.addLookupListener(WeakListeners.create(LookupListener.class, this, CL)); } @SuppressWarnings("deprecation") - private static Lookup[] delegates(String path) { + private static Lookup[] delegates(final String path, final Lookups.RecursionMode mode) { Collection allCL = CL.allInstances(); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl != null) { @@ -84,6 +92,41 @@ } } try { + if (mode == Lookups.RecursionMode.FILE) { + FileObject fo = FileUtil.getConfigFile(path); + if (fo != null) { + InstanceCookie cookie = DataObject.find(fo).getLookup().lookup(InstanceCookie.class); + if (cookie != null) { + InstanceContent ic = new InstanceContent(); + ic.add(cookie, new InstanceContent.Convertor() { + public Object convert(InstanceCookie cookie) { + try { + return cookie.instanceCreate(); + } catch (Exception x) { + Exceptions.printStackTrace(x); + return null; + } + } + public Class type(InstanceCookie cookie) { + try { + return cookie.instanceClass(); + } catch (Exception x) { + Exceptions.printStackTrace(x); + return Object.class; + } + } + public String id(InstanceCookie cookie) { + return path; + } + public String displayName(InstanceCookie cookie) { + return path; + } + }); + return new Lookup[] {new AbstractLookup(ic)}; + } + } + return new Lookup[0]; + } FileObject fo = FileUtil.createFolder(FileUtil.getConfigRoot(), path); String s; @@ -94,7 +137,14 @@ } org.openide.loaders.FolderLookup l; - l = new org.openide.loaders.FolderLookup(DataFolder.findFolder(fo), s); + l = new org.openide.loaders.FolderLookup(DataFolder.findFolder(fo), s) { + protected @Override InstanceCookie acceptFolder(DataFolder df) { + return mode == Lookups.RecursionMode.NONRECURSIVE_FOLDER ? null : super.acceptFolder(df); + } + protected @Override InstanceCookie acceptContainer(Container df) { + return mode == Lookups.RecursionMode.NONRECURSIVE_FOLDER ? null : super.acceptContainer(df); + } + }; return new Lookup[] { l.getLookup(), Lookups.metaInfServices(allCL.iterator().next(), "META-INF/namedservices/" + path) }; // NOI18N } catch (IOException ex) { return new Lookup[] { Lookups.metaInfServices(allCL.iterator().next(), "META-INF/namedservices/" + path) }; // NOI18N @@ -103,7 +153,7 @@ } public void resultChanged(LookupEvent ev) { - setLookups(delegates(path)); + setLookups(delegates(path, mode)); } } // end of OverObjects }