aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/lombok/Getter.java2
-rw-r--r--src/core/lombok/eclipse/Eclipse.java50
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java37
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java34
-rw-r--r--src/core/lombok/eclipse/handlers/HandleGetter.java279
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSetter.java3
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java17
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java34
-rw-r--r--src/core/lombok/javac/handlers/HandleGetter.java188
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java3
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java17
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java35
-rw-r--r--test/transform/resource/after-delombok/GetterLazy.java19
-rw-r--r--test/transform/resource/after-delombok/GetterLazyEahcToString.java58
-rw-r--r--test/transform/resource/after-delombok/GetterLazyInvalid.java25
-rw-r--r--test/transform/resource/after-delombok/GetterLazyNative.java137
-rw-r--r--test/transform/resource/after-ecj/GetterLazy.java27
-rw-r--r--test/transform/resource/after-ecj/GetterLazyEahcToString.java53
-rw-r--r--test/transform/resource/after-ecj/GetterLazyInvalid.java40
-rw-r--r--test/transform/resource/after-ecj/GetterLazyNative.java158
-rw-r--r--test/transform/resource/before/GetterLazy.java7
-rw-r--r--test/transform/resource/before/GetterLazyEahcToString.java8
-rw-r--r--test/transform/resource/before/GetterLazyInvalid.java27
-rw-r--r--test/transform/resource/before/GetterLazyNative.java28
-rw-r--r--test/transform/resource/messages-delombok/GetterLazyInvalid.java.messages6
-rw-r--r--test/transform/resource/messages-ecj/GetterLazyInvalid.java.messages6
26 files changed, 1194 insertions, 104 deletions
diff --git a/src/core/lombok/Getter.java b/src/core/lombok/Getter.java
index 7e545ae0..97ce315c 100644
--- a/src/core/lombok/Getter.java
+++ b/src/core/lombok/Getter.java
@@ -65,6 +65,8 @@ public @interface Getter {
*/
AnyAnnotation[] onMethod() default {};
+ boolean lazy() default false;
+
/**
* Placeholder annotation to enable the placement of annotations on the getter method.
* @deprecated Don't use this annotation, since we might remove it.
diff --git a/src/core/lombok/eclipse/Eclipse.java b/src/core/lombok/eclipse/Eclipse.java
index 4101370c..5dcd9de8 100644
--- a/src/core/lombok/eclipse/Eclipse.java
+++ b/src/core/lombok/eclipse/Eclipse.java
@@ -352,16 +352,24 @@ public class Eclipse {
if (base != null) {
if (dims > 0) {
- return new ArrayTypeReference(base, dims, pos(pos));
+ TypeReference result = new ArrayTypeReference(base, dims, pos(pos));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
- return new SingleTypeReference(base, pos(pos));
+ TypeReference result = new SingleTypeReference(base, pos(pos));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
if (binding.isAnonymousType()) {
ReferenceBinding ref = (ReferenceBinding)binding;
ReferenceBinding[] supers = ref.superInterfaces();
if (supers == null || supers.length == 0) supers = new ReferenceBinding[] {ref.superclass()};
- if (supers[0] == null) return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
+ if (supers[0] == null) {
+ TypeReference result = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
+ }
return makeType(supers[0], pos, false);
}
@@ -371,9 +379,12 @@ public class Eclipse {
if (binding.isUnboundWildcard()) {
if (!allowCompound) {
- return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
+ TypeReference result = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
} else {
Wildcard out = new Wildcard(Wildcard.UNBOUND);
+ Eclipse.setGeneratedBy(out, pos);
out.sourceStart = pos.sourceStart;
out.sourceEnd = pos.sourceEnd;
return out;
@@ -387,6 +398,7 @@ public class Eclipse {
return makeType(wildcard.bound, pos, false);
} else {
Wildcard out = new Wildcard(Wildcard.EXTENDS);
+ Eclipse.setGeneratedBy(out, pos);
out.bound = makeType(wildcard.bound, pos, false);
out.sourceStart = pos.sourceStart;
out.sourceEnd = pos.sourceEnd;
@@ -394,12 +406,15 @@ public class Eclipse {
}
} else if (allowCompound && wildcard.boundKind == Wildcard.SUPER) {
Wildcard out = new Wildcard(Wildcard.SUPER);
+ Eclipse.setGeneratedBy(out, pos);
out.bound = makeType(wildcard.bound, pos, false);
out.sourceStart = pos.sourceStart;
out.sourceEnd = pos.sourceEnd;
return out;
} else {
- return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
+ TypeReference result = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(pos, 3));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
}
@@ -433,23 +448,34 @@ public class Eclipse {
if (parts.length > 1) {
TypeReference[][] typeArguments = new TypeReference[parts.length][];
typeArguments[typeArguments.length - 1] = params;
- return new ParameterizedQualifiedTypeReference(parts, typeArguments, dims, poss(pos, parts.length));
+ TypeReference result = new ParameterizedQualifiedTypeReference(parts, typeArguments, dims, poss(pos, parts.length));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
- return new ParameterizedSingleTypeReference(parts[0], params, dims, pos(pos));
+ TypeReference result = new ParameterizedSingleTypeReference(parts[0], params, dims, pos(pos));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
if (dims > 0) {
if (parts.length > 1) {
- return new ArrayQualifiedTypeReference(parts, dims, poss(pos, parts.length));
+ TypeReference result = new ArrayQualifiedTypeReference(parts, dims, poss(pos, parts.length));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
- return new ArrayTypeReference(parts[0], dims, pos(pos));
+ TypeReference result = new ArrayTypeReference(parts[0], dims, pos(pos));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
if (parts.length > 1) {
- return new QualifiedTypeReference(parts, poss(pos, parts.length));
+ TypeReference result = new QualifiedTypeReference(parts, poss(pos, parts.length));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
- return new SingleTypeReference(parts[0], pos(pos));
-
+ TypeReference result = new SingleTypeReference(parts[0], pos(pos));
+ Eclipse.setGeneratedBy(result, pos);
+ return result;
}
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 921d955e..4097362b 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -175,8 +175,29 @@ public class EclipseHandlerUtil {
return null;
}
- static TypeReference getFieldType(EclipseNode field, boolean useFieldsDirectly) {
- GetterMethod getter = useFieldsDirectly ? null : findGetter(field);
+ 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;
+
+ // If @Getter(lazy = true) is used, then using it is mandatory.
+ for (EclipseNode child : field.down()) {
+ if (child.getKind() != Kind.ANNOTATION) continue;
+ if (Eclipse.annotationTypeMatches(Getter.class, child)) {
+ AnnotationValues<Getter> ann = Eclipse.createAnnotation(Getter.class, child);
+ if (ann.getInstance().lazy()) return true;
+ }
+ }
+ return false;
+ }
+
+ static TypeReference getFieldType(EclipseNode field, FieldAccess fieldAccess) {
+ boolean lookForGetter = lookForGetter(field, fieldAccess);
+
+ GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
return ((FieldDeclaration)field.get()).type;
}
@@ -184,11 +205,13 @@ public class EclipseHandlerUtil {
return getter.type;
}
- static Expression createFieldAccessor(EclipseNode field, boolean useFieldsDirectly, ASTNode source) {
+ static Expression createFieldAccessor(EclipseNode field, FieldAccess fieldAccess, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
- GetterMethod getter = useFieldsDirectly ? null : findGetter(field);
+ boolean lookForGetter = lookForGetter(field, fieldAccess);
+
+ GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
FieldDeclaration fieldDecl = (FieldDeclaration)field.get();
@@ -219,11 +242,13 @@ public class EclipseHandlerUtil {
return call;
}
- static Expression createFieldAccessor(EclipseNode field, boolean useFieldsDirectly, ASTNode source, char[] receiver) {
+ static Expression createFieldAccessor(EclipseNode field, FieldAccess fieldAccess, ASTNode source, char[] receiver) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
- GetterMethod getter = useFieldsDirectly ? null : findGetter(field);
+ boolean lookForGetter = lookForGetter(field, fieldAccess);
+
+ GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
NameReference ref;
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index 2b830241..895c076e 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -113,7 +113,7 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
}
}
- generateMethods(typeNode, errorNode, null, null, null, false, false);
+ generateMethods(typeNode, errorNode, null, null, null, false, FieldAccess.GETTER);
}
@Override public boolean handle(AnnotationValues<EqualsAndHashCode> annotation,
@@ -135,11 +135,13 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
}
- return generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, ann.doNotUseGetters());
+ FieldAccess fieldAccess = ann.doNotUseGetters() ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
+
+ return generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess);
}
public boolean generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes,
- Boolean callSuper, boolean whineIfExists, boolean useFieldsDirectly) {
+ Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
assert excludes == null || includes == null;
TypeDeclaration typeDecl = null;
@@ -209,7 +211,7 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
needsCanEqual = !isDirectDescendantOfObject || !isFinal;
- MethodDeclaration equals = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), useFieldsDirectly, needsCanEqual);
+ MethodDeclaration equals = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess, needsCanEqual);
injectMethod(typeNode, equals);
break;
case EXISTS_BY_LOMBOK:
@@ -237,7 +239,7 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
switch (methodExists("hashCode", typeNode)) {
case NOT_EXISTS:
- MethodDeclaration hashCode = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), useFieldsDirectly);
+ MethodDeclaration hashCode = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess);
injectMethod(typeNode, hashCode);
break;
case EXISTS_BY_LOMBOK:
@@ -253,7 +255,7 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
return true;
}
- private MethodDeclaration createHashCode(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, boolean useFieldsDirectly) {
+ private MethodDeclaration createHashCode(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
@@ -318,9 +320,9 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
int tempCounter = 0;
for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, useFieldsDirectly);
+ TypeReference fType = getFieldType(field, fieldAccess);
char[] token = fType.getLastToken();
- Expression fieldAccessor = createFieldAccessor(field, useFieldsDirectly, source);
+ Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
if (fType.dimensions() == 0 && token != null) {
if (Arrays.equals(TypeConstants.FLOAT, token)) {
/* Float.floatToIntBits(fieldName) */
@@ -363,7 +365,7 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
Eclipse.setGeneratedBy(int1231or1237, source);
intoResult.add(int1231or1237);
} else if (Arrays.equals(TypeConstants.LONG, token)) {
- intoResult.add(longToIntForHashCode(fieldAccessor, createFieldAccessor(field, useFieldsDirectly, source), source));
+ intoResult.add(longToIntForHashCode(fieldAccessor, createFieldAccessor(field, fieldAccess, source), source));
} else if (BUILT_IN_TYPES.contains(new String(token))) {
intoResult.add(fieldAccessor);
} else /* objects */ {
@@ -371,7 +373,7 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
MessageSend hashCodeCall = new MessageSend();
hashCodeCall.sourceStart = pS; hashCodeCall.sourceEnd = pE;
Eclipse.setGeneratedBy(hashCodeCall, source);
- hashCodeCall.receiver = createFieldAccessor(field, useFieldsDirectly, source);
+ hashCodeCall.receiver = createFieldAccessor(field, fieldAccess, source);
hashCodeCall.selector = "hashCode".toCharArray();
NullLiteral nullLiteral = new NullLiteral(pS, pE);
Eclipse.setGeneratedBy(nullLiteral, source);
@@ -433,7 +435,7 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
return method;
}
- private MethodDeclaration createEquals(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, boolean useFieldsDirectly, boolean needsCanEqual) {
+ private MethodDeclaration createEquals(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual) {
int pS = source.sourceStart; int pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
TypeDeclaration typeDecl = (TypeDeclaration)type.get();
@@ -589,10 +591,10 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
}
for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, useFieldsDirectly);
+ TypeReference fType = getFieldType(field, fieldAccess);
char[] token = fType.getLastToken();
- Expression thisFieldAccessor = createFieldAccessor(field, useFieldsDirectly, source);
- Expression otherFieldAccessor = createFieldAccessor(field, useFieldsDirectly, source, otherName);
+ Expression thisFieldAccessor = createFieldAccessor(field, fieldAccess, source);
+ Expression otherFieldAccessor = createFieldAccessor(field, fieldAccess, source, otherName);
if (fType.dimensions() == 0 && token != null) {
if (Arrays.equals(TypeConstants.FLOAT, token)) {
@@ -619,9 +621,9 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA
MessageSend equalsCall = new MessageSend();
equalsCall.sourceStart = pS; equalsCall.sourceEnd = pE;
Eclipse.setGeneratedBy(equalsCall, source);
- equalsCall.receiver = createFieldAccessor(field, useFieldsDirectly, source);
+ equalsCall.receiver = createFieldAccessor(field, fieldAccess, source);
equalsCall.selector = "equals".toCharArray();
- equalsCall.arguments = new Expression[] { createFieldAccessor(field, useFieldsDirectly, source, otherName) };
+ equalsCall.arguments = new Expression[] { createFieldAccessor(field, fieldAccess, source, otherName) };
UnaryExpression fieldsNotEqual = new UnaryExpression(equalsCall, OperatorIds.NOT);
fieldsNotEqual.sourceStart = pS; fieldsNotEqual.sourceEnd = pE;
Eclipse.setGeneratedBy(fieldsNotEqual, source);
diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java
index 38a6b468..56d0ba6c 100644
--- a/src/core/lombok/eclipse/handlers/HandleGetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleGetter.java
@@ -25,6 +25,9 @@ import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
@@ -34,14 +37,30 @@ import lombok.core.handlers.TransformationsUtil;
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.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
+import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+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.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+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.SynchronizedStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@@ -76,7 +95,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
}
for (EclipseNode field : typeNode.down()) {
- if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, pos.get(), level, null);
+ if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, pos.get(), level, null, false);
}
return true;
}
@@ -103,7 +122,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
* If not, the getter is still generated if it isn't already there, though there will not
* be a warning if its already there. The default access level is used.
*/
- public void generateGetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, Annotation[] onMethod) {
+ public void generateGetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, Annotation[] onMethod, boolean lazy) {
for (EclipseNode child : fieldNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
if (annotationTypeMatches(Getter.class, child)) {
@@ -113,42 +132,61 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
}
}
- createGetterForField(level, fieldNode, fieldNode, pos, false, onMethod);
+ createGetterForField(level, fieldNode, fieldNode, pos, false, onMethod, lazy);
}
public boolean handle(AnnotationValues<Getter> annotation, Annotation ast, EclipseNode annotationNode) {
EclipseNode node = annotationNode.up();
- AccessLevel level = annotation.getInstance().value();
- if (level == AccessLevel.NONE) return true;
+ Getter annotationInstance = annotation.getInstance();
+ AccessLevel level = annotationInstance.value();
+ boolean lazy = annotationInstance.lazy();
+ if (level == AccessLevel.NONE) {
+ if (lazy) {
+ annotationNode.addWarning("'lazy' does not work with AccessLevel.NONE.");
+ }
+ return true;
+ }
if (node == null) return false;
Annotation[] onMethod = getAndRemoveAnnotationParameter(ast, "onMethod");
if (node.getKind() == Kind.FIELD) {
- return createGetterForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true, onMethod);
+ return createGetterForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true, onMethod, lazy);
}
if (node.getKind() == Kind.TYPE) {
if (onMethod != null && onMethod.length != 0) annotationNode.addError("'onMethod' is not supported for @Getter on a type.");
+ if (lazy) annotationNode.addError("'lazy' is not supported for @Getter on a type.");
return generateGetterForType(node, annotationNode, level, false);
}
return false;
}
- private boolean createGetterForFields(AccessLevel level, Collection<EclipseNode> fieldNodes, EclipseNode errorNode, ASTNode source, boolean whineIfExists, Annotation[] onMethod) {
+ private boolean createGetterForFields(AccessLevel level, Collection<EclipseNode> fieldNodes, EclipseNode errorNode, ASTNode source, boolean whineIfExists, Annotation[] onMethod, boolean lazy) {
for (EclipseNode fieldNode : fieldNodes) {
- createGetterForField(level, fieldNode, errorNode, source, whineIfExists, onMethod);
+ createGetterForField(level, fieldNode, errorNode, source, whineIfExists, onMethod, lazy);
}
return true;
}
private boolean createGetterForField(AccessLevel level,
- EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists, Annotation[] onMethod) {
+ EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists, Annotation[] onMethod, boolean lazy) {
if (fieldNode.getKind() != Kind.FIELD) {
errorNode.addError("@Getter is only supported on a class or a field.");
return true;
}
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
+ if (lazy) {
+ if ((field.modifiers & ClassFileConstants.AccPrivate) == 0 || (field.modifiers & ClassFileConstants.AccFinal) == 0) {
+ errorNode.addError("'lazy' requires the field to be private and final.");
+ return true;
+ }
+ if (field.initialization == null) {
+ errorNode.addError("'lazy' requires field initialization.");
+ return true;
+ }
+ }
+
TypeReference fieldType = copyType(field.type, source);
String fieldName = new String(field.name);
boolean isBoolean = nameEquals(fieldType.getTypeName(), "boolean") && fieldType.dimensions() == 0;
@@ -174,7 +212,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
}
}
- MethodDeclaration method = generateGetter((TypeDeclaration) fieldNode.up().get(), fieldNode, getterName, modifier, source);
+ MethodDeclaration method = generateGetter((TypeDeclaration) fieldNode.up().get(), fieldNode, getterName, modifier, source, lazy);
Annotation[] copiedAnnotations = copyAnnotations(source, findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN), findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN), onMethod);
if (copiedAnnotations.length != 0) {
method.annotations = copiedAnnotations;
@@ -185,12 +223,22 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
return true;
}
- private MethodDeclaration generateGetter(TypeDeclaration parent, EclipseNode fieldNode, String name, int modifier, ASTNode source) {
- FieldDeclaration field = (FieldDeclaration) fieldNode.get();
+ private MethodDeclaration generateGetter(TypeDeclaration parent, EclipseNode fieldNode, String name, int modifier, ASTNode source, boolean lazy) {
+
+ // Remember the type; lazy will change it;
+ TypeReference returnType = copyType(((FieldDeclaration) fieldNode.get()).type, source);
+
+ Statement[] statements;
+ if (lazy) {
+ statements = createLazyGetterBody(source, fieldNode);
+ } else {
+ statements = createSimpleGetterBody(source, fieldNode);
+ }
+
MethodDeclaration method = new MethodDeclaration(parent.compilationResult);
Eclipse.setGeneratedBy(method, source);
method.modifiers = modifier;
- method.returnType = copyType(field.type, source);
+ method.returnType = returnType;
method.annotations = null;
method.arguments = null;
method.selector = name.toCharArray();
@@ -198,12 +246,209 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> {
method.thrownExceptions = null;
method.typeParameters = null;
method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
- Expression fieldRef = createFieldAccessor(fieldNode, true, source);
- Statement returnStatement = new ReturnStatement(fieldRef, field.sourceStart, field.sourceEnd);
- Eclipse.setGeneratedBy(returnStatement, source);
method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
- method.statements = new Statement[] { returnStatement };
+ method.statements = statements;
return method;
}
+
+ private Statement[] createSimpleGetterBody(ASTNode source, EclipseNode fieldNode) {
+ FieldDeclaration field = (FieldDeclaration) fieldNode.get();
+ Expression fieldRef = createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
+ Statement returnStatement = new ReturnStatement(fieldRef, field.sourceStart, field.sourceEnd);
+ Eclipse.setGeneratedBy(returnStatement, source);
+ return new Statement[] {returnStatement};
+ }
+
+ private static final char[][] AR = fromQualifiedName("java.util.concurrent.atomic.AtomicReference");
+ private static final TypeReference[][] AR_PARAMS = new TypeReference[5][];
+
+ private static final java.util.Map<String, char[][]> TYPE_MAP;
+ static {
+ Map<String, char[][]> m = new HashMap<String, char[][]>();
+ m.put("int", fromQualifiedName("java.lang.Integer"));
+ m.put("double", fromQualifiedName("java.lang.Double"));
+ m.put("float", fromQualifiedName("java.lang.Float"));
+ m.put("short", fromQualifiedName("java.lang.Short"));
+ m.put("byte", fromQualifiedName("java.lang.Byte"));
+ m.put("long", fromQualifiedName("java.lang.Long"));
+ m.put("boolean", fromQualifiedName("java.lang.Boolean"));
+ m.put("char", fromQualifiedName("java.lang.Character"));
+ TYPE_MAP = Collections.unmodifiableMap(m);
+ }
+
+ private static char[] valueName = "value".toCharArray();
+
+ private Statement[] createLazyGetterBody(ASTNode source, EclipseNode fieldNode) {
+ /*
+ java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();
+ if (value == null) {
+ synchronized (this.fieldName) {
+ value = this.fieldName.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<ValueType>(new ValueType());
+ this.fieldName.set(value);
+ }
+ }
+ }
+ return value.get();
+ */
+
+ FieldDeclaration field = (FieldDeclaration) fieldNode.get();
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long)pS << 32 | pE;
+
+ TypeReference componentType = copyType(field.type, source);
+ if (field.type instanceof SingleTypeReference && !(field.type instanceof ArrayTypeReference)) {
+ char[][] newType = TYPE_MAP.get(new String(((SingleTypeReference)field.type).token));
+ if (newType != null) {
+ componentType = new QualifiedTypeReference(newType, poss(source, 3));
+ Eclipse.setGeneratedBy(componentType, source);
+ }
+ }
+
+ Statement[] statements = new Statement[3];
+
+ /* java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get(); */ {
+ LocalDeclaration valueDecl = new LocalDeclaration(valueName, pS, pE);
+ Eclipse.setGeneratedBy(valueDecl, source);
+ TypeReference[][] typeParams = AR_PARAMS.clone();
+ typeParams[4] = new TypeReference[] {copyType(componentType, source)};
+ valueDecl.type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
+ valueDecl.type.sourceStart = pS; valueDecl.type.sourceEnd = pE;
+ Eclipse.setGeneratedBy(valueDecl.type, source);
+
+ MessageSend getter = new MessageSend();
+ Eclipse.setGeneratedBy(getter, source);
+ getter.sourceStart = pS; getter.sourceEnd = pE;
+ getter.selector = new char[] {'g', 'e', 't'};
+ getter.receiver = EclipseHandlerUtil.createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
+
+ valueDecl.initialization = getter;
+ Eclipse.setGeneratedBy(valueDecl.initialization, source);
+ statements[0] = valueDecl;
+ }
+
+ /*
+ if (value == null) {
+ synchronized (this.fieldName) {
+ value = this.fieldName.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<ValueType>(new ValueType());
+ this.fieldName.set(value);
+ }
+ }
+ }
+ */ {
+ EqualExpression cond = new EqualExpression(
+ new SingleNameReference(valueName, p), new NullLiteral(pS, pE),
+ BinaryExpression.EQUAL_EQUAL);
+ Eclipse.setGeneratedBy(cond.left, source);
+ Eclipse.setGeneratedBy(cond.right, source);
+ Eclipse.setGeneratedBy(cond, source);
+ Block then = new Block(0);
+ Eclipse.setGeneratedBy(then, source);
+ Expression lock = EclipseHandlerUtil.createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
+ Block inner = new Block(0);
+ Eclipse.setGeneratedBy(inner, source);
+ inner.statements = new Statement[2];
+ /* value = this.fieldName.get(); */ {
+ MessageSend getter = new MessageSend();
+ Eclipse.setGeneratedBy(getter, source);
+ getter.sourceStart = pS; getter.sourceEnd = pE;
+ getter.selector = new char[] {'g', 'e', 't'};
+ getter.receiver = EclipseHandlerUtil.createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
+ Assignment assign = new Assignment(new SingleNameReference(valueName, p), getter, pE);
+ Eclipse.setGeneratedBy(assign, source);
+ Eclipse.setGeneratedBy(assign.lhs, source);
+ inner.statements[0] = assign;
+ }
+ /* if (value == null) */ {
+ EqualExpression innerCond = new EqualExpression(
+ new SingleNameReference(valueName, p), new NullLiteral(pS, pE),
+ BinaryExpression.EQUAL_EQUAL);
+ Eclipse.setGeneratedBy(innerCond.left, source);
+ Eclipse.setGeneratedBy(innerCond.right, source);
+ Eclipse.setGeneratedBy(innerCond, source);
+ Block innerThen = new Block(0);
+ Eclipse.setGeneratedBy(innerThen, source);
+ innerThen.statements = new Statement[2];
+ /*value = new java.util.concurrent.atomic.AtomicReference<ValueType>(new ValueType()); */ {
+ AllocationExpression create = new AllocationExpression();
+ Eclipse.setGeneratedBy(create, source);
+ create.sourceStart = pS; create.sourceEnd = pE;
+ TypeReference[][] typeParams = AR_PARAMS.clone();
+ typeParams[4] = new TypeReference[] {copyType(componentType, source)};
+ create.type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
+ create.type.sourceStart = pS; create.type.sourceEnd = pE;
+ Eclipse.setGeneratedBy(create.type, source);
+ create.arguments = new Expression[] {field.initialization};
+ Assignment innerAssign = new Assignment(new SingleNameReference(valueName, p), create, pE);
+ Eclipse.setGeneratedBy(innerAssign, source);
+ Eclipse.setGeneratedBy(innerAssign.lhs, source);
+ innerThen.statements[0] = innerAssign;
+ }
+
+ /*this.fieldName.set(value);*/ {
+ MessageSend setter = new MessageSend();
+ Eclipse.setGeneratedBy(setter, source);
+ setter.sourceStart = pS; setter.sourceEnd = pE;
+ setter.receiver = EclipseHandlerUtil.createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
+ setter.selector = new char[] { 's', 'e', 't' };
+ setter.arguments = new Expression[] {
+ new SingleNameReference(valueName, p)};
+ Eclipse.setGeneratedBy(setter.arguments[0], source);
+ innerThen.statements[1] = setter;
+ }
+
+ IfStatement innerIf = new IfStatement(innerCond, innerThen, pS, pE);
+ Eclipse.setGeneratedBy(innerIf, source);
+ inner.statements[1] = innerIf;
+ }
+
+ SynchronizedStatement sync = new SynchronizedStatement(lock, inner, pS, pE);
+ Eclipse.setGeneratedBy(sync, source);
+ then.statements = new Statement[] {sync};
+
+ IfStatement ifStatement = new IfStatement(cond, then, pS, pE);
+ Eclipse.setGeneratedBy(ifStatement, source);
+ statements[1] = ifStatement;
+ }
+
+ /* return value.get(); */ {
+ MessageSend getter = new MessageSend();
+ Eclipse.setGeneratedBy(getter, source);
+ getter.sourceStart = pS; getter.sourceEnd = pE;
+ getter.selector = new char[] {'g', 'e', 't'};
+ getter.receiver = new SingleNameReference(valueName, p);
+ Eclipse.setGeneratedBy(getter.receiver, source);
+
+ statements[2] = new ReturnStatement(getter, pS, pE);
+ Eclipse.setGeneratedBy(statements[2], source);
+ }
+
+
+ // update the field type and init last
+
+ /* private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType> fieldName = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>>(); */ {
+
+ LocalDeclaration first = (LocalDeclaration) statements[0];
+ TypeReference innerType = copyType(first.type, source);
+
+ TypeReference[][] typeParams = AR_PARAMS.clone();
+ typeParams[4] = new TypeReference[] {copyType(innerType, source)};
+ TypeReference type = new ParameterizedQualifiedTypeReference(AR, typeParams, 0, poss(source, 5));
+ // Some magic here
+ type.sourceStart = -1; type.sourceEnd = -2;
+ Eclipse.setGeneratedBy(type, source);
+
+ field.type = type;
+ AllocationExpression init = new AllocationExpression();
+ // Some magic here
+ init.sourceStart = field.initialization.sourceStart; init.sourceEnd = field.initialization.sourceEnd;
+ init.type = copyType(type, source);
+ field.initialization = init;
+ }
+ return statements;
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index 48e688fd..2c3ca6ed 100644
--- a/src/core/lombok/eclipse/handlers/HandleSetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleSetter.java
@@ -35,6 +35,7 @@ import lombok.core.handlers.TransformationsUtil;
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;
@@ -206,7 +207,7 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> {
method.thrownExceptions = null;
method.typeParameters = null;
method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
- Expression fieldRef = createFieldAccessor(fieldNode, true, source);
+ Expression fieldRef = createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source);
NameReference fieldNameRef = new SingleNameReference(field.name, p);
Eclipse.setGeneratedBy(fieldNameRef, source);
Assignment assignment = new Assignment(fieldRef, fieldNameRef, (int)p);
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index b3f4abaa..f39cb129 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.java
@@ -38,6 +38,7 @@ import lombok.core.AST.Kind;
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;
@@ -94,7 +95,7 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
try {
includeFieldNames = ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
- generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, false);
+ generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER);
}
public boolean handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) {
@@ -115,11 +116,13 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
checkForBogusFieldNames(typeNode, annotation);
- return generateToString(typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true, ann.doNotUseGetters());
+ FieldAccess fieldAccess = ann.doNotUseGetters() ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
+
+ return generateToString(typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true, fieldAccess);
}
public boolean generateToString(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes,
- boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, boolean useFieldsDirectly) {
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
TypeDeclaration typeDecl = null;
if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
@@ -161,7 +164,7 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
switch (methodExists("toString", typeNode)) {
case NOT_EXISTS:
- MethodDeclaration toString = createToString(typeNode, nodesForToString, includeFieldNames, callSuper, errorNode.get(), useFieldsDirectly);
+ MethodDeclaration toString = createToString(typeNode, nodesForToString, includeFieldNames, callSuper, errorNode.get(), fieldAccess);
injectMethod(typeNode, toString);
return true;
case EXISTS_BY_LOMBOK:
@@ -176,7 +179,7 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
}
private MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
- boolean includeFieldNames, boolean callSuper, ASTNode source, boolean useFieldsDirectly) {
+ boolean includeFieldNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
String typeName = getTypeName(type);
char[] suffix = ")".toCharArray();
String infixS = ", ";
@@ -214,8 +217,8 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> {
}
for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, useFieldsDirectly);
- Expression fieldAccessor = createFieldAccessor(field, useFieldsDirectly, source);
+ TypeReference fType = getFieldType(field, fieldAccess);
+ Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
Expression ex;
if (fType.dimensions() > 0) {
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 8d524964..8799f2e1 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -90,7 +90,9 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
}
- return generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, ann.doNotUseGetters());
+ FieldAccess fieldAccess = ann.doNotUseGetters() ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
+
+ return generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess);
}
public void generateEqualsAndHashCodeForType(JavacNode typeNode, JavacNode errorNode) {
@@ -103,11 +105,11 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
}
}
- generateMethods(typeNode, errorNode, null, null, null, false, false);
+ generateMethods(typeNode, errorNode, null, null, null, false, FieldAccess.GETTER);
}
private boolean generateMethods(JavacNode typeNode, JavacNode errorNode, List<String> excludes, List<String> includes,
- Boolean callSuper, boolean whineIfExists, boolean useFieldsDirectly) {
+ Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
boolean notAClass = true;
if (typeNode.get() instanceof JCClassDecl) {
long flags = ((JCClassDecl)typeNode.get()).mods.flags;
@@ -173,7 +175,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
needsCanEqual = !isFinal || !isDirectDescendantOfObject;
- JCMethodDecl method = createEquals(typeNode, nodesForEquality, callSuper, useFieldsDirectly, needsCanEqual);
+ JCMethodDecl method = createEquals(typeNode, nodesForEquality, callSuper, fieldAccess, needsCanEqual);
injectMethod(typeNode, method);
break;
case EXISTS_BY_LOMBOK:
@@ -200,7 +202,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
}
switch (methodExists("hashCode", typeNode)) {
case NOT_EXISTS:
- JCMethodDecl method = createHashCode(typeNode, nodesForEquality, callSuper, useFieldsDirectly);
+ JCMethodDecl method = createHashCode(typeNode, nodesForEquality, callSuper, fieldAccess);
injectMethod(typeNode, method);
break;
case EXISTS_BY_LOMBOK:
@@ -215,7 +217,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
return true;
}
- private JCMethodDecl createHashCode(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, boolean useFieldsDirectly) {
+ private JCMethodDecl createHashCode(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess) {
TreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(chainDots(maker, typeNode, "java", "lang", "Override"), List.<JCExpression>nil());
@@ -247,8 +249,8 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
int tempCounter = 0;
for (JavacNode fieldNode : fields) {
- JCExpression fType = getFieldType(fieldNode, useFieldsDirectly);
- JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, useFieldsDirectly);
+ JCExpression fType = getFieldType(fieldNode, fieldAccess);
+ JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
if (fType instanceof JCPrimitiveTypeTree) {
switch (((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind()) {
case BOOLEAN:
@@ -256,7 +258,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
intoResult = intoResult.append(maker.Conditional(fieldAccessor, maker.Literal(1231), maker.Literal(1237)));
break;
case LONG:
- intoResult = intoResult.append(longToIntForHashCode(maker, fieldAccessor, createFieldAccessor(maker, fieldNode, useFieldsDirectly)));
+ intoResult = intoResult.append(longToIntForHashCode(maker, fieldAccessor, createFieldAccessor(maker, fieldNode, fieldAccess)));
break;
case FLOAT:
/* Float.floatToIntBits(this.fieldName) */
@@ -296,7 +298,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(fieldAccessor)));
} else /* objects */ {
/* this.fieldName == null ? 0 : this.fieldName.hashCode() */
- JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(createFieldAccessor(maker, fieldNode, useFieldsDirectly), typeNode.toName("hashCode")),
+ JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(createFieldAccessor(maker, fieldNode, fieldAccess), typeNode.toName("hashCode")),
List.<JCExpression>nil());
JCExpression thisEqualsNull = maker.Binary(JCTree.EQ, fieldAccessor, maker.Literal(TypeTags.BOT, null));
intoResult = intoResult.append(
@@ -329,7 +331,7 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
return maker.TypeCast(maker.TypeIdent(TypeTags.INT), xorBits);
}
- private JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, boolean useFieldsDirectly, boolean needsCanEqual) {
+ private JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual) {
TreeMaker maker = typeNode.getTreeMaker();
JCClassDecl type = (JCClassDecl) typeNode.get();
@@ -398,9 +400,9 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
}
for (JavacNode fieldNode : fields) {
- JCExpression fType = getFieldType(fieldNode, useFieldsDirectly);
- JCExpression thisFieldAccessor = createFieldAccessor(maker, fieldNode, useFieldsDirectly);
- JCExpression otherFieldAccessor = createFieldAccessor(maker, fieldNode, useFieldsDirectly, maker.Ident(otherName));
+ JCExpression fType = getFieldType(fieldNode, fieldAccess);
+ JCExpression thisFieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
+ JCExpression otherFieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess, maker.Ident(otherName));
if (fType instanceof JCPrimitiveTypeTree) {
switch (((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind()) {
case FLOAT:
@@ -432,8 +434,8 @@ public class HandleEqualsAndHashCode implements JavacAnnotationHandler<EqualsAnd
JCExpression thisEqualsNull = maker.Binary(JCTree.EQ, thisFieldAccessor, maker.Literal(TypeTags.BOT, null));
JCExpression otherNotEqualsNull = maker.Binary(JCTree.NE, otherFieldAccessor, maker.Literal(TypeTags.BOT, null));
JCExpression thisEqualsThat = maker.Apply(List.<JCExpression>nil(),
- maker.Select(createFieldAccessor(maker, fieldNode, useFieldsDirectly), typeNode.toName("equals")),
- List.of(createFieldAccessor(maker, fieldNode, useFieldsDirectly, maker.Ident(otherName))));
+ maker.Select(createFieldAccessor(maker, fieldNode, fieldAccess), typeNode.toName("equals")),
+ List.of(createFieldAccessor(maker, fieldNode, fieldAccess, maker.Ident(otherName))));
JCExpression fieldsAreNotEqual = maker.Conditional(thisEqualsNull, otherNotEqualsNull, maker.Unary(JCTree.NOT, thisEqualsThat));
statements = statements.append(maker.If(fieldsAreNotEqual, returnBool(maker, false), null));
}
diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java
index e58069e1..be1bc49a 100644
--- a/src/core/lombok/javac/handlers/HandleGetter.java
+++ b/src/core/lombok/javac/handlers/HandleGetter.java
@@ -24,6 +24,9 @@ package lombok.javac.handlers;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
@@ -33,17 +36,28 @@ import lombok.core.handlers.TransformationsUtil;
import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
+import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.TypeTags;
+import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
+import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCNewClass;
+import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCSynchronized;
+import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
@@ -78,7 +92,7 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> {
}
for (JavacNode field : typeNode.down()) {
- if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, errorNode.get(), level, List.<JCExpression>nil());
+ if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, errorNode.get(), level, List.<JCExpression>nil(), false);
}
return true;
@@ -108,8 +122,9 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> {
*
* @param fieldNode The node representing the field you want a getter for.
* @param pos The node responsible for generating the getter (the {@code @Data} or {@code @Getter} annotation).
+ * @param lazy
*/
- public void generateGetterForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, List<JCExpression> onMethod) {
+ public void generateGetterForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, List<JCExpression> onMethod, boolean lazy) {
for (JavacNode child : fieldNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
if (Javac.annotationTypeMatches(Getter.class, child)) {
@@ -119,7 +134,7 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> {
}
}
- createGetterForField(level, fieldNode, fieldNode, false, onMethod);
+ createGetterForField(level, fieldNode, fieldNode, false, onMethod, lazy);
}
@Override public boolean handle(AnnotationValues<Getter> annotation, JCAnnotation ast, JavacNode annotationNode) {
@@ -127,38 +142,58 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> {
markAnnotationAsProcessed(annotationNode, Getter.class);
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
JavacNode node = annotationNode.up();
- AccessLevel level = annotation.getInstance().value();
- if (level == AccessLevel.NONE) return true;
+ Getter annotationInstance = annotation.getInstance();
+ AccessLevel level = annotationInstance.value();
+ boolean lazy = annotationInstance.lazy();
+ if (level == AccessLevel.NONE) {
+ if (lazy) {
+ annotationNode.addWarning("'lazy' does not work with AccessLevel.NONE.");
+ }
+ return true;
+ }
if (node == null) return false;
List<JCExpression> onMethod = getAndRemoveAnnotationParameter(ast, "onMethod");
if (node.getKind() == Kind.FIELD) {
- return createGetterForFields(level, fields, annotationNode, true, onMethod);
+ return createGetterForFields(level, fields, annotationNode, true, onMethod, lazy);
}
if (node.getKind() == Kind.TYPE) {
if (!onMethod.isEmpty()) annotationNode.addError("'onMethod' is not supported for @Getter on a type.");
+ if (lazy) annotationNode.addError("'lazy' is not supported for @Getter on a type.");
return generateGetterForType(node, annotationNode, level, false);
}
return false;
}
- private boolean createGetterForFields(AccessLevel level, Collection<JavacNode> fieldNodes, JavacNode errorNode, boolean whineIfExists, List<JCExpression> onMethod) {
+ private boolean createGetterForFields(AccessLevel level, Collection<JavacNode> fieldNodes, JavacNode errorNode, boolean whineIfExists, List<JCExpression> onMethod, boolean lazy) {
for (JavacNode fieldNode : fieldNodes) {
- createGetterForField(level, fieldNode, errorNode, whineIfExists, onMethod);
+ createGetterForField(level, fieldNode, errorNode, whineIfExists, onMethod, lazy);
}
return true;
}
private boolean createGetterForField(AccessLevel level,
- JavacNode fieldNode, JavacNode errorNode, boolean whineIfExists, List<JCExpression> onMethod) {
+ JavacNode fieldNode, JavacNode errorNode, boolean whineIfExists, List<JCExpression> onMethod, boolean lazy) {
if (fieldNode.getKind() != Kind.FIELD) {
errorNode.addError("@Getter is only supported on a class or a field.");
return true;
}
JCVariableDecl fieldDecl = (JCVariableDecl)fieldNode.get();
+
+ if (lazy) {
+ if ((fieldDecl.mods.flags & Flags.PRIVATE) == 0 || (fieldDecl.mods.flags & Flags.FINAL) == 0) {
+ errorNode.addError("'lazy' requires the field to be private and final.");
+ return true;
+ }
+ if (fieldDecl.init == null) {
+ errorNode.addError("'lazy' requires field initialization.");
+ return true;
+ }
+ }
+
String methodName = toGetterName(fieldDecl);
for (String altName : toAllGetterNames(fieldDecl)) {
@@ -181,19 +216,26 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> {
long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC);
- injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), onMethod));
+ injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), onMethod, lazy));
return true;
}
- private JCMethodDecl createGetter(long access, JavacNode field, TreeMaker treeMaker, List<JCExpression> onMethod) {
+ private JCMethodDecl createGetter(long access, JavacNode field, TreeMaker treeMaker, List<JCExpression> onMethod, boolean lazy) {
JCVariableDecl fieldNode = (JCVariableDecl) field.get();
- JCExpression fieldRef = createFieldAccessor(treeMaker, field, true);
- JCStatement returnStatement = treeMaker.Return(fieldRef);
- JCBlock methodBody = treeMaker.Block(0, List.of(returnStatement));
+ // Remember the type; lazy will change it;
+ JCExpression methodType = copyType(treeMaker, fieldNode);
+
+ List<JCStatement> statements;
+ if (lazy) {
+ statements = createLazyGetterBody(treeMaker, field);
+ } else {
+ statements = createSimpleGetterBody(treeMaker, field);
+ }
+
+ JCBlock methodBody = treeMaker.Block(0, statements);
Name methodName = field.toName(toGetterName(fieldNode));
- JCExpression methodType = fieldNode.type != null ? treeMaker.Type(fieldNode.type) : fieldNode.vartype;
List<JCTypeParameter> methodGenericParams = List.nil();
List<JCVariableDecl> parameters = List.nil();
@@ -209,6 +251,122 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> {
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue);
}
+ private List<JCStatement> createSimpleGetterBody(TreeMaker treeMaker, JavacNode field) {
+ return List.<JCStatement>of(treeMaker.Return(createFieldAccessor(treeMaker, field, FieldAccess.ALWAYS_FIELD)));
+ }
+
+ private static final String AR = "java.util.concurrent.atomic.AtomicReference";
+ private static final List<JCExpression> NIL_EXPRESSION = List.nil();
+
+ private static final java.util.Map<Integer, String> TYPE_MAP;
+ static {
+ Map<Integer, String> m = new HashMap<Integer, String>();
+ m.put(TypeTags.INT, "java.lang.Integer");
+ m.put(TypeTags.DOUBLE, "java.lang.Double");
+ m.put(TypeTags.FLOAT, "java.lang.Float");
+ m.put(TypeTags.SHORT, "java.lang.Short");
+ m.put(TypeTags.BYTE, "java.lang.Byte");
+ m.put(TypeTags.LONG, "java.lang.Long");
+ m.put(TypeTags.BOOLEAN, "java.lang.Boolean");
+ m.put(TypeTags.CHAR, "java.lang.Character");
+ TYPE_MAP = Collections.unmodifiableMap(m);
+ }
+
+ private List<JCStatement> createLazyGetterBody(TreeMaker maker, JavacNode fieldNode) {
+ /*
+ java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();
+ if (value == null) {
+ synchronized (this.fieldName) {
+ value = this.fieldName.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<ValueType>(new ValueType());
+ this.fieldName.set(value);
+ }
+ }
+ }
+ return value.get();
+ */
+
+ List<JCStatement> statements = List.nil();
+
+ JCVariableDecl field = (JCVariableDecl) fieldNode.get();
+ field.type = null;
+ if (field.vartype instanceof JCPrimitiveTypeTree) {
+ String boxed = TYPE_MAP.get(((JCPrimitiveTypeTree)field.vartype).typetag);
+ if (boxed != null) {
+ field.vartype = chainDotsString(maker, fieldNode, boxed);
+ }
+ }
+
+ Name valueName = fieldNode.toName("value");
+
+ /* java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();*/ {
+ JCTypeApply valueVarType = maker.TypeApply(chainDotsString(maker, fieldNode, AR), List.of(copyType(maker, field)));
+ statements = statements.append(maker.VarDef(maker.Modifiers(0), valueName, valueVarType, callGet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD))));
+ }
+
+ /* if (value == null) { */ {
+ JCSynchronized synchronizedStatement;
+ /* synchronized (this.fieldName) { */ {
+ List<JCStatement> synchronizedStatements = List.nil();
+ /* value = this.fieldName.get(); */ {
+ JCExpressionStatement newAssign = maker.Exec(maker.Assign(maker.Ident(valueName), callGet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD))));
+ synchronizedStatements = synchronizedStatements.append(newAssign);
+ }
+
+ /* if (value == null) { */ {
+ List<JCStatement> innerIfStatements = List.nil();
+ /* value = new java.util.concurrent.atomic.AtomicReference<ValueType>(new ValueType());*/ {
+ JCTypeApply valueVarType = maker.TypeApply(chainDotsString(maker, fieldNode, AR), List.of(copyType(maker, field)));
+ JCNewClass newInstance = maker.NewClass(null, NIL_EXPRESSION, valueVarType, List.<JCExpression>of(field.init), null);
+
+ JCStatement statement = maker.Exec(maker.Assign(maker.Ident(valueName), newInstance));
+ innerIfStatements = innerIfStatements.append(statement);
+ }
+ /* this.fieldName.set(value); */ {
+ JCStatement statement = callSet(fieldNode, createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD), maker.Ident(valueName));
+ innerIfStatements = innerIfStatements.append(statement);
+ }
+
+ JCBinary isNull = maker.Binary(JCTree.EQ, maker.Ident(valueName), maker.Literal(TypeTags.BOT, null));
+ JCIf ifStatement = maker.If(isNull, maker.Block(0, innerIfStatements), null);
+ synchronizedStatements = synchronizedStatements.append(ifStatement);
+ }
+
+ synchronizedStatement = maker.Synchronized(createFieldAccessor(maker, fieldNode, FieldAccess.ALWAYS_FIELD), maker.Block(0, synchronizedStatements));
+ }
+
+ JCBinary isNull = maker.Binary(JCTree.EQ, maker.Ident(valueName), maker.Literal(TypeTags.BOT, null));
+ JCIf ifStatement = maker.If(isNull, maker.Block(0, List.<JCStatement>of(synchronizedStatement)), null);
+ statements = statements.append(ifStatement);
+ }
+ /* return value.get(); */
+ statements = statements.append(maker.Return(callGet(fieldNode, maker.Ident(valueName))));
+
+ // update the field type and init last
+
+ /* private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType> fieldName = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>>(); */ {
+ field.vartype = maker.TypeApply(chainDotsString(maker, fieldNode, AR), List.<JCExpression>of(maker.TypeApply(chainDotsString(maker, fieldNode, AR), List.of(copyType(maker, field)))));
+ field.init = maker.NewClass(null, NIL_EXPRESSION, copyType(maker, field), NIL_EXPRESSION, null);
+ }
+
+ return statements;
+ }
+
+ private JCMethodInvocation callGet(JavacNode source, JCExpression receiver) {
+ TreeMaker maker = source.getTreeMaker();
+ return maker.Apply(NIL_EXPRESSION, maker.Select(receiver, source.toName("get")), NIL_EXPRESSION);
+ }
+
+ private JCStatement callSet(JavacNode source, JCExpression receiver, JCExpression value) {
+ TreeMaker maker = source.getTreeMaker();
+ return maker.Exec(maker.Apply(NIL_EXPRESSION, maker.Select(receiver, source.toName("set")), List.<JCExpression>of(value)));
+ }
+
+ private JCExpression copyType(TreeMaker treeMaker, JCVariableDecl fieldNode) {
+ return fieldNode.type != null ? treeMaker.Type(fieldNode.type) : fieldNode.vartype;
+ }
+
@Override public boolean isResolutionBased() {
return false;
}
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index 68de9ebe..875c63dd 100644
--- a/src/core/lombok/javac/handlers/HandleSetter.java
+++ b/src/core/lombok/javac/handlers/HandleSetter.java
@@ -38,6 +38,7 @@ import lombok.core.handlers.TransformationsUtil;
import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
+import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
@@ -197,7 +198,7 @@ public class HandleSetter implements JavacAnnotationHandler<Setter> {
private JCMethodDecl createSetter(long access, JavacNode field, TreeMaker treeMaker, List<JCExpression> onMethod, List<JCExpression> onParam) {
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
- JCExpression fieldRef = createFieldAccessor(treeMaker, field, true);
+ JCExpression fieldRef = createFieldAccessor(treeMaker, field, FieldAccess.ALWAYS_FIELD);
JCAssign assign = treeMaker.Assign(fieldRef, treeMaker.Ident(fieldDecl.name));
List<JCStatement> statements;
diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java
index f52602c3..82e32c06 100644
--- a/src/core/lombok/javac/handlers/HandleToString.java
+++ b/src/core/lombok/javac/handlers/HandleToString.java
@@ -29,6 +29,7 @@ import lombok.core.AST.Kind;
import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
+import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
@@ -88,7 +89,9 @@ public class HandleToString implements JavacAnnotationHandler<ToString> {
annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
}
- return generateToString(typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true, ann.doNotUseGetters());
+ FieldAccess fieldAccess = ann.doNotUseGetters() ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
+
+ return generateToString(typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true, fieldAccess);
}
public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) {
@@ -105,11 +108,11 @@ public class HandleToString implements JavacAnnotationHandler<ToString> {
try {
includeFieldNames = ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
- generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, false);
+ generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER);
}
private boolean generateToString(JavacNode typeNode, JavacNode errorNode, List<String> excludes, List<String> includes,
- boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, boolean useFieldsDirectly) {
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
boolean notAClass = true;
if (typeNode.get() instanceof JCClassDecl) {
long flags = ((JCClassDecl)typeNode.get()).mods.flags;
@@ -150,7 +153,7 @@ public class HandleToString implements JavacAnnotationHandler<ToString> {
switch (methodExists("toString", typeNode)) {
case NOT_EXISTS:
- JCMethodDecl method = createToString(typeNode, nodesForToString, includeFieldNames, callSuper, useFieldsDirectly);
+ JCMethodDecl method = createToString(typeNode, nodesForToString, includeFieldNames, callSuper, fieldAccess);
injectMethod(typeNode, method);
return true;
case EXISTS_BY_LOMBOK:
@@ -165,7 +168,7 @@ public class HandleToString implements JavacAnnotationHandler<ToString> {
}
- private JCMethodDecl createToString(JavacNode typeNode, List<JavacNode> fields, boolean includeFieldNames, boolean callSuper, boolean useFieldsDirectly) {
+ private JCMethodDecl createToString(JavacNode typeNode, List<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess) {
TreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(chainDots(maker, typeNode, "java", "lang", "Override"), List.<JCExpression>nil());
@@ -202,9 +205,9 @@ public class HandleToString implements JavacAnnotationHandler<ToString> {
JCVariableDecl field = (JCVariableDecl) fieldNode.get();
JCExpression expr;
- JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, useFieldsDirectly);
+ JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
- if (getFieldType(fieldNode, useFieldsDirectly) instanceof JCArrayTypeTree) {
+ if (getFieldType(fieldNode, fieldAccess) instanceof JCArrayTypeTree) {
boolean multiDim = ((JCArrayTypeTree)field.vartype).elemtype instanceof JCArrayTypeTree;
boolean primitiveArray = ((JCArrayTypeTree)field.vartype).elemtype instanceof JCPrimitiveTypeTree;
boolean useDeepTS = multiDim || !primitiveArray;
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index bf356853..d1884e5c 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -345,13 +345,34 @@ public class JavacHandlerUtil {
return null;
}
+ 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;
+
+ // If @Getter(lazy = true) is used, then using it is mandatory.
+ for (JavacNode child : field.down()) {
+ if (child.getKind() != Kind.ANNOTATION) continue;
+ if (Javac.annotationTypeMatches(Getter.class, child)) {
+ AnnotationValues<Getter> ann = Javac.createAnnotation(Getter.class, child);
+ if (ann.getInstance().lazy()) return true;
+ }
+ }
+ return false;
+ }
+
/**
* Returns the type of the field, unless a getter exists for this field, in which case the return type of the getter is returned.
*
* @see #createFieldAccessor(TreeMaker, JavacNode)
*/
- static JCExpression getFieldType(JavacNode field, boolean useFieldsDirectly) {
- GetterMethod getter = useFieldsDirectly ? null : findGetter(field);
+ static JCExpression getFieldType(JavacNode field, FieldAccess fieldAccess) {
+ boolean lookForGetter = lookForGetter(field, fieldAccess);
+
+ GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
return ((JCVariableDecl)field.get()).vartype;
@@ -363,12 +384,14 @@ public class JavacHandlerUtil {
/**
* Creates an expression that reads the field. Will either be {@code this.field} or {@code this.getField()} depending on whether or not there's a getter.
*/
- static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, boolean useFieldsDirectly) {
- return createFieldAccessor(maker, field, useFieldsDirectly, null);
+ static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, FieldAccess fieldAccess) {
+ return createFieldAccessor(maker, field, fieldAccess, null);
}
- static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, boolean useFieldsDirectly, JCExpression receiver) {
- GetterMethod getter = useFieldsDirectly ? null : findGetter(field);
+ static JCExpression createFieldAccessor(TreeMaker maker, JavacNode field, FieldAccess fieldAccess, JCExpression receiver) {
+ boolean lookForGetter = lookForGetter(field, fieldAccess);
+
+ GetterMethod getter = lookForGetter ? findGetter(field) : null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
if (getter == null) {
diff --git a/test/transform/resource/after-delombok/GetterLazy.java b/test/transform/resource/after-delombok/GetterLazy.java
new file mode 100644
index 00000000..d7f97f0d
--- /dev/null
+++ b/test/transform/resource/after-delombok/GetterLazy.java
@@ -0,0 +1,19 @@
+class GetterLazy {
+ static class ValueType {
+ }
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>> fieldName = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>>();
+ @java.lang.SuppressWarnings("all")
+ public ValueType getFieldName() {
+ java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();
+ if (value == null) {
+ synchronized (this.fieldName) {
+ value = this.fieldName.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<ValueType>(new ValueType());
+ this.fieldName.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+}
diff --git a/test/transform/resource/after-delombok/GetterLazyEahcToString.java b/test/transform/resource/after-delombok/GetterLazyEahcToString.java
new file mode 100644
index 00000000..f085722d
--- /dev/null
+++ b/test/transform/resource/after-delombok/GetterLazyEahcToString.java
@@ -0,0 +1,58 @@
+class GetterLazyEahcToString {
+
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<String>> value = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<String>>();
+ private final String value2 = "";
+
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof GetterLazyEahcToString)) return false;
+ final GetterLazyEahcToString other = (GetterLazyEahcToString)o;
+ if (!other.canEqual(this)) return false;
+ if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
+ if (this.value2 == null ? other.value2 != null : !this.value2.equals(other.value2)) return false;
+ return true;
+ }
+
+ @java.lang.SuppressWarnings("all")
+ public boolean canEqual(final java.lang.Object other) {
+ return other instanceof GetterLazyEahcToString;
+ }
+
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = result * PRIME + (this.getValue() == null ? 0 : this.getValue().hashCode());
+ result = result * PRIME + (this.value2 == null ? 0 : this.value2.hashCode());
+ return result;
+ }
+
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "GetterLazyEahcToString(value=" + this.getValue() + ", value2=" + this.value2 + ")";
+ }
+
+ @java.lang.SuppressWarnings("all")
+ public String getValue() {
+ java.util.concurrent.atomic.AtomicReference<String> value = this.value.get();
+ if (value == null) {
+ synchronized (this.value) {
+ value = this.value.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<String>("");
+ this.value.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+
+ @java.lang.SuppressWarnings("all")
+ public String getValue2() {
+ return this.value2;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/GetterLazyInvalid.java b/test/transform/resource/after-delombok/GetterLazyInvalid.java
new file mode 100644
index 00000000..25963921
--- /dev/null
+++ b/test/transform/resource/after-delombok/GetterLazyInvalid.java
@@ -0,0 +1,25 @@
+class GetterLazyInvalidNotFinal {
+ private String fieldName = "";
+}
+class GetterLazyInvalidNotPrivate {
+ final String fieldName = "";
+}
+class GetterLazyInvalidNotPrivateFinal {
+ String fieldName = "";
+}
+class GetterLazyInvalidNone {
+ private final String fieldName = "";
+}
+class GetterLazyInvalidClass {
+ private final String fieldName = "";
+ @java.lang.SuppressWarnings("all")
+ public String getFieldName() {
+ return this.fieldName;
+ }
+}
+class GetterLazyInvalidNoInit {
+ private final String fieldName;
+ GetterLazyInvalidNoInit() {
+ this.fieldName = "foo";
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/GetterLazyNative.java b/test/transform/resource/after-delombok/GetterLazyNative.java
new file mode 100644
index 00000000..650d0496
--- /dev/null
+++ b/test/transform/resource/after-delombok/GetterLazyNative.java
@@ -0,0 +1,137 @@
+class GetterLazyNative {
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Boolean>> booleanField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Boolean>>();
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Byte>> byteField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Byte>>();
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Short>> shortField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Short>>();
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Integer>> intField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Integer>>();
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Long>> longField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Long>>();
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Float>> floatField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Float>>();
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Double>> doubleField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Double>>();
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Character>> charField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Character>>();
+ private final java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<int[]>> intArrayField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<int[]>>();
+ @java.lang.SuppressWarnings("all")
+ public boolean getBooleanField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Boolean> value = this.booleanField.get();
+ if (value == null) {
+ synchronized (this.booleanField) {
+ value = this.booleanField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Boolean>(true);
+ this.booleanField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ @java.lang.SuppressWarnings("all")
+ public byte getByteField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Byte> value = this.byteField.get();
+ if (value == null) {
+ synchronized (this.byteField) {
+ value = this.byteField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Byte>(1);
+ this.byteField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ @java.lang.SuppressWarnings("all")
+ public short getShortField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Short> value = this.shortField.get();
+ if (value == null) {
+ synchronized (this.shortField) {
+ value = this.shortField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Short>(1);
+ this.shortField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ @java.lang.SuppressWarnings("all")
+ public int getIntField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Integer> value = this.intField.get();
+ if (value == null) {
+ synchronized (this.intField) {
+ value = this.intField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Integer>(1);
+ this.intField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ @java.lang.SuppressWarnings("all")
+ public long getLongField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Long> value = this.longField.get();
+ if (value == null) {
+ synchronized (this.longField) {
+ value = this.longField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Long>(1);
+ this.longField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ @java.lang.SuppressWarnings("all")
+ public float getFloatField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Float> value = this.floatField.get();
+ if (value == null) {
+ synchronized (this.floatField) {
+ value = this.floatField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Float>(1.0F);
+ this.floatField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ @java.lang.SuppressWarnings("all")
+ public double getDoubleField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Double> value = this.doubleField.get();
+ if (value == null) {
+ synchronized (this.doubleField) {
+ value = this.doubleField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Double>(1.0);
+ this.doubleField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ @java.lang.SuppressWarnings("all")
+ public char getCharField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Character> value = this.charField.get();
+ if (value == null) {
+ synchronized (this.charField) {
+ value = this.charField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Character>('1');
+ this.charField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ @java.lang.SuppressWarnings("all")
+ public int[] getIntArrayField() {
+ java.util.concurrent.atomic.AtomicReference<int[]> value = this.intArrayField.get();
+ if (value == null) {
+ synchronized (this.intArrayField) {
+ value = this.intArrayField.get();
+ if (value == null) {
+ value = new java.util.concurrent.atomic.AtomicReference<int[]>(new int[]{1});
+ this.intArrayField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/GetterLazy.java b/test/transform/resource/after-ecj/GetterLazy.java
new file mode 100644
index 00000000..669a9a81
--- /dev/null
+++ b/test/transform/resource/after-ecj/GetterLazy.java
@@ -0,0 +1,27 @@
+class GetterLazy {
+ static class ValueType {
+ ValueType() {
+ super();
+ }
+ }
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>> fieldName = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<ValueType>>();
+ public @java.lang.SuppressWarnings("all") ValueType getFieldName() {
+ java.util.concurrent.atomic.AtomicReference<ValueType> value = this.fieldName.get();
+ if ((value == null))
+ {
+ synchronized (this.fieldName)
+ {
+ value = this.fieldName.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<ValueType>(new ValueType());
+ this.fieldName.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ GetterLazy() {
+ super();
+ }
+}
diff --git a/test/transform/resource/after-ecj/GetterLazyEahcToString.java b/test/transform/resource/after-ecj/GetterLazyEahcToString.java
new file mode 100644
index 00000000..6261ce38
--- /dev/null
+++ b/test/transform/resource/after-ecj/GetterLazyEahcToString.java
@@ -0,0 +1,53 @@
+@lombok.EqualsAndHashCode(doNotUseGetters = true) @lombok.ToString(doNotUseGetters = true) class GetterLazyEahcToString {
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<String>> value = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<String>>();
+ private final @lombok.Getter String value2 = "";
+ public @java.lang.SuppressWarnings("all") String getValue() {
+ java.util.concurrent.atomic.AtomicReference<String> value = this.value.get();
+ if ((value == null))
+ {
+ synchronized (this.value)
+ {
+ value = this.value.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<String>("");
+ this.value.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") String getValue2() {
+ return this.value2;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
+ if ((o == this))
+ return true;
+ if ((! (o instanceof GetterLazyEahcToString)))
+ return false;
+ final GetterLazyEahcToString other = (GetterLazyEahcToString) o;
+ if ((! other.canEqual(this)))
+ return false;
+ if (((this.getValue() == null) ? (other.getValue() != null) : (! this.getValue().equals(other.getValue()))))
+ return false;
+ if (((this.value2 == null) ? (other.value2 != null) : (! this.value2.equals(other.value2))))
+ return false;
+ return true;
+ }
+ public @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
+ return (other instanceof GetterLazyEahcToString);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
+ final int PRIME = 31;
+ int result = 1;
+ result = ((result * PRIME) + ((this.getValue() == null) ? 0 : this.getValue().hashCode()));
+ result = ((result * PRIME) + ((this.value2 == null) ? 0 : this.value2.hashCode()));
+ return result;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("GetterLazyEahcToString(value=" + this.getValue()) + ", value2=") + this.value2) + ")");
+ }
+ GetterLazyEahcToString() {
+ super();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/GetterLazyInvalid.java b/test/transform/resource/after-ecj/GetterLazyInvalid.java
new file mode 100644
index 00000000..eaa22d71
--- /dev/null
+++ b/test/transform/resource/after-ecj/GetterLazyInvalid.java
@@ -0,0 +1,40 @@
+class GetterLazyInvalidNotFinal {
+ private @lombok.Getter(lazy = true) String fieldName = "";
+ GetterLazyInvalidNotFinal() {
+ super();
+ }
+}
+class GetterLazyInvalidNotPrivate {
+ final @lombok.Getter(lazy = true) String fieldName = "";
+ GetterLazyInvalidNotPrivate() {
+ super();
+ }
+}
+class GetterLazyInvalidNotPrivateFinal {
+ @lombok.Getter(lazy = true) String fieldName = "";
+ GetterLazyInvalidNotPrivateFinal() {
+ super();
+ }
+}
+class GetterLazyInvalidNone {
+ private final @lombok.Getter(lazy = true,value = lombok.AccessLevel.NONE) String fieldName = "";
+ GetterLazyInvalidNone() {
+ super();
+ }
+}
+@lombok.Getter(lazy = true) class GetterLazyInvalidClass {
+ private final String fieldName = "";
+ public @java.lang.SuppressWarnings("all") String getFieldName() {
+ return this.fieldName;
+ }
+ GetterLazyInvalidClass() {
+ super();
+ }
+}
+class GetterLazyInvalidNoInit {
+ private final @lombok.Getter(lazy = true) String fieldName;
+ GetterLazyInvalidNoInit() {
+ super();
+ this.fieldName = "foo";
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/GetterLazyNative.java b/test/transform/resource/after-ecj/GetterLazyNative.java
new file mode 100644
index 00000000..6c90a101
--- /dev/null
+++ b/test/transform/resource/after-ecj/GetterLazyNative.java
@@ -0,0 +1,158 @@
+class GetterLazyNative {
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Boolean>> booleanField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Boolean>>();
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Byte>> byteField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Byte>>();
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Short>> shortField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Short>>();
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Integer>> intField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Integer>>();
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Long>> longField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Long>>();
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Float>> floatField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Float>>();
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Double>> doubleField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Double>>();
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Character>> charField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<java.lang.Character>>();
+ private final @lombok.Getter(lazy = true) java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<int[]>> intArrayField = new java.util.concurrent.atomic.AtomicReference<java.util.concurrent.atomic.AtomicReference<int[]>>();
+ public @java.lang.SuppressWarnings("all") boolean isBooleanField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Boolean> value = this.booleanField.get();
+ if ((value == null))
+ {
+ synchronized (this.booleanField)
+ {
+ value = this.booleanField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Boolean>(true);
+ this.booleanField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") byte getByteField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Byte> value = this.byteField.get();
+ if ((value == null))
+ {
+ synchronized (this.byteField)
+ {
+ value = this.byteField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Byte>(1);
+ this.byteField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") short getShortField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Short> value = this.shortField.get();
+ if ((value == null))
+ {
+ synchronized (this.shortField)
+ {
+ value = this.shortField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Short>(1);
+ this.shortField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") int getIntField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Integer> value = this.intField.get();
+ if ((value == null))
+ {
+ synchronized (this.intField)
+ {
+ value = this.intField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Integer>(1);
+ this.intField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") long getLongField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Long> value = this.longField.get();
+ if ((value == null))
+ {
+ synchronized (this.longField)
+ {
+ value = this.longField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Long>(1);
+ this.longField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") float getFloatField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Float> value = this.floatField.get();
+ if ((value == null))
+ {
+ synchronized (this.floatField)
+ {
+ value = this.floatField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Float>(1.0f);
+ this.floatField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") double getDoubleField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Double> value = this.doubleField.get();
+ if ((value == null))
+ {
+ synchronized (this.doubleField)
+ {
+ value = this.doubleField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Double>(1.0);
+ this.doubleField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") char getCharField() {
+ java.util.concurrent.atomic.AtomicReference<java.lang.Character> value = this.charField.get();
+ if ((value == null))
+ {
+ synchronized (this.charField)
+ {
+ value = this.charField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<java.lang.Character>('1');
+ this.charField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ public @java.lang.SuppressWarnings("all") int[] getIntArrayField() {
+ java.util.concurrent.atomic.AtomicReference<int[]> value = this.intArrayField.get();
+ if ((value == null))
+ {
+ synchronized (this.intArrayField)
+ {
+ value = this.intArrayField.get();
+ if ((value == null))
+ {
+ value = new java.util.concurrent.atomic.AtomicReference<int[]>(new int[]{1});
+ this.intArrayField.set(value);
+ }
+ }
+ }
+ return value.get();
+ }
+ GetterLazyNative() {
+ super();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/GetterLazy.java b/test/transform/resource/before/GetterLazy.java
new file mode 100644
index 00000000..51c7a921
--- /dev/null
+++ b/test/transform/resource/before/GetterLazy.java
@@ -0,0 +1,7 @@
+class GetterLazy {
+ static class ValueType {
+ }
+
+ @lombok.Getter(lazy=true)
+ private final ValueType fieldName = new ValueType();
+}
diff --git a/test/transform/resource/before/GetterLazyEahcToString.java b/test/transform/resource/before/GetterLazyEahcToString.java
new file mode 100644
index 00000000..642f8deb
--- /dev/null
+++ b/test/transform/resource/before/GetterLazyEahcToString.java
@@ -0,0 +1,8 @@
+@lombok.EqualsAndHashCode(doNotUseGetters = true)
+@lombok.ToString(doNotUseGetters = true)
+class GetterLazyEahcToString {
+ @lombok.Getter(lazy=true)
+ private final String value = "";
+ @lombok.Getter
+ private final String value2 = "";
+}
diff --git a/test/transform/resource/before/GetterLazyInvalid.java b/test/transform/resource/before/GetterLazyInvalid.java
new file mode 100644
index 00000000..cc9f9501
--- /dev/null
+++ b/test/transform/resource/before/GetterLazyInvalid.java
@@ -0,0 +1,27 @@
+class GetterLazyInvalidNotFinal {
+ @lombok.Getter(lazy=true)
+ private String fieldName = "";
+}
+class GetterLazyInvalidNotPrivate {
+ @lombok.Getter(lazy=true)
+ final String fieldName = "";
+}
+class GetterLazyInvalidNotPrivateFinal {
+ @lombok.Getter(lazy=true)
+ String fieldName = "";
+}
+class GetterLazyInvalidNone {
+ @lombok.Getter(lazy=true, value=lombok.AccessLevel.NONE)
+ private final String fieldName = "";
+}
+@lombok.Getter(lazy = true)
+class GetterLazyInvalidClass {
+ private final String fieldName = "";
+}
+class GetterLazyInvalidNoInit {
+ @lombok.Getter(lazy = true)
+ private final String fieldName;
+ GetterLazyInvalidNoInit() {
+ this.fieldName = "foo";
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/before/GetterLazyNative.java b/test/transform/resource/before/GetterLazyNative.java
new file mode 100644
index 00000000..9b290bc7
--- /dev/null
+++ b/test/transform/resource/before/GetterLazyNative.java
@@ -0,0 +1,28 @@
+class GetterLazyNative {
+ @lombok.Getter(lazy=true)
+ private final boolean booleanField = true;
+
+ @lombok.Getter(lazy=true)
+ private final byte byteField = 1;
+
+ @lombok.Getter(lazy=true)
+ private final short shortField = 1;
+
+ @lombok.Getter(lazy=true)
+ private final int intField = 1;
+
+ @lombok.Getter(lazy=true)
+ private final long longField = 1;
+
+ @lombok.Getter(lazy=true)
+ private final float floatField = 1.0f;
+
+ @lombok.Getter(lazy=true)
+ private final double doubleField = 1.0;
+
+ @lombok.Getter(lazy=true)
+ private final char charField = '1';
+
+ @lombok.Getter(lazy=true)
+ private final int[] intArrayField = new int[] {1};
+}
diff --git a/test/transform/resource/messages-delombok/GetterLazyInvalid.java.messages b/test/transform/resource/messages-delombok/GetterLazyInvalid.java.messages
new file mode 100644
index 00000000..4f7e3df8
--- /dev/null
+++ b/test/transform/resource/messages-delombok/GetterLazyInvalid.java.messages
@@ -0,0 +1,6 @@
+2:9 ERROR 'lazy' requires the field to be private and final.
+6:9 ERROR 'lazy' requires the field to be private and final.
+10:9 ERROR 'lazy' requires the field to be private and final.
+14:9 WARNING 'lazy' does not work with AccessLevel.NONE.
+17:1 ERROR 'lazy' is not supported for @Getter on a type.
+22:9 ERROR 'lazy' requires field initialization. \ No newline at end of file
diff --git a/test/transform/resource/messages-ecj/GetterLazyInvalid.java.messages b/test/transform/resource/messages-ecj/GetterLazyInvalid.java.messages
new file mode 100644
index 00000000..25641930
--- /dev/null
+++ b/test/transform/resource/messages-ecj/GetterLazyInvalid.java.messages
@@ -0,0 +1,6 @@
+2 error 'lazy' requires the field to be private and final.
+6 error 'lazy' requires the field to be private and final.
+10 error 'lazy' requires the field to be private and final.
+14 warning 'lazy' does not work with AccessLevel.NONE.
+17 error 'lazy' is not supported for @Getter on a type.
+22 error 'lazy' requires field initialization.