Initial commit.
authorFredrik Tolf <fredrik@dolda2000.com>
Tue, 12 Mar 2013 16:52:25 +0000 (17:52 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Tue, 12 Mar 2013 16:52:25 +0000 (17:52 +0100)
.gitignore [new file with mode: 0644]
build.xml [new file with mode: 0644]
src/dolda/jglob/Collector.java [new file with mode: 0644]
src/dolda/jglob/Discoverable.java [new file with mode: 0644]
src/dolda/jglob/GlobAccessException.java [new file with mode: 0644]
src/dolda/jglob/GlobInstantiationException.java [new file with mode: 0644]
src/dolda/jglob/Loader.java [new file with mode: 0644]
src/dolda/jglob/TypeMap.java [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..796b96d
--- /dev/null
@@ -0,0 +1 @@
+/build
diff --git a/build.xml b/build.xml
new file mode 100644 (file)
index 0000000..be5db23
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,26 @@
+<?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>
diff --git a/src/dolda/jglob/Collector.java b/src/dolda/jglob/Collector.java
new file mode 100644 (file)
index 0000000..2ffd65f
--- /dev/null
@@ -0,0 +1,106 @@
+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);
+    }
+}
diff --git a/src/dolda/jglob/Discoverable.java b/src/dolda/jglob/Discoverable.java
new file mode 100644 (file)
index 0000000..8ca2444
--- /dev/null
@@ -0,0 +1,7 @@
+package dolda.jglob;
+
+import java.lang.annotation.*;
+
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface Discoverable {
+}
diff --git a/src/dolda/jglob/GlobAccessException.java b/src/dolda/jglob/GlobAccessException.java
new file mode 100644 (file)
index 0000000..6efffb5
--- /dev/null
@@ -0,0 +1,7 @@
+package dolda.jglob;
+
+public class GlobAccessException extends RuntimeException {
+    public GlobAccessException(Throwable cause) {
+       super(cause);
+    }
+}
diff --git a/src/dolda/jglob/GlobInstantiationException.java b/src/dolda/jglob/GlobInstantiationException.java
new file mode 100644 (file)
index 0000000..919d801
--- /dev/null
@@ -0,0 +1,7 @@
+package dolda.jglob;
+
+public class GlobInstantiationException extends RuntimeException {
+    public GlobInstantiationException(Throwable cause) {
+       super(cause);
+    }
+}
diff --git a/src/dolda/jglob/Loader.java b/src/dolda/jglob/Loader.java
new file mode 100644 (file)
index 0000000..528c3fb
--- /dev/null
@@ -0,0 +1,162 @@
+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()));
+    }
+}
diff --git a/src/dolda/jglob/TypeMap.java b/src/dolda/jglob/TypeMap.java
new file mode 100644 (file)
index 0000000..ac42e63
--- /dev/null
@@ -0,0 +1,27 @@
+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));
+    }
+}