Index: api/doc/org/openide/doc-files/services-api.html =================================================================== RCS file: /cvs/openide/api/doc/org/openide/doc-files/services-api.html,v retrieving revision 1.34 diff -u -r1.34 services-api.html --- api/doc/org/openide/doc-files/services-api.html 17 Mar 2004 15:03:20 -0000 1.34 +++ api/doc/org/openide/doc-files/services-api.html 30 Apr 2004 10:22:26 -0000 @@ -853,7 +853,7 @@

- The Lookup supports a small extension to the + The Lookup supports two small extensions to the JDK's standard. It allows a module to remove class registered by another one. That is why it is possible to write a module that @@ -871,6 +871,22 @@ compatibility with JDK's implementation. The # means comment and thus JDK will not interpret the line and will not get confused by the - before class name. + +

Second extension allows ordering of items. The class implementing + the interface can be followed by advisory position attribute. When + querying on an interface, items with a smaller position are guaranteed + to be returned before items with a larger position. Items with no defined + position are returned last. Example of content of + META-INF/services/org.me.MyService + file could be: +

+  org.you.MyServiceImpl
+  #position=10
+    
+ It is recommended to pick up larger numbers so that there is + gap for other modules if they need to get in front of your item. And, + again, to keep compatibility the position attribute must starts with + comment delimiter.

