diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-08-13 14:11:00 +0100 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-08-13 14:11:00 +0100 |
commit | aad0666ef2f5463bb7e746318a902ebee57e3e86 (patch) | |
tree | b3457ecc46a17807253a16d1eceed823b1ebed8d /src/core/lombok/javac/handlers/HandleHelper.java | |
parent | 6ca549224f4db8dc431f624d1f09556c8939eaed (diff) | |
download | lombok-aad0666ef2f5463bb7e746318a902ebee57e3e86.tar.gz lombok-aad0666ef2f5463bb7e746318a902ebee57e3e86.tar.bz2 lombok-aad0666ef2f5463bb7e746318a902ebee57e3e86.zip |
Added new feature: @Helper including both annotations and tests.
Diffstat (limited to 'src/core/lombok/javac/handlers/HandleHelper.java')
-rw-r--r-- | src/core/lombok/javac/handlers/HandleHelper.java | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/src/core/lombok/javac/handlers/HandleHelper.java b/src/core/lombok/javac/handlers/HandleHelper.java new file mode 100644 index 00000000..99131f70 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleHelper.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.handlers; + +import static lombok.core.handlers.HandlerUtil.*; +import static lombok.javac.handlers.JavacHandlerUtil.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.TreeVisitor; +import com.sun.source.util.TreeScanner; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Name; + +import lombok.ConfigurationKeys; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.experimental.Helper; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; + +@ProviderFor(JavacAnnotationHandler.class) +public class HandleHelper extends JavacAnnotationHandler<Helper> { + @Override public void handle(AnnotationValues<Helper> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper"); + + deleteAnnotationIfNeccessary(annotationNode, Helper.class); + JavacNode annotatedType = annotationNode.up(); + JavacNode containingMethod = annotatedType == null ? null : annotatedType.up(); + + if (annotatedType == null || containingMethod == null || annotatedType.getKind() != Kind.TYPE || containingMethod.getKind() != Kind.METHOD) { + annotationNode.addError("@Helper is legal only on method-local classes."); + return; + } + + JCClassDecl annotatedType_ = (JCClassDecl) annotatedType.get(); + JCMethodDecl amd = (JCMethodDecl) containingMethod.get(); + List<JCStatement> origStatements = amd.body.stats; + Iterator<JCStatement> it = origStatements.iterator(); + while (it.hasNext()) { + if (it.next() == annotatedType_) { + break; + } + } + + java.util.List<String> knownMethodNames = new ArrayList<String>(); + + for (JavacNode ch : annotatedType.down()) { + if (ch.getKind() != Kind.METHOD) continue; + String n = ch.getName(); + if (n == null || n.isEmpty() || n.charAt(0) == '<') continue; + knownMethodNames.add(n); + } + + Collections.sort(knownMethodNames); + final String[] knownMethodNames_ = knownMethodNames.toArray(new String[knownMethodNames.size()]); + + final Name helperName = annotationNode.toName("$" + annotatedType_.name); + final boolean[] helperUsed = new boolean[1]; + final JavacTreeMaker maker = annotationNode.getTreeMaker(); + + TreeVisitor<Void, Void> visitor = new TreeScanner<Void, Void>() { + @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) { + JCMethodInvocation jcmi = (JCMethodInvocation) node; + apply(jcmi); + return super.visitMethodInvocation(node, p); + } + + private void apply(JCMethodInvocation jcmi) { + if (!(jcmi.meth instanceof JCIdent)) return; + JCIdent jci = (JCIdent) jcmi.meth; + if (Arrays.binarySearch(knownMethodNames_, jci.name.toString()) < 0) return; + jcmi.meth = maker.Select(maker.Ident(helperName), jci.name); + helperUsed[0] = true; + } + }; + + while (it.hasNext()) { + JCStatement stat = it.next(); + stat.accept(visitor, null); + } + + if (!helperUsed[0]) { + annotationNode.addWarning("No methods of this helper class are ever used."); + return; + } + + ListBuffer<JCStatement> newStatements = new ListBuffer<JCStatement>(); + + boolean mark = false; + for (JCStatement stat : origStatements) { + newStatements.append(stat); + if (mark || stat != annotatedType_) continue; + mark = true; + JCExpression init = maker.NewClass(null, List.<JCExpression>nil(), maker.Ident(annotatedType_.name), List.<JCExpression>nil(), null); + JCExpression varType = maker.Ident(annotatedType_.name); + JCVariableDecl decl = maker.VarDef(maker.Modifiers(Flags.FINAL), helperName, varType, init); + newStatements.append(decl); + } + amd.body.stats = newStatements.toList(); + } +} |