|
| 1 | +package lombok.javac.handlers; |
| 2 | + |
| 3 | +import static lombok.javac.handlers.JavacHandlerUtil.deleteAnnotationIfNeccessary; |
| 4 | + |
| 5 | +import java.util.ArrayList; |
| 6 | +import java.util.Collection; |
| 7 | +import java.util.Collections; |
| 8 | +import java.util.List; |
| 9 | + |
| 10 | +import com.sun.tools.javac.tree.JCTree; |
| 11 | +import com.sun.tools.javac.tree.JCTree.JCAnnotation; |
| 12 | +import com.sun.tools.javac.tree.JCTree.JCExpression; |
| 13 | +import com.sun.tools.javac.tree.JCTree.JCIdent; |
| 14 | +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; |
| 15 | + |
| 16 | +import lombok.Onstruct; |
| 17 | +import lombok.core.AST.Kind; |
| 18 | +import lombok.core.AnnotationValues; |
| 19 | +import lombok.core.HandlerPriority; |
| 20 | +import lombok.core.LombokNode; |
| 21 | +import lombok.core.handlers.OnstructUtils; |
| 22 | +import lombok.eclipse.DeferUntilPostDiet; |
| 23 | +import lombok.javac.JavacAnnotationHandler; |
| 24 | +import lombok.javac.JavacNode; |
| 25 | +import lombok.javac.JavacTreeMaker; |
| 26 | +import lombok.spi.Provides; |
| 27 | + |
| 28 | +@Provides |
| 29 | +@DeferUntilPostDiet |
| 30 | +@HandlerPriority(65536) // same as HandleValue // TODO |
| 31 | +public class HandleOnstruct extends JavacAnnotationHandler<Onstruct> { |
| 32 | + |
| 33 | + |
| 34 | + /** |
| 35 | + * find the siblings with same kind and annotation. Copy of |
| 36 | + * {@link LombokNode#upFromAnnotationToFields()} with same kind and no check |
| 37 | + * on the parent. |
| 38 | + * |
| 39 | + */ |
| 40 | + public static Collection<JavacNode> upFromAnnotationToSameKind(JavacNode node) { |
| 41 | + if (node.getKind() != Kind.ANNOTATION) return Collections.emptyList(); |
| 42 | + JavacNode declaration = node.up(); |
| 43 | + if (declaration == null) return Collections.emptyList(); |
| 44 | + |
| 45 | + List<JavacNode> fields = new ArrayList(); |
| 46 | + |
| 47 | + for (JavacNode potentialField : declaration.up().down()) { |
| 48 | + if (potentialField.getKind() != declaration.getKind()) continue; |
| 49 | + for (JavacNode child : potentialField.down()) { |
| 50 | + if (child.getKind() != Kind.ANNOTATION) continue; |
| 51 | + if (child.get() == node.get()) fields.add(potentialField); |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + return fields; |
| 56 | + } |
| 57 | + |
| 58 | + /** |
| 59 | + * retrieve the children statements from a list of node |
| 60 | + */ |
| 61 | + protected static List<JCTree> findChildrenStatements(Collection<JavacNode> fields) { |
| 62 | + List<JCTree> ret = new ArrayList(); |
| 63 | + for (JavacNode f : fields) { |
| 64 | + for (JavacNode potentialStatement : f.down()) { |
| 65 | + if (potentialStatement.getKind() == Kind.STATEMENT) { |
| 66 | + ret.add(potentialStatement.get()); |
| 67 | + } |
| 68 | + } |
| 69 | + } |
| 70 | + return ret; |
| 71 | + } |
| 72 | + |
| 73 | + @Override public void handle(AnnotationValues<Onstruct> annotation, JCAnnotation ast, JavacNode annotationNode) { |
| 74 | + Collection<JavacNode> annotatedVariables = upFromAnnotationToSameKind(annotationNode); |
| 75 | + JavacNode parentNode = annotationNode.up(); |
| 76 | + Onstruct annotationInstance = annotation.getInstance(); |
| 77 | + deleteAnnotationIfNeccessary(annotationNode, Onstruct.class); |
| 78 | + |
| 79 | + List<JCTree> statements = findChildrenStatements(annotatedVariables); |
| 80 | + // sanity checks on statements. Among the variables declaration, there |
| 81 | + // must be one statement. |
| 82 | + if (statements.isEmpty()) { |
| 83 | + annotationNode.addError("no assignment. Requires one identifier assignment."); |
| 84 | + return; |
| 85 | + } |
| 86 | + if (statements.size() > 1) { |
| 87 | + annotationNode.addError("Too many assignments:" + statements + " Requires exactly one identifier assignment."); |
| 88 | + return; |
| 89 | + } |
| 90 | + |
| 91 | + JCTree tree = statements.get(0); |
| 92 | + JCTree.JCIdent ident; |
| 93 | + String varName = null; |
| 94 | + // sanity checks on the assignment. It must be an identifier. |
| 95 | + if (tree instanceof JCTree.JCIdent) { |
| 96 | + ident = (JCTree.JCIdent) tree; |
| 97 | + varName = (ident.name.toString()); |
| 98 | + } else { |
| 99 | + annotationNode.addError("invalid assignment" + tree + " : must be an identifier"); |
| 100 | + return; |
| 101 | + } |
| 102 | + if (varName == null) { |
| 103 | + annotationNode.addError("assignement is null . Must be an identifier"); |
| 104 | + return; |
| 105 | + } |
| 106 | + |
| 107 | + for (JavacNode f : annotatedVariables) { |
| 108 | + handleVarDeclaration(f, annotationInstance, parentNode, ident); |
| 109 | + } |
| 110 | + // parentNode.rebuild(); |
| 111 | + } |
| 112 | + |
| 113 | + private void handleVarDeclaration(JavacNode varNode, Onstruct annotationInstance, JavacNode parentNode, JCIdent ident) { |
| 114 | + String varName = OnstructUtils.varName(varNode.getName(), annotationInstance); |
| 115 | + String methName = OnstructUtils.methodName(varNode.getName(), annotationInstance); |
| 116 | + JCTree elem = varNode.get(); |
| 117 | + JavacTreeMaker maker = varNode.getTreeMaker(); |
| 118 | + // System.out.println("create : var " + varName + " = " + ident.name + "." + methName + "();"); |
| 119 | + if (elem instanceof JCVariableDecl) { |
| 120 | + JCVariableDecl variable = (JCVariableDecl) elem; |
| 121 | + variable.type = (JavacHandlerUtil.chainDotsString(varNode, "java.lang.String")).type; |
| 122 | + JCExpression methCall = maker.Select(ident, varNode.toName(methName)); |
| 123 | + variable.init = maker.Apply(com.sun.tools.javac.util.List.<JCExpression>nil(), |
| 124 | + methCall, |
| 125 | + com.sun.tools.javac.util.List.<JCExpression>nil()); |
| 126 | + variable.name = varNode.toName(varName); |
| 127 | + // System.out.println("replaced with " + variable); |
| 128 | + } else |
| 129 | + System.err.println(varNode.get()); |
| 130 | + } |
| 131 | + |
| 132 | +} |
0 commit comments