/** * Copyright 2015 Santhosh Kumar Tekuri * * The JLibs authors license this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package jlibs.xml.sax.binding.impl.processor; import jlibs.core.annotation.processing.AnnotationError; import jlibs.core.annotation.processing.AnnotationProcessor; import jlibs.core.annotation.processing.Printer; import jlibs.core.lang.model.ModelUtil; import jlibs.xml.sax.binding.SAXContext; import jlibs.xml.sax.binding.impl.Delegate; import org.kohsuke.MetaInfServices; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import javax.annotation.processing.Processor; 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.*; import javax.lang.model.util.ElementFilter; import javax.xml.namespace.QName; import java.io.IOException; import java.util.Map; import java.util.Set; import static jlibs.core.annotation.processing.Printer.MINUS; import static jlibs.core.annotation.processing.Printer.PLUS; /** * @author Santhosh Kumar T */ @SuppressWarnings({"unchecked"}) @SupportedAnnotationTypes("jlibs.xml.sax.binding.Binding") @SupportedSourceVersion(SourceVersion.RELEASE_6) @MetaInfServices(Processor.class) public class BindingAnnotationProcessor extends AnnotationProcessor{ private static final String SUFFIX = "Impl"; public static final String FORMAT = "${package}.${class}"+SUFFIX; private static final BindingAnnotation BINDING_ELEMENT = new ElementAnnotation(); private static final BindingAnnotation BINDING_START = new BindingStartAnnotation(); private static final BindingAnnotation BINDING_TEXT = new BindingTextAnnotation(); private static final BindingAnnotation BINDING_FINISH = new BindingFinishAnnotation(); private static final BindingAnnotation RELATION_START = new RelationAnnotation(true); private static final BindingAnnotation RELATION_FINISH = new RelationAnnotation(false); @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){ for(TypeElement annotation: annotations){ for(Element elem: roundEnv.getElementsAnnotatedWith(annotation)){ Binding binding = new Binding(); try{ TypeElement c = (TypeElement)elem; while(c!=null && !c.getQualifiedName().contentEquals(Object.class.getName())){ process(binding, c); c = ModelUtil.getSuper(c); } binding.handleStar(); binding.initID(0); Printer pw = null; try{ pw = Printer.get((TypeElement)elem, jlibs.xml.sax.binding.Binding.class, FORMAT); generateClass(binding, pw); }catch(IOException ex){ throw new RuntimeException(ex); }finally{ if(pw!=null) pw.close(); } }catch(AnnotationError error){ error.report(); } } } return true; } private void process(Binding binding, TypeElement c){ for(ExecutableElement method: ElementFilter.methodsIn(c.getEnclosedElements())){ for(AnnotationMirror mirror: method.getAnnotationMirrors()){ if(BINDING_ELEMENT.matches(mirror)) BINDING_ELEMENT.consume(binding, method, mirror); else if(BINDING_START.matches(mirror)) BINDING_START.consume(binding, method, mirror); else if(BINDING_TEXT.matches(mirror)) BINDING_TEXT.consume(binding, method, mirror); else if(BINDING_FINISH.matches(mirror)) BINDING_FINISH.consume(binding, method, mirror); else if(RELATION_START.matches(mirror)) RELATION_START.consume(binding, method, mirror); else if(RELATION_FINISH.matches(mirror)) RELATION_FINISH.consume(binding, method, mirror); } } } private void generateClass(Binding binding, Printer pw) throws IOException{ TypeElement clazz = pw.clazz; pw.printPackage(); pw.importPackage(Delegate.class); pw.importClass(SAXContext.class); pw.importClass(Attributes.class); pw.importClass(SAXException.class); pw.importClass(QName.class); if(ModelUtil.isInnerClass(clazz)) pw.importClass(clazz); pw.println(); pw.printClassDoc(); pw.printlns( "@SuppressWarnings({\"unchecked\"})", "public class "+pw.generatedClazz +" extends BindingCumRelation{", PLUS, "public static final "+pw.generatedClazz +" INSTANCE = new "+pw.generatedClazz +"(new "+clazz.getSimpleName()+"());", "static{", PLUS, "INSTANCE.init();", MINUS, "}" ); pw.emptyLine(true); pw.print("public static final QName ELEMENT = "); AnnotationMirror bindingMirror = ModelUtil.getAnnotationMirror(clazz, jlibs.xml.sax.binding.Binding.class); if(bindingMirror==null) pw.println("null;"); else{ String element = ModelUtil.getAnnotationValue(clazz, bindingMirror, "value"); if(element.indexOf('/')!=-1) throw new AnnotationError(clazz, bindingMirror, "value of "+jlibs.xml.sax.binding.Binding.class+" should be single element, but not element path"); pw.println(Binding.toJava(Binding.toQName(clazz, bindingMirror, element))+";"); } pw.emptyLine(true); pw.printlns( "private final "+clazz.getSimpleName()+" handler;", "private "+pw.generatedClazz +"("+clazz.getSimpleName()+" handler){", PLUS, "this.handler = handler;", MINUS, "}" ); pw.emptyLine(true); pw.print("private void init()"); if(binding.registry.size()>0) generateInitMethod(binding, pw); else{ pw.println("{}"); pw.emptyLine(true); } BINDING_START.printMethod(pw, binding); BINDING_TEXT.printMethod(pw, binding); BINDING_FINISH.printMethod(pw, binding); RELATION_START.printMethod(pw, binding); RELATION_FINISH.printMethod(pw, binding); pw.indent--; pw.emptyLine(false); pw.println("}"); } private void generateInitMethod(Binding binding, Printer pw){ if(binding.registry.size()>0){ pw.println("{"); pw.indent++; if(binding.id==0) pw.println("Delegate leaf = new Delegate(this);"); for(Map.Entry<QName, BindingRelation> entry: binding.registry.entrySet()){ QName qname = entry.getKey(); BindingRelation bindingRelation = entry.getValue(); Binding childBinding = bindingRelation.binding; boolean useDelegate = childBinding.registry.size()>0; if(useDelegate) pw.print("Registry registry"+childBinding.id+" = "); pw.print("registry"+(binding.id>0?binding.id:"")+".register("); if(childBinding.element!=null){ pw.print(Binding.toJava(qname)+", 0, "); String className = ModelUtil.getPackage(childBinding.element).equals(ModelUtil.getPackage(pw.clazz)) ? childBinding.element.getSimpleName().toString() : childBinding.element.getQualifiedName().toString(); pw.println(className+SUFFIX+".INSTANCE, "+childBinding.id+", this);"); }else{ pw.print(Binding.toJava(qname)+", "+childBinding.id+", "+(useDelegate ?"new Delegate(this)":"leaf")+", "); pw.println(childBinding.id+", this);"); generateInitMethod(childBinding, pw); } } pw.indent--; pw.println("}"); pw.emptyLine(true); } } }