/* * Copyright 2007 Perry Nguyen Licensed 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 com.hanhuy.panno.processing; import java.util.Map; import java.util.Stack; import javax.annotation.processing.Messager; import javax.lang.model.element.Modifier; import javax.tools.Diagnostic; import com.hanhuy.panno.Property; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.BlockTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.ImportTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.StatementTree; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreeScanner; /** * Old attempt to get the annotation working, have a better approach now. */ @Deprecated class ASTTreeToSource extends TreeScanner { public final static String PROPERTY_ANNOTATION_FULL = "com.hanhuy.panno.Property"; public final static String PROPERTY_ANNOTATION = "Property"; private final Stack currentClassNameStack = new Stack(); private final Messager messager; private final Map fieldMap; private String currentClassName; private String currentField; private String currentType; ASTTreeToSource(Map fieldMap, Messager messager) { this.fieldMap = fieldMap; this.messager = messager; } /** * Handle initializer blocks, both static and non. */ @Override public Void visitBlock(BlockTree node, StringBuilder p) { p.append("\n"); if ( node.isStatic() ) p.append("static "); p.append("{\n"); for ( StatementTree st : node.getStatements() ) { p.append(st); p.append(";"); } p.append("\n}\n"); return null; } /** * Detect our annotation and add accessors if necessary. */ @Override public Void visitAnnotation(AnnotationTree node, StringBuilder p) { String anno = ((IdentifierTree)node.getAnnotationType()).getName().toString(); if ( anno != null && PROPERTY_ANNOTATION.equals(anno.trim()) ) { Property pAnno = fieldMap.get(currentClassName + "." + currentField); String propName = currentField; if ( !"".equals(pAnno.name()) ) propName = pAnno.name(); propName = PropertyProcessor.ucfirst(propName); if ( pAnno.writeOnly() && pAnno.readOnly() ) { messager.printMessage(Diagnostic.Kind.ERROR, "@Property's writeOnly and readOnly may not both be true"); } if ( !"boolean".equals(currentType) && pAnno.useGet() ) { messager.printMessage(Diagnostic.Kind.WARNING, "@Property's useGet is only for booleans"); } if ( !pAnno.writeOnly() ) { // generate a getter p.append("\n"); p.append("public "); p.append(currentType); if ( !"boolean".equals(currentType) || pAnno.useGet() ) p.append(" get"); else p.append(" is"); p.append(propName); p.append("() { return "); p.append(currentField); p.append("; }"); } if ( !pAnno.readOnly() ) { // generate a setter p.append("\n"); p.append("public void set"); p.append(propName); p.append("("); p.append(currentType); p.append(" "); p.append(currentField); p.append(") { this."); p.append(currentField); p.append(" = "); p.append(currentField); p.append("; }"); } } return null; } /** * Gather file information. Then continue to visit classes. */ @Override public Void visitCompilationUnit(CompilationUnitTree node, StringBuilder p) { new Exception("Visit CUT: " + node.getSourceFile()).printStackTrace(); p.append("// Generated from: "); p.append(node.getSourceFile()); p.append("\n\npackage "); p.append(node.getPackageName()); p.append(";\n\n"); for ( ImportTree it : node.getImports() ) { if ( PROPERTY_ANNOTATION_FULL.equals(it.getQualifiedIdentifier().toString().trim()) ) { continue; } p.append("import "); if ( it.isStatic() ) p.append("static "); p.append(it.getQualifiedIdentifier()); p.append(";\n"); } super.visitCompilationUnit(node, p); return null; } private void pushClass(String className) { currentClassName = className; currentClassNameStack.push(currentClassName); } private void popClass() { currentClassNameStack.pop(); if ( currentClassNameStack.size() > 0 ) currentClassName = currentClassNameStack.peek(); } /** * Reproduce class declarations, and go on to handle methods, blocks and * variables. */ @Override public Void visitClass(ClassTree node, StringBuilder p) { pushClass(node.getSimpleName().toString()); // Check to see if this is an enum // I have no idea what will happen if we encounter an AST // implementation that is not Sun's (or if this has changed in java7) if ( node instanceof com.sun.tools.javac.tree.JCTree.JCClassDecl ) { com.sun.tools.javac.tree.JCTree.JCClassDecl jcd = (com.sun.tools.javac.tree.JCTree.JCClassDecl)node; if ( (jcd.mods.flags & 16384L) != 0L ) { p.append(node.getModifiers()); p.append(" enum "); p.append(currentClassName); appendEnumBody(jcd.defs, p); popClass(); return null; } } p.append("\n"); p.append(node.getModifiers()); p.append(" class "); p.append(currentClassName); if ( node.getTypeParameters().size() > 0 ) { p.append("<"); p.append(node.getTypeParameters()); p.append(">"); } if ( node.getExtendsClause() != null ) { p.append("\nextends "); p.append(node.getExtendsClause()); } if ( node.getImplementsClause().size() > 0 ) { p.append("\nimplements "); p.append(node.getImplementsClause()); } p.append("\n{"); super.visitClass(node, p); p.append("\n}\n"); popClass(); return null; } /** * Enums appear to be a special case. We must reconstitute its source in a * different manner from the rest. Need to remove any 'super()' statements * and print the lines appropriately. */ public void appendEnumBody(com.sun.tools.javac.util.List list, StringBuilder p) { p.append("{\n"); boolean flag = true; for ( com.sun.tools.javac.util.List list1 = list; list1.nonEmpty(); list1 = list1.tail ) { if ( !isEnumerator(list1.head) ) continue; if ( !flag ) p.append(",\n"); p.append(list1.head); flag = false; } p.append(";\n"); for ( com.sun.tools.javac.util.List list2 = list; list2.nonEmpty(); list2 = list2.tail ) { if ( !isEnumerator(list2.head) ) { if ( list2.head instanceof MethodTree ) visitMethod((MethodTree)list2.head, p); else p.append(list2.head); p.append("\n"); } } p.append("}"); } private boolean isEnumerator(com.sun.tools.javac.tree.JCTree jctree) { return jctree.tag == 5 && (((com.sun.tools.javac.tree.JCTree.JCVariableDecl)jctree).mods.flags & 16384L) != 0L; } /** * Reconstitute methods. We could just do node.toString() here, but * constructors need to be renamed from <init> to the class name, so * handle all methods. */ @Override public Void visitMethod(MethodTree node, StringBuilder p) { p.append("\n"); p.append(node.getModifiers()); p.append(" "); if ( node.getReturnType() != null ) p.append(node.getReturnType()); if ( node.getTypeParameters().size() > 0 ) { p.append(" <"); p.append(node.getTypeParameters()); p.append(">"); } p.append(" "); if ( node.getName() != null && "".equals(node.getName().toString().trim()) ) p.append(currentClassName); else p.append(node.getName()); p.append("("); p.append(node.getParameters()); p.append(")"); if ( node.getThrows().size() > 0 ) { p.append("\nthrows "); p.append(node.getThrows()); } p.append(" {\n"); // this needs to be done for enums, otherwise we'll get a compile error // if we didn't need this case, we'd just do p.append(node.getBody()) for ( StatementTree st : node.getBody().getStatements() ) { if ( "super()".equals(st.toString().trim()) ) continue; p.append(st); if ( p.charAt(p.length() - 1) != ';' ) p.append(";\n"); } p.append("\n}\n"); return null; } /** * Reconstitute class fields. While doing so, search for our annotation to * perform processing. */ @Override public Void visitVariable(VariableTree node, StringBuilder p) { currentField = node.getName().toString(); currentType = node.getType().toString(); p.append("\n"); for ( AnnotationTree a : node.getModifiers().getAnnotations() ) { String anno = ((IdentifierTree)a.getAnnotationType()).getName().toString(); if ( !PROPERTY_ANNOTATION.equals(anno) ) { p.append(a); p.append("\n"); } } for ( Modifier m : node.getModifiers().getFlags() ) { p.append(m.toString()); p.append(" "); } p.append(" "); p.append(currentType); p.append(" "); p.append(currentField); if ( node.getInitializer() != null ) { p.append(" = "); p.append(node.getInitializer()); } p.append(";"); super.visitVariable(node, p); return null; } }