--- /dev/null
+<?xml version="1.0" ?>
+
+<project name="jglob" default="jar">
+ <target name="build-env">
+ <mkdir dir="build" />
+ <mkdir dir="build/classes" />
+ </target>
+
+ <target name="classes" depends="build-env">
+ <javac srcdir="src" destdir="build/classes" debug="on"
+ source="1.5" target="1.5" includeantruntime="no">
+ <!-- <compilerarg value="-Xbarda" /> -->
+ </javac>
+ </target>
+
+ <target name="jar" depends="build-env,classes">
+ <jar destfile="build/jglob.jar" update="true">
+ <fileset dir="build/classes" />
+ <service type="javax.annotation.processing.Processor" provider="dolda.jglob.Collector" />
+ </jar>
+ </target>
+
+ <target name="clean">
+ <delete dir="build" />
+ </target>
+</project>
--- /dev/null
+package dolda.jglob;
+
+import java.util.*;
+import java.io.*;
+import javax.annotation.processing.*;
+import javax.tools.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.lang.model.util.*;
+
+@SupportedAnnotationTypes({"*"})
+@SupportedSourceVersion(SourceVersion.RELEASE_5)
+public class Collector extends AbstractProcessor {
+ private ProcessingEnvironment cfg;
+ private Elements eu;
+
+ public void init(ProcessingEnvironment cfg) {
+ this.cfg = cfg;
+ eu = cfg.getElementUtils();
+ }
+
+ private String tn(TypeElement el) {
+ return(eu.getBinaryName(el).toString());
+ }
+
+ private Set<String> getprev(TypeElement annotation) {
+ Set<String> prev = new HashSet<String>();
+ try {
+ FileObject lf = cfg.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/glob/" + tn(annotation));
+ InputStream in;
+ try {
+ in = lf.openInputStream();
+ } catch(FileNotFoundException e) {
+ return(prev);
+ }
+ try {
+ BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"));
+ String ln;
+ while((ln = r.readLine()) != null)
+ prev.add(ln);
+ return(prev);
+ } finally {
+ in.close();
+ }
+ } catch(IOException e) {
+ cfg.getMessager().printMessage(Diagnostic.Kind.ERROR, "could not read previous globlist for " + tn(annotation) + ": " + e);
+ return(Collections.emptySet());
+ }
+ }
+
+ private void writenew(TypeElement annotation, Collection<String> names) {
+ try {
+ FileObject lf = cfg.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/glob/" + tn(annotation));
+ OutputStream out = lf.openOutputStream();
+ try {
+ Writer w = new BufferedWriter(new OutputStreamWriter(out, "utf-8"));
+ for(String nm : names)
+ w.write(nm + "\n");
+ w.flush();
+ } finally {
+ out.close();
+ }
+ } catch(IOException e) {
+ cfg.getMessager().printMessage(Diagnostic.Kind.ERROR, "could not write new globlist for " + tn(annotation) + ": " + e);
+ }
+ }
+
+ private void process(TypeElement annotation, RoundEnvironment round, TypeMap types) {
+ Set<String> prev = getprev(annotation);
+ Set<String> carry = new HashSet<String>(prev);
+ Set<String> found = new HashSet<String>();
+ for(Element e : round.getElementsAnnotatedWith(annotation)) {
+ if(!(e instanceof TypeElement)) {
+ cfg.getMessager().printMessage(Diagnostic.Kind.ERROR, tn(annotation) + " must annotate types", e);
+ continue;
+ }
+ TypeElement type = (TypeElement)e;
+ String nm = tn(type);
+ if(!prev.contains(nm))
+ cfg.getMessager().printMessage(Diagnostic.Kind.NOTE, "added " + nm, type);
+ found.add(nm);
+ carry.remove(nm);
+ }
+ for(Iterator<String> i = carry.iterator(); i.hasNext();) {
+ String nm = i.next();
+ TypeElement el = types.get(nm);
+ if(el != null) {
+ i.remove();
+ cfg.getMessager().printMessage(Diagnostic.Kind.NOTE, "removed " + nm, el);
+ }
+ }
+ List<String> all = new ArrayList<String>();
+ all.addAll(carry);
+ all.addAll(found);
+ Collections.sort(all);
+ writenew(annotation, all);
+ }
+
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment round) {
+ for(TypeElement a : annotations) {
+ if(a.getAnnotation(Discoverable.class) != null)
+ process(a, round, new TypeMap(round.getRootElements(), eu));
+ }
+ return(false);
+ }
+}
--- /dev/null
+package dolda.jglob;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface Discoverable {
+}
--- /dev/null
+package dolda.jglob;
+
+public class GlobAccessException extends RuntimeException {
+ public GlobAccessException(Throwable cause) {
+ super(cause);
+ }
+}
--- /dev/null
+package dolda.jglob;
+
+public class GlobInstantiationException extends RuntimeException {
+ public GlobInstantiationException(Throwable cause) {
+ super(cause);
+ }
+}
--- /dev/null
+package dolda.jglob;
+
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import java.lang.annotation.*;
+
+public class Loader {
+ private final Class<? extends Annotation> an;
+ private final ClassLoader cl;
+ private final Map<Class<?>, Object> instances = new HashMap<Class<?>, Object>();
+
+ private Loader(Class<? extends Annotation> annotation, ClassLoader loader) {
+ this.an = annotation;
+ this.cl = loader;
+ }
+
+ public Iterable<String> names() {
+ return(new Iterable<String>() {
+ public Iterator<String> iterator() {
+ return(new Iterator<String>() {
+ private Enumeration<URL> rls;
+ private Iterator<String> cur = null;
+
+ private Iterator<String> parse(URL url) {
+ try {
+ List<String> buf = new LinkedList<String>();
+ InputStream in = url.openStream();
+ try {
+ BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"));
+ String ln;
+ while((ln = r.readLine()) != null) {
+ ln = ln.trim();
+ if(ln.length() < 1)
+ continue;
+ buf.add(ln);
+ }
+ return(buf.iterator());
+ } finally {
+ in.close();
+ }
+ } catch(IOException e) {
+ throw(new GlobAccessException(e));
+ }
+ }
+
+ public boolean hasNext() {
+ if((cur == null) || !cur.hasNext()) {
+ if(rls == null) {
+ try {
+ rls = cl.getResources("META-INF/glob/" + an.getName());
+ } catch(IOException e) {
+ throw(new GlobAccessException(e));
+ }
+ }
+ if(!rls.hasMoreElements())
+ return(false);
+ URL u = rls.nextElement();
+ cur = parse(u);
+ }
+ return(true);
+ }
+
+ public String next() {
+ if(!hasNext())
+ throw(new NoSuchElementException());
+ String ret = cur.next();
+ return(ret);
+ }
+
+ public void remove() {throw(new UnsupportedOperationException());}
+ });
+ }
+ });
+ }
+
+ public Iterable<Class<?>> classes() {
+ return(new Iterable<Class<?>>() {
+ public Iterator<Class<?>> iterator() {
+ return(new Iterator<Class<?>>() {
+ private final Iterator<String> names = names().iterator();
+ private Class<?> n = null;
+
+ public boolean hasNext() {
+ while(n == null) {
+ if(!names.hasNext())
+ return(false);
+ String nm = names.next();
+ Class<?> c;
+ try {
+ c = cl.loadClass(nm);
+ } catch(ClassNotFoundException e) {
+ continue;
+ }
+ if(c.getAnnotation(an) == null)
+ continue;
+ n = c;
+ }
+ return(true);
+ }
+
+ public Class<?> next() {
+ if(!hasNext())
+ throw(new NoSuchElementException());
+ Class<?> r = n;
+ n = null;
+ return(r);
+ }
+
+ public void remove() {throw(new UnsupportedOperationException());}
+ });
+ }
+ });
+ }
+
+ public Iterable<?> instances() {
+ return(new Iterable<Object>() {
+ public Iterator<Object> iterator() {
+ return(new Iterator<Object>() {
+ private final Iterator<Class<?>> classes = classes().iterator();
+ private Object n = null;
+
+ public boolean hasNext() {
+ while(n == null) {
+ if(!classes.hasNext())
+ return(false);
+ Class<?> cl = classes.next();
+ Object inst;
+ try {
+ inst = cl.newInstance();
+ } catch(InstantiationException e) {
+ throw(new GlobInstantiationException(e));
+ } catch(IllegalAccessException e) {
+ throw(new GlobInstantiationException(e));
+ }
+ n = inst;
+ }
+ return(true);
+ }
+
+ public Object next() {
+ if(!hasNext())
+ throw(new NoSuchElementException());
+ Object r = n;
+ n = null;
+ return(r);
+ }
+
+ public void remove() {throw(new UnsupportedOperationException());}
+ });
+ }
+ });
+ }
+
+ public static Loader get(Class<? extends Annotation> annotation, ClassLoader loader) {
+ return(new Loader(annotation, loader));
+ }
+
+ public static Loader get(Class<? extends Annotation> annotation) {
+ return(get(annotation, annotation.getClassLoader()));
+ }
+}
--- /dev/null
+package dolda.jglob;
+
+import java.util.*;
+import javax.lang.model.element.*;
+import javax.lang.model.util.*;
+
+public class TypeMap {
+ private final Map<String, TypeElement> types;
+
+ public TypeMap(Collection<? extends Element> roots, final Elements eu) {
+ final Map<String, TypeElement> types = new HashMap<String, TypeElement>();
+ ElementVisitor<Void, Void> v = new ElementScanner6<Void, Void>() {
+ public Void visitType(TypeElement el, Void p) {
+ if((types.put(eu.getBinaryName(el).toString(), el)) != null)
+ throw(new RuntimeException("type name conflict: " + eu.getBinaryName(el)));
+ return(super.visitType(el, p));
+ }
+ };
+ for(Element el : roots)
+ v.visit(el);
+ this.types = types;
+ }
+
+ public TypeElement get(String name) {
+ return(types.get(name));
+ }
+}