aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/changelog.markdown1
-rw-r--r--src/core/lombok/Delegate.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleDelegate.java67
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java195
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java4
-rw-r--r--test/transform/resource/after-delombok/DelegateOnMethods.java12
-rw-r--r--test/transform/resource/after-ecj/DelegateOnMethods.java1
-rw-r--r--test/transform/resource/after-eclipse/DelegateOnMethods.java13
-rw-r--r--test/transform/resource/before/DelegateOnMethods.java11
-rw-r--r--website/features/Delegate.html14
10 files changed, 243 insertions, 77 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index 7db9be55..baadbb9f 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -2,6 +2,7 @@ Lombok Changelog
----------------
### v0.10.7 (edge)
+* FEATURE: `@Delegate` can now be used on a no-argument method, which works similarly to adding it to fields. See [documentation](http://projectlombok.org/features/Delegate.html).
* REGRESSION: Eclipse save action *Add final modifier to private fields* again adds final keyword to `@Setter` fields, caused by fix to [issue #325](http://code.google.com/p/projectlombok/issues/detail?id=325). Reopened [Issue #263](http://code.google.com/p/projectlombok/issues/detail?id=263)
* BUGFIX: Eclipse refactoring Extract Interface was broken when using lombok annotation to generate methods. [Issue #86](http://code.google.com/p/projectlombok/issues/detail?id=86)
* BUGFIX: Eclipse action Sort Members was broken when using lombok annotations to generate methods or fields. [Issue #265](http://code.google.com/p/projectlombok/issues/detail?id=265)
diff --git a/src/core/lombok/Delegate.java b/src/core/lombok/Delegate.java
index 6d03d649..9ab9acae 100644
--- a/src/core/lombok/Delegate.java
+++ b/src/core/lombok/Delegate.java
@@ -40,7 +40,7 @@ import java.lang.annotation.Target;
* that exist in {@link Object}, the {@code canEqual(Object)} method, and any methods that appear in types
* that are listed in the {@code excludes} property.
*/
-@Target(ElementType.FIELD)
+@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Delegate {
/**
diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java
index f6a81474..3674ae5a 100644
--- a/src/core/lombok/javac/handlers/HandleDelegate.java
+++ b/src/core/lombok/javac/handlers/HandleDelegate.java
@@ -94,23 +94,42 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
@Override public void handle(AnnotationValues<Delegate> annotation, JCAnnotation ast, JavacNode annotationNode) {
deleteAnnotationIfNeccessary(annotationNode, Delegate.class);
- if (annotationNode.up().getKind() != Kind.FIELD) {
- // As the annotation is legal on fields only, javac itself will take care of printing an error message for this.
+
+ Type delegateType;
+ Name delegateName = annotationNode.toName(annotationNode.up().getName());
+ DelegateReceiver delegateReceiver;
+ JavacResolution reso = new JavacResolution(annotationNode.getContext());
+ if (annotationNode.up().getKind() == Kind.FIELD) {
+ delegateReceiver = DelegateReceiver.FIELD;
+ delegateType = annotationNode.up().get().type;
+ if (delegateType == null) reso.resolveClassMember(annotationNode.up());
+ delegateType = annotationNode.up().get().type;
+ } else if (annotationNode.up().getKind() == Kind.METHOD) {
+ if (!(annotationNode.up().get() instanceof JCMethodDecl)) {
+ annotationNode.addError("@Delegate is legal only on no-argument methods.");
+ return;
+ }
+ JCMethodDecl methodDecl = (JCMethodDecl) annotationNode.up().get();
+ if (!methodDecl.params.isEmpty()) {
+ annotationNode.addError("@Delegate is legal only on no-argument methods.");
+ return;
+ }
+ delegateReceiver = DelegateReceiver.METHOD;
+ delegateType = methodDecl.restype.type;
+ if (delegateType == null) reso.resolveClassMember(annotationNode.up());
+ delegateType = methodDecl.restype.type;
+ } else {
+ // As the annotation is legal on fields and methods only, javac itself will take care of printing an error message for this.
return;
}
List<Object> delegateTypes = annotation.getActualExpressions("types");
List<Object> excludeTypes = annotation.getActualExpressions("excludes");
- JavacResolution reso = new JavacResolution(annotationNode.getContext());
List<Type> toDelegate = new ArrayList<Type>();
List<Type> toExclude = new ArrayList<Type>();
if (delegateTypes.isEmpty()) {
- Type type = ((JCVariableDecl)annotationNode.up().get()).type;
- if (type == null) reso.resolveClassMember(annotationNode.up());
- //TODO I'm fairly sure the above line (and that entire method) does effectively bupkis!
- type = ((JCVariableDecl)annotationNode.up().get()).type;
- if (type != null) toDelegate.add(type);
+ if (delegateType != null) toDelegate.add(delegateType);
} else {
for (Object dt : delegateTypes) {
if (dt instanceof JCFieldAccess && ((JCFieldAccess)dt).name.toString().equals("class")) {
@@ -167,15 +186,13 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
}
}
- Name delegateFieldName = annotationNode.toName(annotationNode.up().getName());
-
- for (MethodSig sig : signaturesToDelegate) generateAndAdd(sig, annotationNode, delegateFieldName);
+ for (MethodSig sig : signaturesToDelegate) generateAndAdd(sig, annotationNode, delegateName, delegateReceiver);
}
- private void generateAndAdd(MethodSig sig, JavacNode annotation, Name delegateFieldName) {
+ private void generateAndAdd(MethodSig sig, JavacNode annotation, Name delegateName, DelegateReceiver delegateReceiver) {
List<JCMethodDecl> toAdd = new ArrayList<JCMethodDecl>();
try {
- toAdd.add(createDelegateMethod(sig, annotation, delegateFieldName));
+ toAdd.add(createDelegateMethod(sig, annotation, delegateName, delegateReceiver));
} catch (TypeNotConvertibleException e) {
annotation.addError("Can't create delegate method for " + sig.name + ": " + e.getMessage());
return;
@@ -240,7 +257,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
}
}
- private JCMethodDecl createDelegateMethod(MethodSig sig, JavacNode annotation, Name delegateFieldName) throws TypeNotConvertibleException, CantMakeDelegates {
+ private JCMethodDecl createDelegateMethod(MethodSig sig, JavacNode annotation, Name delegateName, DelegateReceiver delegateReceiver) throws TypeNotConvertibleException, CantMakeDelegates {
/* public <T, U, ...> ReturnType methodName(ParamType1 name1, ParamType2 name2, ...) throws T1, T2, ... {
* (return) delegate.<T, U>methodName(name1, name2);
* }
@@ -288,9 +305,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
args.append(maker.Ident(name));
}
- JCExpression delegateFieldRef = maker.Select(maker.Ident(annotation.toName("this")), delegateFieldName);
-
- JCExpression delegateCall = maker.Apply(toList(typeArgs), maker.Select(delegateFieldRef, sig.name), toList(args));
+ JCExpression delegateCall = maker.Apply(toList(typeArgs), maker.Select(delegateReceiver.get(annotation, delegateName), sig.name), toList(args));
JCStatement body = useReturn ? maker.Return(delegateCall) : maker.Exec(delegateCall);
JCBlock bodyBlock = maker.Block(0, com.sun.tools.javac.util.List.of(body));
@@ -367,4 +382,22 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
binding = types.erasure(binding);
return binding.toString();
}
+
+ private enum DelegateReceiver {
+ METHOD {
+ public JCExpression get(final JavacNode node, final Name name) {
+ com.sun.tools.javac.util.List<JCExpression> nilExprs = com.sun.tools.javac.util.List.nil();
+ final TreeMaker maker = node.getTreeMaker();
+ return maker.Apply(nilExprs, maker.Select(maker.Ident(node.toName("this")), name), nilExprs);
+ }
+ },
+ FIELD {
+ public JCExpression get(final JavacNode node, final Name name) {
+ final TreeMaker maker = node.getTreeMaker();
+ return maker.Select(maker.Ident(node.toName("this")), name);
+ }
+ };
+
+ public abstract JCExpression get(final JavacNode node, final Name name);
+ }
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
index affcd4f2..e510d37b 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
@@ -103,25 +103,25 @@ public class PatchDelegate {
return new String(decl.name);
}
- private static boolean hasDelegateMarkedFields(TypeDeclaration decl) {
+ private static boolean hasDelegateMarkedFieldsOrMethods(TypeDeclaration decl) {
if (decl.fields != null) for (FieldDeclaration field : decl.fields) {
if (field.annotations == null) continue;
for (Annotation ann : field.annotations) {
- if (ann.type == null) continue;
- TypeBinding tb = ann.type.resolveType(decl.initializerScope);
- if (tb == null) continue;
- if (!charArrayEquals("lombok", tb.qualifiedPackageName())) continue;
- if (!charArrayEquals("Delegate", tb.qualifiedSourceName())) continue;
- return true;
+ if (isDelegate(ann, decl)) return true;
+ }
+ }
+ if (decl.methods != null) for (AbstractMethodDeclaration method : decl.methods) {
+ if (method.annotations == null) continue;
+ for (Annotation ann : method.annotations) {
+ if (isDelegate(ann, decl)) return true;
}
}
-
return false;
}
public static boolean handleDelegateForType(ClassScope scope) {
if (TransformEclipseAST.disableLombok) return false;
- if (!hasDelegateMarkedFields(scope.referenceContext)) return false;
+ if (!hasDelegateMarkedFieldsOrMethods(scope.referenceContext)) return false;
List<ClassScopeEntry> stack = visited.get();
StringBuilder corrupted = null;
@@ -145,16 +145,23 @@ public class PatchDelegate {
stack.add(entry);
try {
- List<BindingTuple> methodsToDelegate = new ArrayList<BindingTuple>();
TypeDeclaration decl = scope.referenceContext;
if (decl != null) {
CompilationUnitDeclaration cud = scope.compilationUnitScope().referenceContext;
EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
- fillMethodBindings(cud, scope, methodsToDelegate);
+ List<BindingTuple> methodsToDelegate = new ArrayList<BindingTuple>();
+ fillMethodBindingsForFields(cud, scope, methodsToDelegate);
if (entry.corruptedPath != null) {
eclipseAst.get(scope.referenceContext).addError("No @Delegate methods created because there's a loop: " + entry.corruptedPath);
} else {
- generateDelegateMethods(eclipseAst.get(decl), methodsToDelegate);
+ generateDelegateMethods(eclipseAst.get(decl), methodsToDelegate, DelegateReceiver.FIELD);
+ }
+ methodsToDelegate.clear();
+ fillMethodBindingsForMethods(cud, scope, methodsToDelegate);
+ if (entry.corruptedPath != null) {
+ eclipseAst.get(scope.referenceContext).addError("No @Delegate methods created because there's a loop: " + entry.corruptedPath);
+ } else {
+ generateDelegateMethods(eclipseAst.get(decl), methodsToDelegate, DelegateReceiver.METHOD);
}
}
} finally {
@@ -181,44 +188,25 @@ public class PatchDelegate {
private static Map<ASTNode, Object> alreadyApplied = new WeakHashMap<ASTNode, Object>();
private static final Object MARKER = new Object();
- private static void fillMethodBindings(CompilationUnitDeclaration cud, ClassScope scope, List<BindingTuple> methodsToDelegate) {
+ private static void fillMethodBindingsForFields(CompilationUnitDeclaration cud, ClassScope scope, List<BindingTuple> methodsToDelegate) {
TypeDeclaration decl = scope.referenceContext;
if (decl == null) return;
if (decl.fields != null) for (FieldDeclaration field : decl.fields) {
if (field.annotations == null) continue;
for (Annotation ann : field.annotations) {
- if (ann.type == null) continue;
- TypeBinding tb = ann.type.resolveType(decl.initializerScope);
- if (!charArrayEquals("lombok", tb.qualifiedPackageName())) continue;
- if (!charArrayEquals("Delegate", tb.qualifiedSourceName())) continue;
+ if (!isDelegate(ann, decl)) continue;
if (alreadyApplied.put(ann, MARKER) == MARKER) continue;
- List<ClassLiteralAccess> rawTypes = new ArrayList<ClassLiteralAccess>();
- List<ClassLiteralAccess> excludedRawTypes = new ArrayList<ClassLiteralAccess>();
- for (MemberValuePair pair : ann.memberValuePairs()) {
- if (charArrayEquals("types", pair.name)) {
- if (pair.value instanceof ArrayInitializer) {
- for (Expression expr : ((ArrayInitializer)pair.value).expressions) {
- if (expr instanceof ClassLiteralAccess) rawTypes.add((ClassLiteralAccess) expr);
- }
- }
- if (pair.value instanceof ClassLiteralAccess) {
- rawTypes.add((ClassLiteralAccess) pair.value);
- }
- }
- if (charArrayEquals("excludes", pair.name)) {
- if (pair.value instanceof ArrayInitializer) {
- for (Expression expr : ((ArrayInitializer)pair.value).expressions) {
- if (expr instanceof ClassLiteralAccess) excludedRawTypes.add((ClassLiteralAccess) expr);
- }
- }
- if (pair.value instanceof ClassLiteralAccess) {
- excludedRawTypes.add((ClassLiteralAccess) pair.value);
- }
- }
+ if ((field.modifiers & ClassFileConstants.AccStatic) != 0) {
+ EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
+ eclipseAst.get(ann).addError(LEGALITY_OF_DELEGATE);
+ break;
}
+ List<ClassLiteralAccess> rawTypes = rawTypes(ann, "types");
+ List<ClassLiteralAccess> excludedRawTypes = rawTypes(ann, "excludes");
+
List<BindingTuple> methodsToExclude = new ArrayList<BindingTuple>();
for (ClassLiteralAccess cla : excludedRawTypes) {
addAllMethodBindings(methodsToExclude, cla.type.resolveType(decl.initializerScope), new HashSet<String>(), field.name, ann);
@@ -251,6 +239,95 @@ public class PatchDelegate {
}
}
+ private static final String LEGALITY_OF_DELEGATE = "@Delegate is legal only on instance fields or no-argument instance methods.";
+
+ private static void fillMethodBindingsForMethods(CompilationUnitDeclaration cud, ClassScope scope, List<BindingTuple> methodsToDelegate) {
+ TypeDeclaration decl = scope.referenceContext;
+ if (decl == null) return;
+
+ if (decl.methods != null) for (AbstractMethodDeclaration methodDecl : decl.methods) {
+ if (methodDecl.annotations == null) continue;
+ for (Annotation ann : methodDecl.annotations) {
+ if (!isDelegate(ann, decl)) continue;
+ if (alreadyApplied.put(ann, MARKER) == MARKER) continue;
+ if (!(methodDecl instanceof MethodDeclaration)) {
+ EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
+ eclipseAst.get(ann).addError(LEGALITY_OF_DELEGATE);
+ break;
+ }
+ if (methodDecl.arguments != null) {
+ EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
+ eclipseAst.get(ann).addError(LEGALITY_OF_DELEGATE);
+ break;
+ }
+ if ((methodDecl.modifiers & ClassFileConstants.AccStatic) != 0) {
+ EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
+ eclipseAst.get(ann).addError(LEGALITY_OF_DELEGATE);
+ break;
+ }
+ MethodDeclaration method = (MethodDeclaration) methodDecl;
+
+ List<ClassLiteralAccess> rawTypes = rawTypes(ann, "types");
+ List<ClassLiteralAccess> excludedRawTypes = rawTypes(ann, "excludes");
+
+ List<BindingTuple> methodsToExclude = new ArrayList<BindingTuple>();
+ for (ClassLiteralAccess cla : excludedRawTypes) {
+ addAllMethodBindings(methodsToExclude, cla.type.resolveType(decl.initializerScope), new HashSet<String>(), method.selector, ann);
+ }
+
+ Set<String> banList = new HashSet<String>();
+ for (BindingTuple excluded : methodsToExclude) banList.add(printSig(excluded.parameterized));
+
+ List<BindingTuple> methodsToDelegateForThisAnn = new ArrayList<BindingTuple>();
+
+ if (rawTypes.isEmpty()) {
+ addAllMethodBindings(methodsToDelegateForThisAnn, method.returnType.resolveType(decl.initializerScope), banList, method.selector, ann);
+ } else {
+ for (ClassLiteralAccess cla : rawTypes) {
+ addAllMethodBindings(methodsToDelegateForThisAnn, cla.type.resolveType(decl.initializerScope), banList, method.selector, ann);
+ }
+ }
+
+ // Not doing this right now because of problems - see commented-out-method for info.
+ // removeExistingMethods(methodsToDelegate, decl, scope);
+
+ String dupe = containsDuplicates(methodsToDelegateForThisAnn);
+ if (dupe != null) {
+ EclipseAST eclipseAst = TransformEclipseAST.getAST(cud, true);
+ eclipseAst.get(ann).addError("The method '" + dupe + "' is being delegated by more than one specified type.");
+ } else {
+ methodsToDelegate.addAll(methodsToDelegateForThisAnn);
+ }
+ }
+ }
+ }
+
+ private static boolean isDelegate(Annotation ann, TypeDeclaration decl) {
+ if (ann.type == null) return false;
+ TypeBinding tb = ann.type.resolveType(decl.initializerScope);
+ if (tb == null) return false;
+ if (!charArrayEquals("lombok", tb.qualifiedPackageName())) return false;
+ if (!charArrayEquals("Delegate", tb.qualifiedSourceName())) return false;
+ return true;
+ }
+
+ private static List<ClassLiteralAccess> rawTypes(Annotation ann, String name) {
+ List<ClassLiteralAccess> rawTypes = new ArrayList<ClassLiteralAccess>();
+ for (MemberValuePair pair : ann.memberValuePairs()) {
+ if (charArrayEquals(name, pair.name)) {
+ if (pair.value instanceof ArrayInitializer) {
+ for (Expression expr : ((ArrayInitializer)pair.value).expressions) {
+ if (expr instanceof ClassLiteralAccess) rawTypes.add((ClassLiteralAccess) expr);
+ }
+ }
+ if (pair.value instanceof ClassLiteralAccess) {
+ rawTypes.add((ClassLiteralAccess) pair.value);
+ }
+ }
+ }
+ return rawTypes;
+ }
+
/*
* We may someday finish this method. Steps to be completed:
*
@@ -321,11 +398,11 @@ public class PatchDelegate {
// }
// }
- private static void generateDelegateMethods(EclipseNode typeNode, List<BindingTuple> methods) {
+ private static void generateDelegateMethods(EclipseNode typeNode, List<BindingTuple> methods, DelegateReceiver delegateReceiver) {
CompilationUnitDeclaration top = (CompilationUnitDeclaration) typeNode.top().get();
for (BindingTuple pair : methods) {
EclipseNode annNode = typeNode.getAst().get(pair.responsible);
- MethodDeclaration method = createDelegateMethod(pair.fieldName, typeNode, pair, top.compilationResult, annNode);
+ MethodDeclaration method = createDelegateMethod(pair.fieldName, typeNode, pair, top.compilationResult, annNode, delegateReceiver);
if (method != null) {
SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(annNode.get());
method.traverse(visitor, ((TypeDeclaration)typeNode.get()).scope);
@@ -473,7 +550,7 @@ public class PatchDelegate {
}
}
- private static MethodDeclaration createDelegateMethod(char[] name, EclipseNode typeNode, BindingTuple pair, CompilationResult compilationResult, EclipseNode annNode) {
+ private static MethodDeclaration createDelegateMethod(char[] name, EclipseNode typeNode, BindingTuple pair, CompilationResult compilationResult, EclipseNode annNode, DelegateReceiver delegateReceiver) {
/* public <T, U, ...> ReturnType methodName(ParamType1 name1, ParamType2 name2, ...) throws T1, T2, ... {
* (return) delegate.<T, U>methodName(name1, name2);
* }
@@ -514,11 +591,7 @@ public class PatchDelegate {
call.sourceStart = pS; call.sourceEnd = pE;
call.nameSourcePosition = pos(source);
setGeneratedBy(call, source);
- FieldReference fieldRef = new FieldReference(name, pos(source));
- fieldRef.receiver = new ThisReference(pS, pE);
- setGeneratedBy(fieldRef, source);
- setGeneratedBy(fieldRef.receiver, source);
- call.receiver = fieldRef;
+ call.receiver = delegateReceiver.get(source, name);
call.selector = binding.selector;
if (binding.typeVariables != null && binding.typeVariables.length > 0) {
@@ -742,7 +815,31 @@ public class PatchDelegate {
if (s.length() != c.length) return false;
for (int i = 0; i < s.length(); i++) if (s.charAt(i) != c[i]) return false;
return true;
+ }
+
+ private enum DelegateReceiver {
+ METHOD {
+ public Expression get(final ASTNode source, char[] name) {
+ MessageSend call = new MessageSend();
+ call.sourceStart = source.sourceStart; call.sourceEnd = source.sourceEnd;
+ call.nameSourcePosition = pos(source);
+ setGeneratedBy(call, source);
+ call.selector = name;
+ call.receiver = new ThisReference(source.sourceStart, source.sourceEnd);
+ setGeneratedBy(call.receiver, source);
+ return call;
+ }
+ },
+ FIELD {
+ public Expression get(final ASTNode source, char[] name) {
+ FieldReference fieldRef = new FieldReference(name, pos(source));
+ setGeneratedBy(fieldRef, source);
+ fieldRef.receiver = new ThisReference(source.sourceStart, source.sourceEnd);
+ setGeneratedBy(fieldRef.receiver, source);
+ return fieldRef;
+ }
+ };
-
+ public abstract Expression get(final ASTNode source, char[] name);
}
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
index 5a6b8479..1da96256 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
@@ -19,7 +19,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package lombok.eclipse.agent;
import java.io.BufferedOutputStream;
@@ -27,7 +26,6 @@ import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.Stack;
@@ -42,9 +40,7 @@ import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.MethodDeclaration;
-import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.SimpleName;
-import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent;
import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent;
diff --git a/test/transform/resource/after-delombok/DelegateOnMethods.java b/test/transform/resource/after-delombok/DelegateOnMethods.java
new file mode 100644
index 00000000..b900f140
--- /dev/null
+++ b/test/transform/resource/after-delombok/DelegateOnMethods.java
@@ -0,0 +1,12 @@
+abstract class DelegateOnMethods {
+ public abstract Bar getBar();
+
+ public static interface Bar {
+ void bar(java.util.ArrayList<java.lang.String> list);
+ }
+
+ @java.lang.SuppressWarnings("all")
+ public void bar(final java.util.ArrayList<java.lang.String> list) {
+ this.getBar().bar(list);
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/DelegateOnMethods.java b/test/transform/resource/after-ecj/DelegateOnMethods.java
new file mode 100644
index 00000000..cb06d3c1
--- /dev/null
+++ b/test/transform/resource/after-ecj/DelegateOnMethods.java
@@ -0,0 +1 @@
+//ignore \ No newline at end of file
diff --git a/test/transform/resource/after-eclipse/DelegateOnMethods.java b/test/transform/resource/after-eclipse/DelegateOnMethods.java
new file mode 100644
index 00000000..37922e2a
--- /dev/null
+++ b/test/transform/resource/after-eclipse/DelegateOnMethods.java
@@ -0,0 +1,13 @@
+import lombok.Delegate;
+abstract class DelegateOnMethods {
+ public static interface Bar {
+ void bar(java.util.ArrayList<java.lang.String> list);
+ }
+ public @java.lang.SuppressWarnings("all") void bar(final java.util.ArrayList<java.lang.String> list) {
+ this.getBar().bar(list);
+ }
+ DelegateOnMethods() {
+ super();
+ }
+ public abstract @Delegate Bar getBar();
+} \ No newline at end of file
diff --git a/test/transform/resource/before/DelegateOnMethods.java b/test/transform/resource/before/DelegateOnMethods.java
new file mode 100644
index 00000000..1606e18c
--- /dev/null
+++ b/test/transform/resource/before/DelegateOnMethods.java
@@ -0,0 +1,11 @@
+import lombok.Delegate;
+
+abstract class DelegateOnMethods {
+
+ @Delegate
+ public abstract Bar getBar();
+
+ public static interface Bar {
+ void bar(java.util.ArrayList<java.lang.String> list);
+ }
+} \ No newline at end of file
diff --git a/website/features/Delegate.html b/website/features/Delegate.html
index 28cd0a6b..4872c2e9 100644
--- a/website/features/Delegate.html
+++ b/website/features/Delegate.html
@@ -15,20 +15,20 @@
<div class="overview">
<h3>Overview</h3>
<p>
- <em> NEW IN Lombok 0.10: </em> Any field can be annotated with <code>@Delegate</code> to let lombok generate delegate methods that forward the call
- to this field.
+ <em> NEW IN Lombok 0.10: </em> Any field or no-argument method can be annotated with <code>@Delegate</code> to let lombok generate delegate methods
+ that forward the call to this field (or the result of invoking this method).
</p>
<p>
- Lombok delegates all <code>public</code> methods of the field's type, as well as those of its supertype except for all methods declared
- in <code>java.lang.Object</code>.
+ Lombok delegates all <code>public</code> methods of the field's type (or method's return type), as well as those of its supertype except for all
+ methods declared in <code>java.lang.Object</code>.
</p>
<p>
You can pass any number of classes into the <code>@Delegate</code> annotation's <code>types</code> parameter.
If you do that, then lombok will delegate all <code>public</code> methods in those types (and their supertypes, except
- <code>java.lang.Object</code>) instead of looking at the field's type.
+ <code>java.lang.Object</code>) instead of looking at the field/method's type.
</p>
<p>
- All public non-Object methods that are part of the field's type (or, if you used <code>types</code> parameter, the methods of those types) are
+ All public non-<code>Object</code> methods that are part of the calculated type(s) are
copied, whether or not you also wrote implementations for those methods. That would thus result in duplicate method errors. You can avoid these
by using the <code>@Delegate(excludes=SomeType.class)</code> parameter to exclude all public methods in the excluded type(s), and their supertypes.
</p>
@@ -59,6 +59,8 @@
<p>
When passing classes to the annotation, these classes do not need to be supertypes of the field. See the example.
</p>
+ <p>
+ <code>@Delegate</code> cannot be used on static fields or methods.
<div>
</div>
<div class="footer">