Index: src/org/openide/util/lookup/MetaInfServicesLookup.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/lookup/MetaInfServicesLookup.java,v retrieving revision 1.6 diff -u -r1.6 MetaInfServicesLookup.java --- src/org/openide/util/lookup/MetaInfServicesLookup.java 5 Oct 2003 13:17:43 -0000 1.6 +++ src/org/openide/util/lookup/MetaInfServicesLookup.java 30 Apr 2004 10:22:38 -0000 @@ -113,7 +113,7 @@ // has the same entry in it (and they load to the same class). // Probably would not happen, assuming JARs only list classes // they own, but just in case... - Collection foundClasses = new ArrayList (); // Collection + List/**/ foundClasses = new ArrayList(); Collection removeClasses = new ArrayList (); // Collection boolean foundOne = false; @@ -152,6 +152,7 @@ } URL url = (URL)en.nextElement(); + Item currentItem = null; try { InputStream is = url.openStream(); try { @@ -160,8 +161,26 @@ String line = reader.readLine(); if (line == null) break; - // Ignore blank lines and comments. line = line.trim(); + // is it position attribute? + if (line.startsWith("#position=")) { + if (currentItem == null) { + assert false : "Found line '"+line+"' but there is no item to associate it with!"; + } + try { + currentItem.position = Integer.parseInt(line.substring(10)); + } catch (NumberFormatException e) { + // do not use ErrorManager because we are in the startup code + // and ErrorManager might not be ready + e.printStackTrace(); + } + } + if (currentItem != null) { + insertItem(currentItem, foundClasses); + currentItem = null; + } + + // Ignore blank lines and comments. if (line.length() == 0) continue; boolean remove = false; @@ -196,9 +215,17 @@ if (remove) { removeClasses.add (inst); } else { - foundClasses.add(inst); + // create new item here, but do not put it into + // foundClasses array yet because following line + // might specify its position + currentItem = new Item(); + currentItem.clazz = inst; } } + if (currentItem != null) { + insertItem(currentItem, foundClasses); + currentItem = null; + } } finally { is.close(); } @@ -217,9 +244,44 @@ foundClasses.removeAll (removeClasses); Iterator it = foundClasses.iterator (); while (it.hasNext ()) { - Class inst = (Class)it.next (); - result.add(new P(inst)); + Item item = (Item)it.next (); + if (removeClasses.contains(item.clazz)) { + continue; + } + result.add(new P(item.clazz)); + } + } + + /** + * Insert item to the list according to item.position value. + */ + private void insertItem(Item item, List list) { + // no position? -> add it to the end + if (item.position == -1) { + list.add(item); + return; + } + int index = -1; + Iterator it = list.iterator(); + while (it.hasNext()) { + index++; + Item i = (Item)it.next(); + if (i.position == -1) { + list.add(index, item); + return; + } else { + if (i.position > item.position) { + list.add(index, item); + return; + } + } } + list.add(item); + } + + private static class Item { + private Class clazz; + private int position = -1; } /** Pair that holds name of a class and maybe the instance. Index: test/unit/src/org/openide/util/MetaInfServicesLookupTest.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/util/MetaInfServicesLookupTest.java,v retrieving revision 1.6 diff -u -r1.6 MetaInfServicesLookupTest.java --- test/unit/src/org/openide/util/MetaInfServicesLookupTest.java 11 Sep 2003 15:56:47 -0000 1.6 +++ test/unit/src/org/openide/util/MetaInfServicesLookupTest.java 30 Apr 2004 10:22:42 -0000 @@ -100,6 +100,30 @@ assertNull ("services1.jar defines Runnable, but services2.jar masks it out", l4.lookup (Runnable.class)); } + public void testOrdering() throws Exception { + Lookup l = Lookups.metaInfServices(c1); + Class xface = c1.loadClass("java.util.Comparator"); + List results = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances()); + assertEquals(1, results.size()); + + l = Lookups.metaInfServices(c2); + xface = c2.loadClass("java.util.Comparator"); + results = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances()); + assertEquals(2, results.size()); + // Test order: + assertEquals("org.bar.Comparator2", results.get(0).getClass().getName()); + assertEquals("org.foo.impl.Comparator1", results.get(1).getClass().getName()); + + // test that items without position are always at the end + l = Lookups.metaInfServices(c2); + xface = c2.loadClass("java.util.Iterator"); + results = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances()); + assertEquals(2, results.size()); + // Test order: + assertEquals("org.bar.Iterator2", results.get(0).getClass().getName()); + assertEquals("org.foo.impl.Iterator1", results.get(1).getClass().getName()); + } + public void testListenersAreNotifiedWithoutHoldingALockIssue36035 () throws Exception { final Lookup l = Lookups.metaInfServices(c2); final Class xface = c1.loadClass("org.foo.Interface"); Index: test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/java.util.Comparator =================================================================== RCS file: test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/java.util.Comparator diff -N test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/java.util.Comparator --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/java.util.Comparator 30 Apr 2004 10:22:42 -0000 @@ -0,0 +1,4 @@ +#some comment +org.foo.impl.Comparator1 +#position=10 +#som comment2 Index: test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/java.util.Iterator =================================================================== RCS file: test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/java.util.Iterator diff -N test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/java.util.Iterator --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/java.util.Iterator 30 Apr 2004 10:22:42 -0000 @@ -0,0 +1 @@ +org.foo.impl.Iterator1 Index: test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Comparator1.java =================================================================== RCS file: test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Comparator1.java diff -N test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Comparator1.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Comparator1.java 30 Apr 2004 10:22:42 -0000 @@ -0,0 +1,7 @@ + +package org.foo.impl; + +public class Comparator1 implements java.util.Comparator { + public int compare(Object o1, Object o2) {return 0;} + public boolean equals(Object obj) {return true;} +} Index: test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Iterator1.java =================================================================== RCS file: test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Iterator1.java diff -N test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Iterator1.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Iterator1.java 30 Apr 2004 10:22:42 -0000 @@ -0,0 +1,11 @@ + +package org.foo.impl; + +public class Iterator1 implements java.util.Iterator { + public boolean hasNext() {return false;} + + public Object next() {return null;} + + public void remove() {} + +} Index: test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/java.util.Comparator =================================================================== RCS file: test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/java.util.Comparator diff -N test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/java.util.Comparator --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/java.util.Comparator 30 Apr 2004 10:22:42 -0000 @@ -0,0 +1,2 @@ +org.bar.Comparator2 +#position=5 Index: test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/java.util.Iterator =================================================================== RCS file: test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/java.util.Iterator diff -N test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/java.util.Iterator --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/java.util.Iterator 30 Apr 2004 10:22:42 -0000 @@ -0,0 +1,2 @@ +org.bar.Iterator2 +#position=100 Index: test/unit/src/org/openide/util/data/services-jar-2/org/bar/Comparator2.java =================================================================== RCS file: test/unit/src/org/openide/util/data/services-jar-2/org/bar/Comparator2.java diff -N test/unit/src/org/openide/util/data/services-jar-2/org/bar/Comparator2.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/data/services-jar-2/org/bar/Comparator2.java 30 Apr 2004 10:22:42 -0000 @@ -0,0 +1,7 @@ + +package org.bar; + +public class Comparator2 implements java.util.Comparator { + public int compare(Object o1, Object o2) {return 0;} + public boolean equals(Object obj) {return true;} +} Index: test/unit/src/org/openide/util/data/services-jar-2/org/bar/Iterator2.java =================================================================== RCS file: test/unit/src/org/openide/util/data/services-jar-2/org/bar/Iterator2.java diff -N test/unit/src/org/openide/util/data/services-jar-2/org/bar/Iterator2.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/openide/util/data/services-jar-2/org/bar/Iterator2.java 30 Apr 2004 10:22:42 -0000 @@ -0,0 +1,11 @@ + +package org.bar; + +public class Iterator2 implements java.util.Iterator { + public boolean hasNext() {return false;} + + public Object next() {return null;} + + public void remove() {} + +}