Skip to content

Instantly share code, notes, and snippets.

@9re
Created June 18, 2012 10:25
Show Gist options
  • Save 9re/2947777 to your computer and use it in GitHub Desktop.
Save 9re/2947777 to your computer and use it in GitHub Desktop.
Metannot Demo
package mn.uwvm.metannot.test;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;
import mn.uwvm.metannot.annotation.Template;
import mn.uwvm.metannot.annotation.TemplateParameter;
import mn.uwvm.metannot.model.MetannotTemplate;
import mn.uwvm.metannot.model.MetannotTemplateParameter;
import mn.uwvm.metannot.util.Logger;
import mn.uwvm.metannot.util.MetannotUtil;
import mn.uwvm.metannot.visitor.TemplateVisitor;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
@SupportedAnnotationTypes("mn.uwvm.metannot.annotation.Template")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public abstract class AbstractMetannotProcessor extends AbstractProcessor {
private static final String IMPORT_WRITER = "writeImports";
private Trees mTrees;
private Set<String> mVisitedClass = new HashSet<String>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mTrees = Trees.instance(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
boolean processed = false;
for (TypeElement annotation : annotations) {
for (Element element : roundEnvironment.getElementsAnnotatedWith(annotation)) {
TypeElement typeElement = (TypeElement) element.getEnclosingElement();
if (handleEnclosingElement(typeElement)) {
processed = true;
}
}
}
return processed;
}
private boolean handleEnclosingElement(TypeElement typeElement) {
final JCCompilationUnit unit;
{
TreePath treePath = mTrees.getPath(typeElement);
unit = (JCCompilationUnit) (treePath != null ? treePath.getCompilationUnit() : null);
}
if (unit == null) {
return false;
}
if (unit.sourcefile.getKind() != JavaFileObject.Kind.SOURCE) {
Logger.log("skipped:", unit.sourcefile);
return false;
}
processClass(unit, typeElement);
return false;
}
private boolean processClass(JCCompilationUnit unit, TypeElement element) {
boolean processed = false;
String packageName = element.getEnclosingElement().toString();
String originalName = element.getQualifiedName().toString();
String originalSimpleName = originalName.substring(packageName.length() + 1);
if (!originalSimpleName.startsWith("Abstract")) {
processingEnv.getMessager()
.printMessage(Kind.ERROR, "class name using @" + Template.class.getSimpleName()
+ " should start with Abstract");
return false;
}
String simpleName = originalSimpleName.substring("Abstract".length());
String sourceName = packageName + "." + simpleName;
if (mVisitedClass.contains(sourceName)) {
return false;
}
Filer filer = processingEnv.getFiler();
Writer writer = null;
JavaFileObject javaFileObject;
try {
javaFileObject = filer.createSourceFile(sourceName);
writer = javaFileObject.openWriter();
TemplateVisitor templateVisitor = new TemplateVisitor(processingEnv);
unit.accept(templateVisitor);
String generationTime = String.format("%1$tFT%1$tH:%1$tM:%1$tS.%1$tL%1$tz", new Date(System.currentTimeMillis()));
writer.append("package ").append(packageName).append(";\n\n");
writeImports(writer);
writer
.append("@javax.annotation.Generated(value = \"" + this.getClass().getName() + "\", date = \"" + generationTime + "\")\n");
for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
writer.append(mirror.toString()).append('\n');
}
writer
.append("public class ")
.append(simpleName)
.append(" extends ")
.append(originalName)
.append(' ');
Map<String, String> templateParams = new HashMap<String, String>();
//
{
StringWriter stringWriter = new StringWriter();
writeTemplateWriters(templateVisitor, element, stringWriter);
templateParams.put("templateWriters", stringWriter.toString());
}
writeImportWriter(templateVisitor, element, templateParams);
writeClassImpl(writer, processingEnv.getMessager(), templateParams, simpleName);
//
writer.flush();
processed = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
mVisitedClass.add(sourceName);
return processed;
}
private void writeImportWriter(TemplateVisitor templateVisitor, TypeElement typeElement, Map<String, String> templateParams) throws IOException {
{ //
Map<String, JCMethodDecl> absMethods = templateVisitor.getAbstractMethods();
if (!absMethods.containsKey(IMPORT_WRITER)) {
processingEnv.getMessager()
.printMessage(Kind.ERROR, "Declare abstract method void writeImports(java.io.Writer writer) throws IOException; in " + typeElement.getQualifiedName() + "!");
return;
}
JCMethodDecl writerDecl = absMethods.get(IMPORT_WRITER);
boolean hasIOException = false;
boolean hasOtherException = false;
for (JCExpression expression : writerDecl.thrown) {
if ("IOException".equals(expression.toString())) {
hasIOException = true;
} else {
hasOtherException = true;
break;
}
}
if (!hasIOException || hasOtherException) {
processingEnv.getMessager()
.printMessage(Kind.ERROR,
"Throws expression should be 'throws IOException' at " + IMPORT_WRITER + " in " + typeElement);
return;
}
}
StringBuilder builder = new StringBuilder();
builder.append("new String[] {");
for (Iterator<String> iter = templateVisitor.getImports().iterator();;) {
builder
.append("\"")
.append(iter.next())
.append("\"");
if (iter.hasNext()) {
builder.append(", ");
} else {
break;
}
}
builder.append("}");
templateParams.put("strImports", builder.toString());
}
private void writeTemplateWriters(TemplateVisitor templateVisitor, TypeElement element, Writer writer) throws IOException {
List<MetannotTemplate> templates = templateVisitor.getTemplates();
Map<String, JCMethodDecl> abstractMethods = templateVisitor.getAbstractMethods();
for (MetannotTemplate template : templates) {
if (!abstractMethods.containsKey(template.writer)) {
processingEnv.getMessager()
.printMessage(Kind.ERROR, "you should declare abstract method abstract void " + template.writer + "(Writer writer, Messager messager, Map<String, String> params) throws IOException; in " + element);
return;
}
writer
.append("protected void ")
.append(template.writer)
.append("(Writer writer, Messager messager, Map<String, String> params, String typename) throws IOException ");
Map<String, String> params = new HashMap<String, String>();
params.put("template", "\"" + template.template.replace("\n", "\\n") + "\"");
StringBuilder parametersBuilder = new StringBuilder();
parametersBuilder.append("new String[]{");
StringBuilder indecesBuilder = new StringBuilder();
indecesBuilder.append("new int[]{");
if (template.parameters.size() > 0) {
for (Iterator<MetannotTemplateParameter> iter = template.parameters.iterator();;){
MetannotTemplateParameter parameter = iter.next();
indecesBuilder.append(parameter.start + ", " + parameter.end);
parametersBuilder
.append("\"")
.append(parameter.key)
.append("\"");
if (iter.hasNext()) {
indecesBuilder.append(", ");
parametersBuilder.append(", ");
} else {
break;
}
}
}
parametersBuilder.append("}");
indecesBuilder.append("}");
params.put("parameters", parametersBuilder.toString());
params.put("indeces", indecesBuilder.toString());
params.put("writerName", "\"" + template.writer + "\"");
params.put("oldToken", "\"" + template.templateName + "\"");
params.put(
"call$writeTemplateParameters$",
"$writeTemplateParameters$(template, parameters, params, messager, writerName, builder);");
writeTemplateWriter(writer, processingEnv.getMessager(), params, null);
writer.append("\n");
}
}
protected abstract void writeImports(Writer writer) throws IOException;
protected abstract void writeClassImpl(Writer writer, Messager messager, Map<String, String> params, String typename) throws IOException;
protected abstract void writeTemplateWriter(Writer writer, Messager messager, Map<String, String> params, String typename) throws IOException;
@Template(writer = "writeTemplateWriter")
protected void templateWriterTemplate(Writer writer, Messager messager, Map<String, String> params, String typename) throws IOException {
@TemplateParameter
String template = null;
@TemplateParameter
String[] parameters = null;
@TemplateParameter
String writerName = null;
@TemplateParameter
String oldToken = null;
StringBuilder builder = new StringBuilder();
@TemplateParameter(keepLhs = false)
String call$writeTemplateParameters$;
if (typename != null) {
writer.append(
MetannotUtil.replaceTokenInString(
builder.toString(), oldToken, typename));
} else {
writer.append(builder);
}
}
@Template(writer = "writeClassImpl")
protected abstract static class MetannotProcessorTemplate extends AbstractProcessor {
@TemplateParameter(keepLhs = false)
String templateWriters;
public MetannotProcessorTemplate() {
}
private void $writeTemplateParameters$(String template, String[] parameters, Map<String, String> params, Messager messager, String writerName, StringBuilder builder) {
int pos = 0;
Pattern templatePattern = Pattern.compile("@" + TemplateParameter.class.getSimpleName() + "\\(");
Matcher matcher = templatePattern.matcher(template);
int keyIndex = 0;
while (true) {
if (matcher.find(pos)) {
String strKeepLhs = template.substring(
matcher.end(), template.indexOf(')', matcher.end()));
boolean keepLhs = true;
if (strKeepLhs.length() > 0) {
keepLhs = strKeepLhs.indexOf("true") > -1;
}
int start, end;
int nextLf = template.indexOf('\n', matcher.end());
String key = parameters[keyIndex ++];
if (keepLhs) {
start = template.indexOf('=', nextLf + 1) + 2;
end = template.indexOf(';', nextLf + 1);
} else {
start = matcher.start();
end = template.indexOf(';', nextLf + 1);
}
builder.append(
template.subSequence(
pos, start));
if (!params.containsKey(key)) {
messager.printMessage(Kind.ERROR,
"Template parameter " + key + " was given, but there are no such key in the params passed to injectionClassWriter!");
return;
}
builder.append(params.get(key));
pos = end;
} else {
break;
}
}
builder.append(template.substring(pos));
}
protected void writeImports(java.io.Writer writer) throws IOException {
@TemplateParameter
String[] strImports = null;
for (String strImport : strImports) {
writer
.append("import ")
.append(strImport)
.append(";\n");
}
writer.append("\n");
}
}
}
package mn.uwvm.metannot.test;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;
import mn.uwvm.metannot.annotation.Template;
import mn.uwvm.metannot.annotation.TemplateParameter;
import mn.uwvm.metannot.model.MetannotTemplate;
import mn.uwvm.metannot.model.MetannotTemplateParameter;
import mn.uwvm.metannot.util.Logger;
import mn.uwvm.metannot.util.MetannotUtil;
import mn.uwvm.metannot.visitor.TemplateVisitor;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
@javax.annotation.Generated(value = "mn.uwvm.metannot.MetannotProcessor", date = "2012-06-18T19:21:21.034+0900")
@javax.annotation.processing.SupportedAnnotationTypes({"mn.uwvm.metannot.annotation.Template"})
@javax.annotation.processing.SupportedSourceVersion(javax.lang.model.SourceVersion.RELEASE_6)
public class MetannotProcessor extends mn.uwvm.metannot.test.AbstractMetannotProcessor {
protected void writeTemplateWriter(Writer writer, Messager messager, Map<String, String> params, String typename) throws IOException {
String template = "{\n @TemplateParameter()\n String template = null;\n @TemplateParameter()\n String[] parameters = null;\n @TemplateParameter()\n String writerName = null;\n @TemplateParameter()\n String oldToken = null;\n StringBuilder builder = new StringBuilder();\n @TemplateParameter(keepLhs = false)\n String call$writeTemplateParameters$;\n if (typename != null) {\n writer.append(MetannotUtil.replaceTokenInString(builder.toString(), oldToken, typename));\n } else {\n writer.append(builder);\n }\n}";
int pos = 0;
String writerName = "writeTemplateWriter";
StringBuilder builder = new StringBuilder();
String[] parameters = new String[] {"template", "parameters", "writerName", "oldToken", "call$writeTemplateParameters$"};
$writeTemplateParameters$(template, parameters, params, messager, writerName, builder);
String output = builder.toString();
if (typename != null) {
output = mn.uwvm.metannot.util.MetannotUtil.replaceTokenInString(
output, "templateWriterTemplate", typename);
}
writer.append(output);
}
protected void writeClassImpl(Writer writer, Messager messager, Map<String, String> params, String typename) throws IOException {
String template = "{\n @TemplateParameter(keepLhs = false)\n String templateWriters;\n \n public MetannotProcessorTemplate() {\n }\n \n private void $writeTemplateParameters$(String template, String[] parameters, Map<String, String> params, Messager messager, String writerName, StringBuilder builder) {\n int pos = 0;\n Pattern templatePattern = Pattern.compile(\"@\" + TemplateParameter.class.getSimpleName() + \"\\\\(\");\n Matcher matcher = templatePattern.matcher(template);\n int keyIndex = 0;\n while (true) {\n if (matcher.find(pos)) {\n String strKeepLhs = template.substring(matcher.end(), template.indexOf(')', matcher.end()));\n boolean keepLhs = true;\n if (strKeepLhs.length() > 0) {\n keepLhs = strKeepLhs.indexOf(\"true\") > -1;\n }\n int start;\n int end;\n int nextLf = template.indexOf('\\n', matcher.end());\n String key = parameters[keyIndex++];\n if (keepLhs) {\n start = template.indexOf('=', nextLf + 1) + 2;\n end = template.indexOf(';', nextLf + 1);\n } else {\n start = matcher.start();\n end = template.indexOf(';', nextLf + 1);\n }\n builder.append(template.subSequence(pos, start));\n if (!params.containsKey(key)) {\n messager.printMessage(Kind.ERROR, \"Template parameter \" + key + \" was given, but there are no such key in the params passed to injectionClassWriter!\");\n return;\n }\n builder.append(params.get(key));\n pos = end;\n } else {\n break;\n }\n }\n builder.append(template.substring(pos));\n }\n \n protected void writeImports(java.io.Writer writer) throws IOException {\n @TemplateParameter()\n String[] strImports = null;\n for (String strImport : strImports) {\n writer.append(\"import \").append(strImport).append(\";\\n\");\n }\n writer.append(\"\\n\");\n }\n}";
int pos = 0;
String writerName = "writeClassImpl";
StringBuilder builder = new StringBuilder();
String[] parameters = new String[] {"templateWriters", "strImports"};
$writeTemplateParameters$(template, parameters, params, messager, writerName, builder);
String output = builder.toString();
if (typename != null) {
output = mn.uwvm.metannot.util.MetannotUtil.replaceTokenInString(
output, "MetannotProcessorTemplate", typename);
}
writer.append(output);
}
private void $writeTemplateParameters$(String template, String[] parameters, Map<String, String> params, Messager messager, String writerName, StringBuilder builder) {
int pos = 0;
java.util.regex.Pattern templatePattern = java.util.regex.Pattern.compile("@TemplateParameter\\(");
java.util.regex.Matcher matcher = templatePattern.matcher(template);
int keyIndex = 0;
while (true) {
if (matcher.find(pos)) {
String strKeepLhs = template.substring(
matcher.end(), template.indexOf(')', matcher.end()));
boolean keepLhs = true;
if (strKeepLhs.length() > 0) {
keepLhs = strKeepLhs.indexOf("true") > -1;
}
int start, end;
int nextLf = template.indexOf('\n', matcher.end());
String key = parameters[keyIndex ++];
if (keepLhs) {
start = template.indexOf('=', nextLf + 1) + 2;
end = template.indexOf(';', nextLf + 1);
} else {
start = matcher.start();
end = template.indexOf(';', nextLf + 1);
}
builder.append(
template.subSequence(
pos, start));
if (!params.containsKey(key)) {
messager.printMessage(javax.tools.Diagnostic.Kind.ERROR,
"Template parameter " + key + " was given, but there are no such key in the params passed to injectionClassWriter!");
return; }
builder.append(params.get(key));
pos = end;
} else {
break;
}
}
builder.append(template.substring(pos));
}
protected void writeImports(Writer writer) throws IOException {
for (String strImport :
new String[] {"java.io.IOException", "java.io.StringWriter", "java.io.Writer", "java.util.Date", "java.util.HashMap", "java.util.HashSet", "java.util.Iterator", "java.util.List", "java.util.Map", "java.util.Set", "java.util.regex.Matcher", "java.util.regex.Pattern", "javax.annotation.processing.AbstractProcessor", "javax.annotation.processing.Filer", "javax.annotation.processing.Messager", "javax.annotation.processing.ProcessingEnvironment", "javax.annotation.processing.RoundEnvironment", "javax.annotation.processing.SupportedAnnotationTypes", "javax.annotation.processing.SupportedSourceVersion", "javax.lang.model.SourceVersion", "javax.lang.model.element.AnnotationMirror", "javax.lang.model.element.Element", "javax.lang.model.element.TypeElement", "javax.tools.Diagnostic.Kind", "javax.tools.JavaFileObject", "mn.uwvm.metannot.annotation.Template", "mn.uwvm.metannot.annotation.TemplateParameter", "mn.uwvm.metannot.model.MetannotTemplate", "mn.uwvm.metannot.model.MetannotTemplateParameter", "mn.uwvm.metannot.util.Logger", "mn.uwvm.metannot.util.MetannotUtil", "mn.uwvm.metannot.visitor.TemplateVisitor", "com.sun.source.util.TreePath", "com.sun.source.util.Trees", "com.sun.tools.javac.tree.JCTree.JCCompilationUnit", "com.sun.tools.javac.tree.JCTree.JCExpression", "com.sun.tools.javac.tree.JCTree.JCMethodDecl"}) {
writer
.append("import ")
.append(strImport)
.append(";\n");
}
writer.append('\n');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment