diff options
18 files changed, 506 insertions, 9 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown index a9bc5742..14baeb49 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -2,6 +2,7 @@ Lombok Changelog ---------------- ### v1.16.5 "Edgy Guinea Pig" +* FEATURE: `@Helper` can be placed on method-local inner classes to make all methods in the class accessible to the rest of the method. [Full documentation](https://projectlombok.org/features/experimental/Helper.html). * FEATURE: `@Builder(toBuilder = true)` is now available. It produces an instance method that creates a new builder, initialized with all the values of that instance. For more, read the [Feature page on Builder](https://projectlombok.org/features/Builder.html). * FEATURE: the `hashCode()` method generated by lombok via `@EqualsAndHashCode`, `@Data`, and `@Value` is now smarter about nulls; they are treated as if they hash to a magic prime instead of 0, which reduces hash collisions. * BUGFIX: Parameterized static methods with `@Builder` would produce compiler errors in javac. [Issue #828](https://github.com/rzwitserloot/lombok/issues/828). diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 6c595504..67d51895 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -402,14 +402,14 @@ public class ConfigurationKeys { */ public static final ConfigurationKey<FlagUsageType> FIELD_DEFAULTS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.fieldDefaults.flagUsage", "Emit a warning or error if @FieldDefaults is used.") {}; - // ----- Wither ----- + // ----- Helper ----- /** - * lombok configuration: {@code lombok.wither.flagUsage} = {@code WARNING} | {@code ERROR}. + * lombok configuration: {@code lombok.helper.flagUsage} = {@code WARNING} | {@code ERROR}. * - * If set, <em>any</em> usage of {@code @Wither} results in a warning / error. + * If set, <em>any</em> usage of {@code @Helper} results in a warning / error. */ - public static final ConfigurationKey<FlagUsageType> WITHER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.wither.flagUsage", "Emit a warning or error if @Wither is used.") {}; + public static final ConfigurationKey<FlagUsageType> HELPER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.helper.flagUsage", "Emit a warning or error if @Helper is used.") {}; // ----- UtilityClass ----- @@ -418,7 +418,16 @@ public class ConfigurationKeys { * * If set, <em>any</em> usage of {@code @UtilityClass} results in a warning / error. */ - public static final ConfigurationKey<FlagUsageType> UTLITY_CLASS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.utilityClass.flagUsage", "Emit a warning or error if @UtilityClass is used.") {}; + public static final ConfigurationKey<FlagUsageType> UTILITY_CLASS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.utilityClass.flagUsage", "Emit a warning or error if @UtilityClass is used.") {}; + + // ----- Wither ----- + + /** + * lombok configuration: {@code lombok.wither.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Wither} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> WITHER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.wither.flagUsage", "Emit a warning or error if @Wither is used.") {}; // ----- Configuration System ----- diff --git a/src/core/lombok/eclipse/EclipseASTVisitor.java b/src/core/lombok/eclipse/EclipseASTVisitor.java index aa19adc6..f5b49cbb 100644 --- a/src/core/lombok/eclipse/EclipseASTVisitor.java +++ b/src/core/lombok/eclipse/EclipseASTVisitor.java @@ -40,7 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; /** - * Implement so you can ask any JavacAST.Node to traverse depth-first through all children, + * Implement so you can ask any EclipseAST.Node to traverse depth-first through all children, * calling the appropriate visit and endVisit methods. */ public interface EclipseASTVisitor { diff --git a/src/core/lombok/eclipse/handlers/HandleHelper.java b/src/core/lombok/eclipse/handlers/HandleHelper.java new file mode 100644 index 00000000..4e9a7c68 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleHelper.java @@ -0,0 +1,137 @@ +/* + * 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.eclipse.handlers; + +import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.ThisReference; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.mangosdk.spi.ProviderFor; + +import lombok.ConfigurationKeys; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; +import lombok.experimental.Helper; + +/** + * Handles the {@code lombok.Cleanup} annotation for eclipse. + */ +@ProviderFor(EclipseAnnotationHandler.class) +public class HandleHelper extends EclipseAnnotationHandler<Helper> { + @Override public void handle(AnnotationValues<Helper> annotation, Annotation ast, EclipseNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper"); + + EclipseNode annotatedType = annotationNode.up(); + EclipseNode 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; + } + + TypeDeclaration annotatedType_ = (TypeDeclaration) annotatedType.get(); + AbstractMethodDeclaration amd = (AbstractMethodDeclaration) containingMethod.get(); + Statement[] origStatements = amd.statements; + int indexOfType = -1; + for (int i = 0; i < origStatements.length; i++) { + if (origStatements[i] == annotatedType_) { + indexOfType = i; + break; + } + } + + final List<String> knownMethodNames = new ArrayList<String>(); + + for (AbstractMethodDeclaration methodOfHelper : annotatedType_.methods) { + if (!(methodOfHelper instanceof MethodDeclaration)) continue; + char[] name = methodOfHelper.selector; + if (name != null && name.length > 0 && name[0] != '<') knownMethodNames.add(new String(name)); + } + + Collections.sort(knownMethodNames); + final String[] knownMethodNames_ = knownMethodNames.toArray(new String[knownMethodNames.size()]); + + final char[] helperName = new char[annotatedType_.name.length + 1]; + final boolean[] helperUsed = new boolean[1]; + helperName[0] = '$'; + System.arraycopy(annotatedType_.name, 0, helperName, 1, helperName.length - 1); + + ASTVisitor visitor = new ASTVisitor() { + @Override public boolean visit(MessageSend messageSend, BlockScope scope) { + if (messageSend.receiver instanceof ThisReference) { + if ((((ThisReference) messageSend.receiver).bits & ASTNode.IsImplicitThis) == 0) return true; + } else if (messageSend.receiver != null) return true; + + char[] name = messageSend.selector; + if (name == null || name.length == 0 || name[0] == '<') return true; + String n = new String(name); + if (Arrays.binarySearch(knownMethodNames_, n) < 0) return true; + messageSend.receiver = new SingleNameReference(helperName, Eclipse.pos(messageSend)); + helperUsed[0] = true; + return true; + } + }; + + for (int i = indexOfType + 1; i < origStatements.length; i++) { + origStatements[i].traverse(visitor, null); + } + + if (!helperUsed[0]) { + annotationNode.addWarning("No methods of this helper class are ever used."); + return; + } + + Statement[] newStatements = new Statement[origStatements.length + 1]; + System.arraycopy(origStatements, 0, newStatements, 0, indexOfType + 1); + System.arraycopy(origStatements, indexOfType + 1, newStatements, indexOfType + 2, origStatements.length - indexOfType - 1); + LocalDeclaration decl = new LocalDeclaration(helperName, 0, 0); + decl.modifiers |= ClassFileConstants.AccFinal; + AllocationExpression alloc = new AllocationExpression(); + alloc.type = new SingleTypeReference(annotatedType_.name, 0L); + decl.initialization = alloc; + decl.type = new SingleTypeReference(annotatedType_.name, 0L); + SetGeneratedByVisitor sgbvVisitor = new SetGeneratedByVisitor(annotationNode.get()); + decl.traverse(sgbvVisitor, null); + newStatements[indexOfType + 1] = decl; + amd.statements = newStatements; + } +} diff --git a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java index c3c85ad4..199ce102 100644 --- a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java +++ b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java @@ -60,7 +60,7 @@ import lombok.experimental.UtilityClass; @ProviderFor(EclipseAnnotationHandler.class) public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> { @Override public void handle(AnnotationValues<UtilityClass> annotation, Annotation ast, EclipseNode annotationNode) { - handleFlagUsage(annotationNode, ConfigurationKeys.UTLITY_CLASS_FLAG_USAGE, "@UtilityClass"); + handleFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass"); EclipseNode typeNode = annotationNode.up(); if (!checkLegality(typeNode, annotationNode)) return; diff --git a/src/core/lombok/experimental/Helper.java b/src/core/lombok/experimental/Helper.java new file mode 100644 index 00000000..34745cbe --- /dev/null +++ b/src/core/lombok/experimental/Helper.java @@ -0,0 +1,35 @@ +/* + * 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.experimental; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Use on a method local class to indicate that all methods inside should be exposed to the rest of + * the method as if they were helper methods. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface Helper {} 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(); + } +} diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java index a4f8cb45..010c05a5 100644 --- a/src/core/lombok/javac/handlers/HandleUtilityClass.java +++ b/src/core/lombok/javac/handlers/HandleUtilityClass.java @@ -52,7 +52,7 @@ import com.sun.tools.javac.util.Name; @ProviderFor(JavacAnnotationHandler.class) public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> { @Override public void handle(AnnotationValues<UtilityClass> annotation, JCAnnotation ast, JavacNode annotationNode) { - handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.UTLITY_CLASS_FLAG_USAGE, "@UtilityClass"); + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass"); deleteAnnotationIfNeccessary(annotationNode, UtilityClass.class); diff --git a/test/transform/resource/after-delombok/Helper.java b/test/transform/resource/after-delombok/Helper.java new file mode 100644 index 00000000..52f50dd2 --- /dev/null +++ b/test/transform/resource/after-delombok/Helper.java @@ -0,0 +1,16 @@ +class HelperTest { + void test() { + class H1 { + void foo() { + System.out.println("Hello"); + } + } + final H1 $H1 = new H1(); + $H1.foo(); + class H2 { + void bar() { + $H1.foo(); + } + } + } +} diff --git a/test/transform/resource/after-ecj/Helper.java b/test/transform/resource/after-ecj/Helper.java new file mode 100644 index 00000000..1b4e8bd2 --- /dev/null +++ b/test/transform/resource/after-ecj/Helper.java @@ -0,0 +1,26 @@ +import lombok.experimental.Helper; +class HelperTest { + HelperTest() { + super(); + } + void test() { + @Helper class H1 { + H1() { + super(); + } + void foo() { + System.out.println("Hello"); + } + } + final H1 $H1 = new H1(); + $H1.foo(); + @Helper class H2 { + H2() { + super(); + } + void bar() { + $H1.foo(); + } + } + } +} diff --git a/test/transform/resource/before/Helper.java b/test/transform/resource/before/Helper.java new file mode 100644 index 00000000..145d740f --- /dev/null +++ b/test/transform/resource/before/Helper.java @@ -0,0 +1,19 @@ +import lombok.experimental.Helper; + +class HelperTest { + void test() { + @Helper class H1 { + void foo() { + System.out.println("Hello"); + } + } + + foo(); + + @Helper class H2 { + void bar() { + foo(); + } + } + } +}
\ No newline at end of file diff --git a/test/transform/resource/messages-delombok/Helper.java.messages b/test/transform/resource/messages-delombok/Helper.java.messages new file mode 100644 index 00000000..05260c0b --- /dev/null +++ b/test/transform/resource/messages-delombok/Helper.java.messages @@ -0,0 +1 @@ +13 No methods of this helper class are ever used.
\ No newline at end of file diff --git a/test/transform/resource/messages-ecj/Helper.java.messages b/test/transform/resource/messages-ecj/Helper.java.messages new file mode 100644 index 00000000..7207e136 --- /dev/null +++ b/test/transform/resource/messages-ecj/Helper.java.messages @@ -0,0 +1 @@ +13 No methods of this helper class are ever used. diff --git a/usage_examples/experimental/Helper_post.jpage b/usage_examples/experimental/Helper_post.jpage new file mode 100644 index 00000000..04a97b9f --- /dev/null +++ b/usage_examples/experimental/Helper_post.jpage @@ -0,0 +1,14 @@ +public class HelperExample { + int someMethod(int arg1) { + int localVar = 5; + + class Helpers { + int helperMethod(int arg) { + return arg + localVar; + } + } + Helpers $Helpers = new Helpers(); + + return $Helpers.helperMethod(10); + } +} diff --git a/usage_examples/experimental/Helper_pre.jpage b/usage_examples/experimental/Helper_pre.jpage new file mode 100644 index 00000000..cd86ef3c --- /dev/null +++ b/usage_examples/experimental/Helper_pre.jpage @@ -0,0 +1,15 @@ +import lombok.experimental.Helper; + +public class HelperExample { + int someMethod(int arg1) { + int localVar = 5; + + @Helper class Helpers { + int helperMethod(int arg) { + return arg + localVar; + } + } + + return helperMethod(10); + } +} diff --git a/website/features/experimental/Helper.html b/website/features/experimental/Helper.html new file mode 100644 index 00000000..7658b780 --- /dev/null +++ b/website/features/experimental/Helper.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html><head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <link rel="stylesheet" type="text/css" href="../../logi/reset.css" /> + <link rel="stylesheet" type="text/css" href="../features.css" /> + <link rel="shortcut icon" href="../../favicon.ico" type="image/x-icon" /> + <meta name="description" content="Spice up your java" /> + <title>@Helper</title> +</head><body><div id="pepper"> + <div class="minimumHeight"></div> + <div class="meat"> + <div class="header"><a href="../../index.html">Project Lombok</a></div> + <h1>@Helper</h1> + <div class="byline">With a little help from my friends... Helper methods for java.</div> + <div class="since"> + <h3>Since</h3> + <p> + <code>@Helper</code> was introduced as an experimental feature in lombok v1.16.6. + </p> + </div> + <div class="experimental"> + <h3>Experimental</h3> + <p> + Experimental because: + <ul> + <li>Lambdas with general function types offer an alternative strategy.</li> + <li>Perhaps a way to make helper methods with less boilerplate is possible, making this feature obsolete.</li> + </ul> + Current status: <em>unknown</em> - We don't have enough experience with this feature to make predictions on its future. + </div> + <div class="overview"> + <h3>Overview</h3> + <p> + This annotation lets you put methods in methods. You might not know this, but you can declare classes inside methods, and the methods in this class can access any (effectively) final local variable or parameter defined and set before the declaration. Unfortunately, to actually call any methods you'd have to make an instance of this method local class first, but that's where <code>@Helper</code> comes in and helps you out! Annotate a method local class with <code>@Helper</code> and it's as if all the methods in that helper class are methods that you can call directly, just as if java had allowed methods to exist inside methods. + </p><p> + Normally you'd have to declare an instance of your helper, for example: <code>HelperClass h = new HelperClass();</code> directly after declaring your helper class, and then call methods in your helper class with <code>h.helperMethod();</code>. With <code>@Helper</code>, both of these things are no longer needed: You do not need to waste a line of code declaring an instance of the helper, and you don't need to prefix all your calls to helper methods with <code>nameOfHelperInstance.</code> + </p> + </div> + <div class="snippets"> + <div class="pre"> + <h3>With Lombok</h3> + <div class="snippet">@HTML_PRE@</div> + </div> + <div class="sep"></div> + <div class="post"> + <h3>Vanilla Java</h3> + <div class="snippet">@HTML_POST@</div> + </div> + </div> + <div style="clear: left;"></div> + <div class="overview"> + <h3>Small print</h3><div class="smallprint"> + <p> + <code>@Helper</code> requires that the helper class has a no-args constructor. A compiler error will be generated if this is not the case. + </p><p> + Currently, the instance of your helper that's made under the hood is called <code>$Foo</code>, where <code>Foo</code> is the name of your helper. We might change this in the future; please don't rely on this variable existing. We might even replace this later with a sibling method instead. + </p><p> + Please don't rely on <code>this</code> making any sense in the helper method code. You can refer to the real 'this' by using the syntax <code>NameOfMyClass.this</code>. + </p><p> + <em>ANY</em> unqualified method call in code that exists <em>below</em> the declaration of the helper method with the same name as any method in the helper is assumed to be a call to the helper. If the arguments don't end up being compatible, you get a compiler error. + </p><p> + Unless you're using JDK8 or higher (which introduced the concept of 'effectively final'), you'll have to declare local variables and parameters as <code>final</code> if you wish to refer to them in your method local class. This is a java limitation, not something specific to lombok's <code>@Helper</code>. + </p> + </div> + </div> + <div class="footer"> + <a href="index.html">Back to experimental features</a> | <a href="UtilityClass.html">Previous feature (@UtilityClass)</a> | <span class="disabled">Next feature</span><br /> + <a href="../../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2015 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> + </div> + <div style="clear: both;"></div> + </div> +</div> +<script type="text/javascript"> + var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); + document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); +</script> +<script type="text/javascript"> + try { + var pageTracker = _gat._getTracker("UA-9884254-1"); + pageTracker._trackPageview(); + } catch(err) {} +</script> +</body></html> diff --git a/website/features/experimental/UtilityClass.html b/website/features/experimental/UtilityClass.html index 5526ec77..3c79b35e 100644 --- a/website/features/experimental/UtilityClass.html +++ b/website/features/experimental/UtilityClass.html @@ -61,7 +61,7 @@ </div> </div> <div class="footer"> - <a href="index.html">Back to experimental features</a> | <a href="onX.html">Previous feature (onX)</a> | <span class="disabled">Next feature</span><br /> + <a href="index.html">Back to experimental features</a> | <a href="onX.html">Previous feature (onX)</a> | <a href="Helper.html">Next feature (@Helper)</a><br /> <a href="../../credits.html" class="creditsLink">credits</a> | <span class="copyright">Copyright © 2009-2015 The Project Lombok Authors, licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a>.</span> </div> <div style="clear: both;"></div> diff --git a/website/features/experimental/index.html b/website/features/experimental/index.html index 3f2d2802..ce21f819 100644 --- a/website/features/experimental/index.html +++ b/website/features/experimental/index.html @@ -36,6 +36,8 @@ <dd>Sup dawg, we heard you like annotations, so we put annotations in your annotations so you can annotate while you're annotating.</dd> <dt><a href="UtilityClass.html"><code>@UtilityClass</code></a></dt> <dd>Utility, metility, wetility! Utility classes for the masses.</dd> + <dt><a href="Helper.html"><code>@Helper</code></a></dt> + <dd>With a little help from my friends... Helper methods for java.</dd> </dl> </div> <div class="overview confKeys"> |