From 01e70f6387cc327f5cf254a55fed02c2385397ab Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Tue, 12 Mar 2013 17:52:25 +0100 Subject: [PATCH] Initial commit. --- .gitignore | 1 + build.xml | 26 ++++ src/dolda/jglob/Collector.java | 106 ++++++++++++++++ src/dolda/jglob/Discoverable.java | 7 + src/dolda/jglob/GlobAccessException.java | 7 + src/dolda/jglob/GlobInstantiationException.java | 7 + src/dolda/jglob/Loader.java | 162 ++++++++++++++++++++++++ src/dolda/jglob/TypeMap.java | 27 ++++ 8 files changed, 343 insertions(+) create mode 100644 .gitignore create mode 100644 build.xml create mode 100644 src/dolda/jglob/Collector.java create mode 100644 src/dolda/jglob/Discoverable.java create mode 100644 src/dolda/jglob/GlobAccessException.java create mode 100644 src/dolda/jglob/GlobInstantiationException.java create mode 100644 src/dolda/jglob/Loader.java create mode 100644 src/dolda/jglob/TypeMap.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..be5db23 --- /dev/null +++ b/build.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/dolda/jglob/Collector.java b/src/dolda/jglob/Collector.java new file mode 100644 index 0000000..2ffd65f --- /dev/null +++ b/src/dolda/jglob/Collector.java @@ -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 getprev(TypeElement annotation) { + Set prev = new HashSet(); + 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 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 prev = getprev(annotation); + Set carry = new HashSet(prev); + Set found = new HashSet(); + 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 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 all = new ArrayList(); + all.addAll(carry); + all.addAll(found); + Collections.sort(all); + writenew(annotation, all); + } + + public boolean process(Set 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 index 0000000..8ca2444 --- /dev/null +++ b/src/dolda/jglob/Discoverable.java @@ -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 index 0000000..6efffb5 --- /dev/null +++ b/src/dolda/jglob/GlobAccessException.java @@ -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 index 0000000..919d801 --- /dev/null +++ b/src/dolda/jglob/GlobInstantiationException.java @@ -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 index 0000000..528c3fb --- /dev/null +++ b/src/dolda/jglob/Loader.java @@ -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 an; + private final ClassLoader cl; + private final Map, Object> instances = new HashMap, Object>(); + + private Loader(Class annotation, ClassLoader loader) { + this.an = annotation; + this.cl = loader; + } + + public Iterable names() { + return(new Iterable() { + public Iterator iterator() { + return(new Iterator() { + private Enumeration rls; + private Iterator cur = null; + + private Iterator parse(URL url) { + try { + List buf = new LinkedList(); + 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> classes() { + return(new Iterable>() { + public Iterator> iterator() { + return(new Iterator>() { + private final Iterator 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() { + public Iterator iterator() { + return(new Iterator() { + private final Iterator> 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 annotation, ClassLoader loader) { + return(new Loader(annotation, loader)); + } + + public static Loader get(Class annotation) { + return(get(annotation, annotation.getClassLoader())); + } +} diff --git a/src/dolda/jglob/TypeMap.java b/src/dolda/jglob/TypeMap.java new file mode 100644 index 0000000..ac42e63 --- /dev/null +++ b/src/dolda/jglob/TypeMap.java @@ -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 types; + + public TypeMap(Collection roots, final Elements eu) { + final Map types = new HashMap(); + ElementVisitor v = new ElementScanner6() { + 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)); + } +} -- 2.11.0