aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReinier Zwitserloot <reinier@zwitserloot.com>2018-04-23 23:43:15 +0200
committerReinier Zwitserloot <reinier@zwitserloot.com>2018-05-14 22:03:46 +0200
commitad21a1573bab57c63ffd5b9867f8e19ac7f0c94b (patch)
tree6aeb4aff3490999ff799374cf9cbbbc33a5d03c5
parent82a7354a848a26021afd3a889cefd65db7693eb9 (diff)
downloadlombok-ad21a1573bab57c63ffd5b9867f8e19ac7f0c94b.tar.gz
lombok-ad21a1573bab57c63ffd5b9867f8e19ac7f0c94b.tar.bz2
lombok-ad21a1573bab57c63ffd5b9867f8e19ac7f0c94b.zip
[annotation based ToString] hey.. we have annotation based ToString now, where you can include/exclude fields by annotating the fields.
-rw-r--r--src/core/lombok/ToString.java33
-rw-r--r--src/core/lombok/core/LombokNode.java12
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java4
-rw-r--r--src/core/lombok/core/handlers/InclusionExclusionUtils.java206
-rw-r--r--src/core/lombok/eclipse/EclipseNode.java71
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java33
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java7
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java1
-rw-r--r--src/core/lombok/eclipse/handlers/HandleGetter.java1
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSetter.java17
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java140
-rw-r--r--src/core/lombok/eclipse/handlers/HandleWither.java1
-rw-r--r--src/core/lombok/javac/JavacNode.java71
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java8
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java1
-rw-r--r--src/core/lombok/javac/handlers/HandleGetter.java1
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java1
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java120
-rw-r--r--src/core/lombok/javac/handlers/HandleWither.java1
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java37
-rw-r--r--src/utils/lombok/eclipse/Eclipse.java12
-rw-r--r--test/transform/resource/after-delombok/ToStringNewStyle.java18
-rw-r--r--test/transform/resource/after-ecj/ToStringNewStyle.java20
-rw-r--r--test/transform/resource/before/ToStringNewStyle.java15
24 files changed, 639 insertions, 192 deletions
diff --git a/src/core/lombok/ToString.java b/src/core/lombok/ToString.java
index 0c43c40b..218e4c00 100644
--- a/src/core/lombok/ToString.java
+++ b/src/core/lombok/ToString.java
@@ -45,6 +45,8 @@ public @interface ToString {
/**
* Any fields listed here will not be printed in the generated {@code toString} implementation.
* Mutually exclusive with {@link #of()}.
+ * <p>
+ * Will soon be marked {@code @Deprecated}; use the {@code @ToString.Exclude} annotation instead.
*
* @return A list of fields to exclude.
*/
@@ -55,6 +57,8 @@ public @interface ToString {
* Normally, all non-static fields are printed.
* <p>
* Mutually exclusive with {@link #exclude()}.
+ * <p>
+ * Will soon be marked {@code @Deprecated}; use the {@code @ToString.Only} annotation instead.
*
* @return A list of fields to use (<em>default</em>: all of them).
*/
@@ -75,4 +79,33 @@ public @interface ToString {
* @return If {@code true}, always use direct field access instead of calling the getter method.
*/
boolean doNotUseGetters() default false;
+
+ /**
+ * Only include fields and methods explicitly marked with {@code @ToString.Include}.
+ * Normally, all (non-static) fields are included by default.
+ */
+ boolean onlyExplicitlyIncluded() default false;
+
+ /**
+ * If present, do not include this field in the generated {@code toString}.
+ */
+ @Target(ElementType.FIELD)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Exclude {}
+
+ /**
+ * Configure the behaviour of how this member is rendered in the {@code toString}; if on a method, include the method's return value in the output.
+ */
+ @Target({ElementType.FIELD, ElementType.METHOD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Include {
+// /** If true and the return value is {@code null}, omit this member entirely from the {@code toString} output. */
+// boolean skipNull() default false; // -- We'll add it later, it requires a complete rework on the toString code we generate.
+
+ /** Higher ranks are printed first. Members of the same rank are printed in the order they appear in the source file. */
+ int rank() default 0;
+
+ /** Defaults to the field / method name of the annotated member. If the name equals the name of a default-included field, this member takes its place. */
+ String name() default "";
+ }
}
diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java
index 07c62151..d6708956 100644
--- a/src/core/lombok/core/LombokNode.java
+++ b/src/core/lombok/core/LombokNode.java
@@ -21,6 +21,7 @@
*/
package lombok.core;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -275,4 +276,15 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
public boolean isStructurallySignificant() {
return isStructurallySignificant;
}
+
+ public abstract boolean hasAnnotation(Class<? extends Annotation> type);
+ public abstract <Z extends Annotation> AnnotationValues<Z> findAnnotation(Class<Z> type);
+
+ public abstract boolean isStatic();
+ public abstract boolean isTransient();
+ public abstract boolean isEnumMember();
+
+ public abstract int countMethodParameters();
+
+ public abstract int getStartPos();
}
diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
index 211b4924..0d64c550 100644
--- a/src/core/lombok/core/handlers/HandlerUtil.java
+++ b/src/core/lombok/core/handlers/HandlerUtil.java
@@ -57,6 +57,10 @@ import lombok.experimental.Wither;
public class HandlerUtil {
private HandlerUtil() {}
+ public enum FieldAccess {
+ GETTER, PREFER_FIELD, ALWAYS_FIELD;
+ }
+
public static int primeForHashcode() {
return 59;
}
diff --git a/src/core/lombok/core/handlers/InclusionExclusionUtils.java b/src/core/lombok/core/handlers/InclusionExclusionUtils.java
new file mode 100644
index 00000000..e2f686cd
--- /dev/null
+++ b/src/core/lombok/core/handlers/InclusionExclusionUtils.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2009-2018 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.core.handlers;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import lombok.ToString;
+import lombok.ToString.Include;
+import lombok.core.AST;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.LombokNode;
+
+public class InclusionExclusionUtils {
+ private static List<Integer> createListOfNonExistentFields(List<String> list, LombokNode<?, ?, ?> type, boolean excludeStandard, boolean excludeTransient) {
+ boolean[] matched = new boolean[list.size()];
+
+ for (LombokNode<?, ?, ?> child : type.down()) {
+ if (list.isEmpty()) break;
+ if (child.getKind() != Kind.FIELD) continue;
+ if (excludeStandard) {
+ if (child.isStatic()) continue;
+ if (child.getName().startsWith("$")) continue;
+ }
+ if (excludeTransient && child.isTransient()) continue;
+
+ int idx = list.indexOf(child.getName());
+ if (idx > -1) matched[idx] = true;
+ }
+
+ List<Integer> problematic = new ArrayList<Integer>();
+ for (int i = 0 ; i < list.size() ; i++) if (!matched[i]) problematic.add(i);
+
+ return problematic;
+ }
+
+ public static void checkForBogusFieldNames(LombokNode<?, ?, ?> type, AnnotationValues<?> annotation, List<String> excludes, List<String> includes) {
+ if (excludes != null && !excludes.isEmpty()) {
+ for (int i : createListOfNonExistentFields(excludes, type, true, false)) {
+ annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
+ }
+ }
+
+ if (includes != null && !includes.isEmpty()) {
+ for (int i : createListOfNonExistentFields(includes, type, false, false)) {
+ annotation.setWarning("of", "This field does not exist.", i);
+ }
+ }
+ }
+
+ public static class ToStringMember<L> {
+ private final L node;
+ private final ToString.Include inc;
+ private final boolean defaultInclude;
+
+ public ToStringMember(L node, ToString.Include inc, boolean defaultInclude) {
+ this.node = node;
+ this.inc = inc;
+ this.defaultInclude = defaultInclude;
+ }
+
+ public L getNode() {
+ return node;
+ }
+
+ public ToString.Include getInc() {
+ return inc;
+ }
+
+ public boolean isDefaultInclude() {
+ return defaultInclude;
+ }
+ }
+
+ public static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> List<ToStringMember<L>> handleToStringMarking(LombokNode<A, L, N> typeNode, AnnotationValues<ToString> annotation, LombokNode<A, L, N> annotationNode) {
+ ToString ann = annotation == null ? null : annotation.getInstance();
+ List<String> oldExcludes = (ann != null && annotation.isExplicit("exclude")) ? Arrays.asList(ann.exclude()) : null;
+ List<String> oldIncludes = (ann != null && annotation.isExplicit("of")) ? Arrays.asList(ann.of()) : null;
+
+ boolean onlyExplicitlyIncluded = ann != null && ann.onlyExplicitlyIncluded();
+ boolean memberAnnotationMode = onlyExplicitlyIncluded;
+ List<ToStringMember<L>> members = new ArrayList<ToStringMember<L>>();
+ List<String> namesToAutoExclude = new ArrayList<String>();
+
+ if (typeNode == null || typeNode.getKind() != Kind.TYPE) return null;
+
+ checkForBogusFieldNames(typeNode, annotation, oldExcludes, oldIncludes);
+
+ if (oldExcludes != null && oldIncludes != null) {
+ oldExcludes = null;
+ annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
+ }
+
+ for (L child : typeNode.down()) {
+ boolean markExclude = child.getKind() == Kind.FIELD && child.hasAnnotation(ToString.Exclude.class);
+ AnnotationValues<ToString.Include> markInclude = null;
+ if (child.getKind() == Kind.FIELD || child.getKind() == Kind.METHOD) markInclude = child.findAnnotation(ToString.Include.class);
+
+ if (markExclude || markInclude != null) memberAnnotationMode = true;
+
+ if (markInclude != null && markExclude) {
+ child.addError("@ToString.Exclude and @ToString.Include are mutually exclusive; the @Include annotation will be ignored");
+ markInclude = null;
+ }
+
+ String name = child.getName();
+
+ if (markExclude) {
+ if (onlyExplicitlyIncluded) {
+ child.addWarning("The @Exclude annotation is not needed; 'onlyExplicitlyIncluded' is set, so this member would be excluded anyway");
+ } else if (child.isStatic()) {
+ child.addWarning("The @Exclude annotation is not needed; static fields aren't included anyway");
+ } else if (name.startsWith("$")) {
+ child.addWarning("The @Exclude annotation is not needed; fields that start with $ aren't included anyway");
+ }
+ continue;
+ }
+
+ if (oldExcludes != null && oldExcludes.contains(name)) continue;
+
+ if (markInclude != null) {
+ Include inc = markInclude.getInstance();
+ if (child.getKind() == Kind.METHOD) {
+ if (child.countMethodParameters() > 0) {
+ child.addError("Methods included for @ToString must have no arguments; it will not be included");
+ continue;
+ }
+ String n = inc.name();
+ if (n.isEmpty()) n = name;
+ namesToAutoExclude.add(n);
+ }
+ members.add(new ToStringMember<L>(child, inc, false));
+ continue;
+ }
+
+ if (onlyExplicitlyIncluded) continue;
+ if (oldIncludes != null) {
+ if (child.getKind() == Kind.FIELD && oldIncludes.contains(name)) members.add(new ToStringMember<L>(child, null, false));
+ continue;
+ }
+ if (child.getKind() != Kind.FIELD) continue;
+ if (child.isStatic()) continue;
+ if (name.startsWith("$")) continue;
+ if (child.isEnumMember()) continue;
+ members.add(new ToStringMember<L>(child, null, true));
+ }
+
+ /* delete default-included fields with the same name as an explicit inclusion */ {
+ Iterator<ToStringMember<L>> it = members.iterator();
+ while (it.hasNext()) {
+ ToStringMember<L> m = it.next();
+ if (m.isDefaultInclude() && namesToAutoExclude.contains(m.getNode().getName())) it.remove();
+ }
+ }
+
+ if (annotation == null || !annotation.isExplicit("exclude")) oldExcludes = null;
+ if (annotation == null || !annotation.isExplicit("of")) oldIncludes = null;
+
+ if (memberAnnotationMode && (oldExcludes != null || oldIncludes != null)) {
+ annotationNode.addError("The old-style 'exclude/of' parameter cannot be used together with the new-style @Include / @Exclude annotations.");
+ return null;
+ }
+
+ Collections.sort(members, new Comparator<ToStringMember<L>>() {
+ @Override public int compare(ToStringMember<L> a, ToStringMember<L> b) {
+ int ra = a.getInc() == null ? 0 : a.getInc().rank();
+ int rb = b.getInc() == null ? 0 : b.getInc().rank();
+ if (ra < rb) return +1;
+ if (ra > rb) return -1;
+
+ int pa = a.getNode().getStartPos();
+ int pb = b.getNode().getStartPos();
+
+ if (pa < pb) return -1;
+ if (pa > pb) return +1;
+
+ return 0;
+ }
+ });
+ return members;
+ }
+}
diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java
index 49867e62..4db1d38d 100644
--- a/src/core/lombok/eclipse/EclipseNode.java
+++ b/src/core/lombok/eclipse/EclipseNode.java
@@ -23,7 +23,9 @@ package lombok.eclipse;
import java.util.List;
+import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
+import lombok.eclipse.handlers.EclipseHandlerUtil;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -36,6 +38,7 @@ import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
/**
* Eclipse specific version of the LombokNode class.
@@ -184,4 +187,72 @@ public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode,
public boolean isCompleteParse() {
return ast.isCompleteParse();
}
+
+ @Override public boolean hasAnnotation(Class<? extends java.lang.annotation.Annotation> type) {
+ return EclipseHandlerUtil.hasAnnotation(type, this);
+ }
+
+ @Override public <Z extends java.lang.annotation.Annotation> AnnotationValues<Z> findAnnotation(Class<Z> type) {
+ EclipseNode annotation = EclipseHandlerUtil.findAnnotation(type, this);
+ if (annotation == null) return null;
+ return EclipseHandlerUtil.createAnnotation(type, annotation);
+ }
+
+ private Integer getModifiers() {
+ if (node instanceof TypeDeclaration) return ((TypeDeclaration) node).modifiers;
+ if (node instanceof FieldDeclaration) return ((FieldDeclaration) node).modifiers;
+ if (node instanceof LocalDeclaration) return ((LocalDeclaration) node).modifiers;
+ if (node instanceof AbstractMethodDeclaration) return ((AbstractMethodDeclaration) node).modifiers;
+
+ return null;
+ }
+
+ @Override public boolean isStatic() {
+ if (node instanceof TypeDeclaration) {
+ EclipseNode directUp = directUp();
+ if (directUp == null || directUp.getKind() == Kind.COMPILATION_UNIT) return true;
+ if (!(directUp.get() instanceof TypeDeclaration)) return false;
+ TypeDeclaration p = (TypeDeclaration) directUp.get();
+ int f = p.modifiers;
+ if ((ClassFileConstants.AccInterface & f) != 0) return true;
+ if ((ClassFileConstants.AccEnum & f) != 0) return true;
+ }
+
+ if (node instanceof FieldDeclaration) {
+ EclipseNode directUp = directUp();
+ if (directUp != null && directUp.get() instanceof TypeDeclaration) {
+ TypeDeclaration p = (TypeDeclaration) directUp.get();
+ int f = p.modifiers;
+ if ((ClassFileConstants.AccInterface & f) != 0) return true;
+ }
+ }
+
+ Integer i = getModifiers();
+ if (i == null) return false;
+ int f = i.intValue();
+ return (ClassFileConstants.AccStatic & f) != 0;
+ }
+
+ @Override public boolean isTransient() {
+ if (getKind() != Kind.FIELD) return false;
+ Integer i = getModifiers();
+ return i != null && (i.intValue() & ClassFileConstants.AccTransient) != 0;
+ }
+
+ @Override public boolean isEnumMember() {
+ if (getKind() != Kind.FIELD) return false;
+ return ((FieldDeclaration) node).getKind() == 3;
+ }
+
+ @Override public int countMethodParameters() {
+ if (getKind() != Kind.METHOD) return 0;
+
+ Argument[] a = ((AbstractMethodDeclaration) node).arguments;
+ if (a == null) return 0;
+ return a.length;
+ }
+
+ @Override public int getStartPos() {
+ return node.sourceStart;
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 2e402c7e..9f9b3975 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -920,10 +920,6 @@ public class EclipseHandlerUtil {
return null;
}
- public enum FieldAccess {
- GETTER, PREFER_FIELD, ALWAYS_FIELD;
- }
-
static boolean lookForGetter(EclipseNode field, FieldAccess fieldAccess) {
if (fieldAccess == FieldAccess.GETTER) return true;
if (fieldAccess == FieldAccess.ALWAYS_FIELD) return false;
@@ -940,11 +936,13 @@ public class EclipseHandlerUtil {
}
static TypeReference getFieldType(EclipseNode field, FieldAccess fieldAccess) {
+ if (field.get() instanceof MethodDeclaration) return ((MethodDeclaration) field.get()).returnType;
+
boolean lookForGetter = lookForGetter(field, fieldAccess);
GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
- return ((FieldDeclaration)field.get()).type;
+ return ((FieldDeclaration) field.get()).type;
}
return getter.type;
@@ -952,7 +950,7 @@ public class EclipseHandlerUtil {
static Expression createFieldAccessor(EclipseNode field, FieldAccess fieldAccess, ASTNode source) {
int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
boolean lookForGetter = lookForGetter(field, fieldAccess);
@@ -1020,6 +1018,29 @@ public class EclipseHandlerUtil {
return call;
}
+ static Expression createMethodAccessor(EclipseNode method, ASTNode source) {
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration methodDecl = (MethodDeclaration) method.get();
+ MessageSend call = new MessageSend();
+ setGeneratedBy(call, source);
+ call.sourceStart = pS; call.statementEnd = call.sourceEnd = pE;
+ if ((methodDecl.modifiers & ClassFileConstants.AccStatic) == 0) {
+ call.receiver = new ThisReference(pS, pE);
+ setGeneratedBy(call.receiver, source);
+ } else {
+ EclipseNode containerNode = method.up();
+ if (containerNode != null && containerNode.get() instanceof TypeDeclaration) {
+ call.receiver = new SingleNameReference(((TypeDeclaration) containerNode.get()).name, p);
+ setGeneratedBy(call.receiver, source);
+ }
+ }
+
+ call.selector = methodDecl.selector;
+ return call;
+ }
+
/** Serves as return value for the methods that check for the existence of fields and methods. */
public enum MemberExistsResult {
NOT_EXISTS, EXISTS_BY_LOMBOK, EXISTS_BY_USER;
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index d4cdc654..aa9fad84 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -76,6 +76,7 @@ import lombok.ConfigurationKeys;
import lombok.Singular;
import lombok.core.AST.Kind;
import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.InclusionExclusionUtils.ToStringMember;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.Eclipse;
@@ -449,9 +450,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
- List<EclipseNode> fieldNodes = new ArrayList<EclipseNode>();
+ List<ToStringMember<EclipseNode>> fieldNodes = new ArrayList<ToStringMember<EclipseNode>>();
for (BuilderFieldData bfd : builderFields) {
- fieldNodes.addAll(bfd.createdFields);
+ for (EclipseNode f : bfd.createdFields) {
+ fieldNodes.add(new ToStringMember<EclipseNode>(f, null, true));
+ }
}
MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
if (md != null) injectMethod(builderType, md);
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index 3e226269..2a497420 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -43,7 +43,6 @@ import lombok.core.configuration.CallSuperType;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java
index 533b97c4..d0c2cc23 100644
--- a/src/core/lombok/eclipse/handlers/HandleGetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleGetter.java
@@ -41,7 +41,6 @@ import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.agent.PatchDelegate;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index ab40b1e5..64f7c3cf 100644
--- a/src/core/lombok/eclipse/handlers/HandleSetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleSetter.java
@@ -32,6 +32,14 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import lombok.AccessLevel;
+import lombok.ConfigurationKeys;
+import lombok.Setter;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
@@ -51,15 +59,6 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.mangosdk.spi.ProviderFor;
-import lombok.AccessLevel;
-import lombok.ConfigurationKeys;
-import lombok.Setter;
-import lombok.core.AST.Kind;
-import lombok.core.AnnotationValues;
-import lombok.eclipse.EclipseAnnotationHandler;
-import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
-
/**
* Handles the {@code lombok.Setter} annotation for eclipse.
*/
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index d8f4c569..14a2374b 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 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
@@ -24,7 +24,6 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -37,17 +36,17 @@ import lombok.ConfigurationKeys;
import lombok.ToString;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
+import lombok.core.handlers.InclusionExclusionUtils;
+import lombok.core.handlers.InclusionExclusionUtils.ToStringMember;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
-import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
@@ -70,17 +69,25 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleToString extends EclipseAnnotationHandler<ToString> {
- public void checkForBogusFieldNames(EclipseNode type, AnnotationValues<ToString> annotation) {
- if (annotation.isExplicit("exclude")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().exclude()), type, true, false)) {
- annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
- }
- }
- if (annotation.isExplicit("of")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().of()), type, false, false)) {
- annotation.setWarning("of", "This field does not exist.", i);
- }
- }
+ public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
+
+ ToString ann = annotation.getInstance();
+ List<ToStringMember<EclipseNode>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode);
+ if (members == null) return;
+
+ Boolean callSuper = ann.callSuper();
+
+ if (!annotation.isExplicit("callSuper")) callSuper = null;
+
+ Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
+ boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
+ FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
+
+ Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
+ boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
+
+ generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess);
}
public void generateToStringForType(EclipseNode typeNode, EclipseNode errorNode) {
@@ -98,41 +105,13 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
- generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, access);
+ List<ToStringMember<EclipseNode>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null);
+ generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access);
}
- public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) {
- handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
-
- ToString ann = annotation.getInstance();
- List<String> excludes = Arrays.asList(ann.exclude());
- List<String> includes = Arrays.asList(ann.of());
- EclipseNode typeNode = annotationNode.up();
- Boolean callSuper = ann.callSuper();
+ public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List<ToStringMember<EclipseNode>> members,
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
- if (!annotation.isExplicit("callSuper")) callSuper = null;
- if (!annotation.isExplicit("exclude")) excludes = null;
- if (!annotation.isExplicit("of")) includes = null;
-
- if (excludes != null && includes != null) {
- excludes = null;
- annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
- }
-
- checkForBogusFieldNames(typeNode, annotation);
-
- Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
- boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
- FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
-
- Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
- boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
-
- generateToString(typeNode, annotationNode, excludes, includes, includeFieldNames, callSuper, true, fieldAccess);
- }
-
- public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes,
- boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
TypeDeclaration typeDecl = null;
if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
@@ -140,39 +119,20 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
boolean notAClass = (modifiers &
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
- if (typeDecl == null || notAClass) {
- errorNode.addError("@ToString is only supported on a class or enum.");
- }
-
if (callSuper == null) {
try {
callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
}
- List<EclipseNode> nodesForToString = new ArrayList<EclipseNode>();
- if (includes != null) {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (includes.contains(new String(fieldDecl.name))) nodesForToString.add(child);
- }
- } else {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (!filterField(fieldDecl)) continue;
-
- //Skip excluded fields.
- if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue;
-
- nodesForToString.add(child);
- }
+ if (typeDecl == null || notAClass) {
+ errorNode.addError("@ToString is only supported on a class or enum.");
+ return;
}
switch (methodExists("toString", typeNode, 0)) {
case NOT_EXISTS:
- MethodDeclaration toString = createToString(typeNode, nodesForToString, includeFieldNames, callSuper, errorNode.get(), fieldAccess);
+ MethodDeclaration toString = createToString(typeNode, members, includeFieldNames, callSuper, errorNode.get(), fieldAccess);
injectMethod(typeNode, toString);
break;
case EXISTS_BY_LOMBOK:
@@ -185,8 +145,9 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
}
- public static MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
- boolean includeFieldNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+ public static MethodDeclaration createToString(EclipseNode type, Collection<ToStringMember<EclipseNode>> members,
+ boolean includeNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+
String typeName = getTypeName(type);
char[] suffix = ")".toCharArray();
String infixS = ", ";
@@ -199,10 +160,13 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
if (callSuper) {
prefix = (typeName + "(super=").toCharArray();
- } else if (fields.isEmpty()) {
+ } else if (members.isEmpty()) {
prefix = (typeName + "()").toCharArray();
- } else if (includeFieldNames) {
- prefix = (typeName + "(" + new String(((FieldDeclaration)fields.iterator().next().get()).name) + "=").toCharArray();
+ } else if (includeNames) {
+ ToStringMember<EclipseNode> firstMember = members.iterator().next();
+ String name = firstMember.getInc() == null ? "" : firstMember.getInc().name();
+ if (name.isEmpty()) name = firstMember.getNode().getName();
+ prefix = (typeName + "(" + name + "=").toCharArray();
} else {
prefix = (typeName + "(").toCharArray();
}
@@ -223,29 +187,35 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
first = false;
}
- for (EclipseNode field : fields) {
- TypeReference fieldType = getFieldType(field, fieldAccess);
- Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
+ for (ToStringMember<EclipseNode> member : members) {
+ EclipseNode memberNode = member.getNode();
+
+ TypeReference fieldType = getFieldType(memberNode, fieldAccess);
+ Expression memberAccessor;
+ if (memberNode.getKind() == Kind.METHOD) {
+ memberAccessor = createMethodAccessor(memberNode, source);
+ } else {
+ memberAccessor = createFieldAccessor(memberNode, fieldAccess, source);
+ }
// The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
boolean fieldBaseTypeIsPrimitive = BUILT_IN_TYPES.contains(new String(fieldType.getLastToken()));
+ @SuppressWarnings("unused")
boolean fieldIsPrimitive = fieldType.dimensions() == 0 && fieldBaseTypeIsPrimitive;
boolean fieldIsPrimitiveArray = fieldType.dimensions() == 1 && fieldBaseTypeIsPrimitive;
boolean fieldIsObjectArray = fieldType.dimensions() > 0 && !fieldIsPrimitiveArray;
- @SuppressWarnings("unused")
- boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
Expression ex;
if (fieldIsPrimitiveArray || fieldIsObjectArray) {
MessageSend arrayToString = new MessageSend();
arrayToString.sourceStart = pS; arrayToString.sourceEnd = pE;
arrayToString.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray());
- arrayToString.arguments = new Expression[] { fieldAccessor };
+ arrayToString.arguments = new Expression[] { memberAccessor };
setGeneratedBy(arrayToString.arguments[0], source);
arrayToString.selector = (fieldIsObjectArray ? "deepToString" : "toString").toCharArray();
ex = arrayToString;
} else {
- ex = fieldAccessor;
+ ex = memberAccessor;
}
setGeneratedBy(ex, source);
@@ -258,8 +228,10 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
StringLiteral fieldNameLiteral;
- if (includeFieldNames) {
- char[] namePlusEqualsSign = (infixS + field.getName() + "=").toCharArray();
+ if (includeNames) {
+ String n = member.getInc() == null ? "" : member.getInc().name();
+ if (n.isEmpty()) n = memberNode.getName();
+ char[] namePlusEqualsSign = (infixS + n + "=").toCharArray();
fieldNameLiteral = new StringLiteral(namePlusEqualsSign, pS, pE, 0);
} else {
fieldNameLiteral = new StringLiteral(infix, pS, pE, 0);
diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java
index 200ebde7..c035fc26 100644
--- a/src/core/lombok/eclipse/handlers/HandleWither.java
+++ b/src/core/lombok/eclipse/handlers/HandleWither.java
@@ -36,7 +36,6 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import lombok.experimental.Wither;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java
index fa24c2f9..2bce6e3a 100644
--- a/src/core/lombok/javac/JavacNode.java
+++ b/src/core/lombok/javac/JavacNode.java
@@ -21,13 +21,17 @@
*/
package lombok.javac;
+import java.lang.annotation.Annotation;
import java.util.List;
import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
+import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
+import lombok.javac.handlers.JavacHandlerUtil;
+import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
@@ -36,6 +40,7 @@ import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Name;
@@ -256,4 +261,70 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
public void addWarning(String message, DiagnosticPosition pos) {
ast.printMessage(Diagnostic.Kind.WARNING, message, null, pos, false);
}
+
+ @Override public boolean hasAnnotation(Class<? extends Annotation> type) {
+ return JavacHandlerUtil.hasAnnotationAndDeleteIfNeccessary(type, this);
+ }
+
+ @Override public <Z extends Annotation> AnnotationValues<Z> findAnnotation(Class<Z> type) {
+ JavacNode annotation = JavacHandlerUtil.findAnnotation(type, this, true);
+ if (annotation == null) return null;
+ return JavacHandlerUtil.createAnnotation(type, annotation);
+ }
+
+ private JCModifiers getModifiers() {
+ if (node instanceof JCClassDecl) return ((JCClassDecl) node).getModifiers();
+ if (node instanceof JCMethodDecl) return ((JCMethodDecl) node).getModifiers();
+ if (node instanceof JCVariableDecl) return ((JCVariableDecl) node).getModifiers();
+ return null;
+ }
+
+ @Override public boolean isStatic() {
+ if (node instanceof JCClassDecl) {
+ JavacNode directUp = directUp();
+ if (directUp == null || directUp.getKind() == Kind.COMPILATION_UNIT) return true;
+ if (!(directUp.get() instanceof JCClassDecl)) return false;
+ JCClassDecl p = (JCClassDecl) directUp.get();
+ long f = p.mods.flags;
+ if ((Flags.INTERFACE & f) != 0) return true;
+ if ((Flags.ENUM & f) != 0) return true;
+ }
+
+ if (node instanceof JCVariableDecl) {
+ JavacNode directUp = directUp();
+ if (directUp != null && directUp.get() instanceof JCClassDecl) {
+ JCClassDecl p = (JCClassDecl) directUp.get();
+ long f = p.mods.flags;
+ if ((Flags.INTERFACE & f) != 0) return true;
+ }
+ }
+
+ JCModifiers mods = getModifiers();
+ if (mods == null) return false;
+ return (mods.flags & Flags.STATIC) != 0;
+ }
+
+ @Override public boolean isEnumMember() {
+ if (getKind() != Kind.FIELD) return false;
+ JCModifiers mods = getModifiers();
+ return mods != null && (Flags.ENUM & mods.flags) != 0;
+ }
+
+ @Override public boolean isTransient() {
+ if (getKind() != Kind.FIELD) return false;
+ JCModifiers mods = getModifiers();
+ return mods != null && (Flags.TRANSIENT & mods.flags) != 0;
+ }
+
+ @Override public int countMethodParameters() {
+ if (getKind() != Kind.METHOD) return 0;
+
+ com.sun.tools.javac.util.List<JCVariableDecl> params = ((JCMethodDecl) node).params;
+ if (params == null) return 0;
+ return params.size();
+ }
+
+ @Override public int getStartPos() {
+ return node.getPreferredPosition();
+ }
}
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index 86ac00e6..7cf78392 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -58,6 +58,7 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.InclusionExclusionUtils.ToStringMember;
import lombok.experimental.NonFinal;
import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
@@ -398,10 +399,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
- java.util.List<JavacNode> fieldNodes = new ArrayList<JavacNode>();
+ java.util.List<ToStringMember<JavacNode>> fieldNodes = new ArrayList<ToStringMember<JavacNode>>();
for (BuilderFieldData bfd : builderFields) {
- fieldNodes.addAll(bfd.createdFields);
+ for (JavacNode f : bfd.createdFields) {
+ fieldNodes.add(new ToStringMember<JavacNode>(f, null, true));
+ }
}
+
JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
if (md != null) injectMethod(builderType, md);
}
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index d8bfd154..b3650ca6 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -39,7 +39,6 @@ import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
-import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
import org.mangosdk.spi.ProviderFor;
diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java
index 7e6598a7..4fc6155c 100644
--- a/src/core/lombok/javac/handlers/HandleGetter.java
+++ b/src/core/lombok/javac/handlers/HandleGetter.java
@@ -41,7 +41,6 @@ import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.JavacTreeMaker.TypeTag;
-import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index 331b2fac..4e3c9576 100644
--- a/src/core/lombok/javac/handlers/HandleSetter.java
+++ b/src/core/lombok/javac/handlers/HandleSetter.java
@@ -36,7 +36,6 @@ import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
-import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java
index 897d5f2c..8c580207 100644
--- a/src/core/lombok/javac/handlers/HandleToString.java
+++ b/src/core/lombok/javac/handlers/HandleToString.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 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
@@ -31,6 +31,8 @@ import lombok.ConfigurationKeys;
import lombok.ToString;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
+import lombok.core.handlers.InclusionExclusionUtils;
+import lombok.core.handlers.InclusionExclusionUtils.ToStringMember;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
@@ -52,57 +54,33 @@ import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.util.ListBuffer;
/**
* Handles the {@code ToString} annotation for javac.
*/
@ProviderFor(JavacAnnotationHandler.class)
public class HandleToString extends JavacAnnotationHandler<ToString> {
- public void checkForBogusFieldNames(JavacNode type, AnnotationValues<ToString> annotation) {
- if (annotation.isExplicit("exclude")) {
- for (int i : createListOfNonExistentFields(List.from(annotation.getInstance().exclude()), type, true, false)) {
- annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
- }
- }
- if (annotation.isExplicit("of")) {
- for (int i : createListOfNonExistentFields(List.from(annotation.getInstance().of()), type, false, false)) {
- annotation.setWarning("of", "This field does not exist.", i);
- }
- }
- }
-
@Override public void handle(AnnotationValues<ToString> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
deleteAnnotationIfNeccessary(annotationNode, ToString.class);
ToString ann = annotation.getInstance();
- List<String> excludes = List.from(ann.exclude());
- List<String> includes = List.from(ann.of());
- JavacNode typeNode = annotationNode.up();
-
- checkForBogusFieldNames(typeNode, annotation);
+ java.util.List<ToStringMember<JavacNode>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode);
+ if (members == null) return;
Boolean callSuper = ann.callSuper();
if (!annotation.isExplicit("callSuper")) callSuper = null;
- if (!annotation.isExplicit("exclude")) excludes = null;
- if (!annotation.isExplicit("of")) includes = null;
-
- if (excludes != null && includes != null) {
- excludes = null;
- annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
- }
Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
- boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
+ boolean includeNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
- generateToString(typeNode, annotationNode, excludes, includes, includeFieldNames, callSuper, true, fieldAccess);
+ generateToString(annotationNode.up(), annotationNode, members, includeNames, callSuper, true, fieldAccess);
}
public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) {
@@ -111,7 +89,6 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
return;
}
-
boolean includeFieldNames = true;
try {
Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
@@ -121,20 +98,22 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
- generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, access);
+ java.util.List<ToStringMember<JavacNode>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null);
+ generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access);
}
- public void generateToString(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes,
- boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
+ public void generateToString(JavacNode typeNode, JavacNode source, java.util.List<ToStringMember<JavacNode>> members,
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
+
boolean notAClass = true;
if (typeNode.get() instanceof JCClassDecl) {
- long flags = ((JCClassDecl)typeNode.get()).mods.flags;
+ long flags = ((JCClassDecl) typeNode.get()).mods.flags;
notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0;
}
if (callSuper == null) {
try {
- callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ callSuper = ((Boolean) ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
}
@@ -143,30 +122,9 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
return;
}
- ListBuffer<JavacNode> nodesForToString = new ListBuffer<JavacNode>();
- if (includes != null) {
- for (JavacNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
- if (includes.contains(fieldDecl.name.toString())) nodesForToString.append(child);
- }
- } else {
- for (JavacNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
- //Skip static fields.
- if ((fieldDecl.mods.flags & Flags.STATIC) != 0) continue;
- //Skip excluded fields.
- if (excludes != null && excludes.contains(fieldDecl.name.toString())) continue;
- //Skip fields that start with $.
- if (fieldDecl.name.toString().startsWith("$")) continue;
- nodesForToString.append(child);
- }
- }
-
switch (methodExists("toString", typeNode, 0)) {
case NOT_EXISTS:
- JCMethodDecl method = createToString(typeNode, nodesForToString.toList(), includeFieldNames, callSuper, fieldAccess, source.get());
+ JCMethodDecl method = createToString(typeNode, members, includeFieldNames, callSuper, fieldAccess, source.get());
injectMethod(typeNode, method);
break;
case EXISTS_BY_LOMBOK:
@@ -180,7 +138,9 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
}
}
- static JCMethodDecl createToString(JavacNode typeNode, Collection<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+ static JCMethodDecl createToString(JavacNode typeNode, Collection<ToStringMember<JavacNode>> members,
+ boolean includeNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+
JavacTreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
@@ -195,10 +155,13 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
String prefix;
if (callSuper) {
prefix = typeName + "(super=";
- } else if (fields.isEmpty()) {
+ } else if (members.isEmpty()) {
prefix = typeName + "()";
- } else if (includeFieldNames) {
- prefix = typeName + "(" + ((JCVariableDecl)fields.iterator().next().get()).name.toString() + "=";
+ } else if (includeNames) {
+ ToStringMember<JavacNode> firstMember = members.iterator().next();
+ String name = firstMember.getInc() == null ? "" : firstMember.getInc().name();
+ if (name.isEmpty()) name = firstMember.getNode().getName();
+ prefix = typeName + "(" + name + "=";
} else {
prefix = typeName + "(";
}
@@ -207,30 +170,35 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
if (callSuper) {
JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("toString")),
- List.<JCExpression>nil());
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("toString")),
+ List.<JCExpression>nil());
current = maker.Binary(CTC_PLUS, current, callToSuper);
first = false;
}
- for (JavacNode fieldNode : fields) {
+ for (ToStringMember<JavacNode> member : members) {
JCExpression expr;
- JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
+ JCExpression memberAccessor;
+ JavacNode memberNode = member.getNode();
+ if (memberNode.getKind() == Kind.METHOD) {
+ memberAccessor = createMethodAccessor(maker, memberNode);
+ } else {
+ memberAccessor = createFieldAccessor(maker, memberNode, fieldAccess);
+ }
- JCExpression fieldType = getFieldType(fieldNode, fieldAccess);
+ JCExpression memberType = getFieldType(memberNode, fieldAccess);
// The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
- boolean fieldIsPrimitive = fieldType instanceof JCPrimitiveTypeTree;
- boolean fieldIsPrimitiveArray = fieldType instanceof JCArrayTypeTree && ((JCArrayTypeTree) fieldType).elemtype instanceof JCPrimitiveTypeTree;
- boolean fieldIsObjectArray = !fieldIsPrimitiveArray && fieldType instanceof JCArrayTypeTree;
@SuppressWarnings("unused")
- boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
+ boolean fieldIsPrimitive = memberType instanceof JCPrimitiveTypeTree;
+ boolean fieldIsPrimitiveArray = memberType instanceof JCArrayTypeTree && ((JCArrayTypeTree) memberType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean fieldIsObjectArray = !fieldIsPrimitiveArray && memberType instanceof JCArrayTypeTree;
if (fieldIsPrimitiveArray || fieldIsObjectArray) {
JCExpression tsMethod = chainDots(typeNode, "java", "util", "Arrays", fieldIsObjectArray ? "deepToString" : "toString");
- expr = maker.Apply(List.<JCExpression>nil(), tsMethod, List.<JCExpression>of(fieldAccessor));
- } else expr = fieldAccessor;
+ expr = maker.Apply(List.<JCExpression>nil(), tsMethod, List.<JCExpression>of(memberAccessor));
+ } else expr = memberAccessor;
if (first) {
current = maker.Binary(CTC_PLUS, current, expr);
@@ -238,8 +206,10 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
continue;
}
- if (includeFieldNames) {
- current = maker.Binary(CTC_PLUS, current, maker.Literal(infix + fieldNode.getName() + "="));
+ if (includeNames) {
+ String n = member.getInc() == null ? "" : member.getInc().name();
+ if (n.isEmpty()) n = memberNode.getName();
+ current = maker.Binary(CTC_PLUS, current, maker.Literal(infix + n + "="));
} else {
current = maker.Binary(CTC_PLUS, current, maker.Literal(infix));
}
@@ -254,7 +224,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
JCBlock body = maker.Block(0, List.of(returnStatement));
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("toString"), returnType,
- List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
+ List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
}
public static String getTypeName(JavacNode typeNode) {
diff --git a/src/core/lombok/javac/handlers/HandleWither.java b/src/core/lombok/javac/handlers/HandleWither.java
index 987a3d34..87f3c16a 100644
--- a/src/core/lombok/javac/handlers/HandleWither.java
+++ b/src/core/lombok/javac/handlers/HandleWither.java
@@ -36,7 +36,6 @@ import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc;
-import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 4b5c48dd..917e2e9c 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -216,7 +216,11 @@ public class JavacHandlerUtil {
}
}
- static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node, boolean delete) {
+ public static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node) {
+ return findAnnotation(type, node, false);
+ }
+
+ public static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node, boolean delete) {
if (node == null) return null;
if (type == null) return null;
switch (node.getKind()) {
@@ -874,10 +878,6 @@ public class JavacHandlerUtil {
return null;
}
- public enum FieldAccess {
- GETTER, PREFER_FIELD, ALWAYS_FIELD;
- }
-
static boolean lookForGetter(JavacNode field, FieldAccess fieldAccess) {
if (fieldAccess == FieldAccess.GETTER) return true;
if (fieldAccess == FieldAccess.ALWAYS_FIELD) return false;
@@ -899,12 +899,14 @@ public class JavacHandlerUtil {
* @see #createFieldAccessor(TreeMaker, JavacNode, FieldAccess)
*/
static JCExpression getFieldType(JavacNode field, FieldAccess fieldAccess) {
+ if (field.getKind() == Kind.METHOD) return ((JCMethodDecl) field.get()).restype;
+
boolean lookForGetter = lookForGetter(field, fieldAccess);
GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
- return ((JCVariableDecl)field.get()).vartype;
+ return ((JCVariableDecl) field.get()).vartype;
}
return getter.type;
@@ -941,7 +943,28 @@ public class JavacHandlerUtil {
if (receiver == null) receiver = maker.Ident(field.toName("this"));
JCMethodInvocation call = maker.Apply(List.<JCExpression>nil(),
- maker.Select(receiver, getter.name), List.<JCExpression>nil());
+ maker.Select(receiver, getter.name), List.<JCExpression>nil());
+ return call;
+ }
+
+ static JCExpression createMethodAccessor(JavacTreeMaker maker, JavacNode method) {
+ JCExpression receiver;
+ JCMethodDecl methodDecl = (JCMethodDecl) method.get();
+
+ if ((methodDecl.mods.flags & Flags.STATIC) == 0) {
+ receiver = maker.Ident(method.toName("this"));
+ } else {
+ JavacNode containerNode = method.up();
+ if (containerNode != null && containerNode.get() instanceof JCClassDecl) {
+ JCClassDecl container = (JCClassDecl) method.up().get();
+ receiver = maker.Ident(container.name);
+ } else {
+ receiver = null;
+ }
+ }
+
+ JCMethodInvocation call = maker.Apply(List.<JCExpression>nil(),
+ receiver == null ? maker.Ident(methodDecl.name) : maker.Select(receiver, methodDecl.name), List.<JCExpression>nil());
return call;
}
diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java
index f2b5486c..5ef33086 100644
--- a/src/utils/lombok/eclipse/Eclipse.java
+++ b/src/utils/lombok/eclipse/Eclipse.java
@@ -42,6 +42,7 @@ import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
@@ -184,6 +185,17 @@ public class Eclipse {
String qName = Eclipse.toQualifiedName(((QualifiedNameReference)e).tokens);
int idx = qName.lastIndexOf('.');
return new FieldSelect(idx == -1 ? qName : qName.substring(idx+1));
+ } else if (e instanceof UnaryExpression) {
+ if ("-".equals(((UnaryExpression) e).operatorToString())) {
+ Object inner = calculateValue(((UnaryExpression) e).expression);
+ if (inner instanceof Integer) return - ((Integer) inner).intValue();
+ if (inner instanceof Byte) return - ((Byte) inner).byteValue();
+ if (inner instanceof Short) return - ((Short) inner).shortValue();
+ if (inner instanceof Long) return - ((Long) inner).longValue();
+ if (inner instanceof Float) return - ((Float) inner).floatValue();
+ if (inner instanceof Double) return - ((Double) inner).doubleValue();
+ return null;
+ }
}
return null;
diff --git a/test/transform/resource/after-delombok/ToStringNewStyle.java b/test/transform/resource/after-delombok/ToStringNewStyle.java
new file mode 100644
index 00000000..0a54bd24
--- /dev/null
+++ b/test/transform/resource/after-delombok/ToStringNewStyle.java
@@ -0,0 +1,18 @@
+public class ToStringNewStyle {
+ int b;
+ double c;
+ int f;
+ int d;
+ int f() {
+ return 0;
+ }
+ int g;
+ int h;
+ int i;
+ int j;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "ToStringNewStyle(a=" + this.b + ", c=" + this.c + ", e=" + this.d + ", f=" + this.f() + ", g=" + this.g + ", i=" + this.i + ", h=" + this.h + ")";
+ }
+}
diff --git a/test/transform/resource/after-ecj/ToStringNewStyle.java b/test/transform/resource/after-ecj/ToStringNewStyle.java
new file mode 100644
index 00000000..cdc6f5bc
--- /dev/null
+++ b/test/transform/resource/after-ecj/ToStringNewStyle.java
@@ -0,0 +1,20 @@
+import lombok.ToString;
+public @ToString class ToStringNewStyle {
+ @ToString.Include(name = "a") int b;
+ double c;
+ int f;
+ @ToString.Include(name = "e") int d;
+ int g;
+ @ToString.Include(rank = (- 1)) int h;
+ int i;
+ @ToString.Exclude int j;
+ public ToStringNewStyle() {
+ super();
+ }
+ @ToString.Include int f() {
+ return 0;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((((((((((("ToStringNewStyle(a=" + this.b) + ", c=") + this.c) + ", e=") + this.d) + ", f=") + this.f()) + ", g=") + this.g) + ", i=") + this.i) + ", h=") + this.h) + ")");
+ }
+}
diff --git a/test/transform/resource/before/ToStringNewStyle.java b/test/transform/resource/before/ToStringNewStyle.java
new file mode 100644
index 00000000..7e436e51
--- /dev/null
+++ b/test/transform/resource/before/ToStringNewStyle.java
@@ -0,0 +1,15 @@
+import lombok.ToString;
+@ToString
+public class ToStringNewStyle {
+ @ToString.Include(name = "a") int b;
+ double c;
+ int f;
+ @ToString.Include(name = "e") int d;
+ @ToString.Include int f() {
+ return 0;
+ }
+ int g;
+ @ToString.Include(rank = -1) int h;
+ int i;
+ @ToString.Exclude int j;
+}