aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok/javac
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/lombok/javac')
-rw-r--r--src/core/lombok/javac/JavacNode.java48
-rw-r--r--src/core/lombok/javac/JavacResolution.java59
-rw-r--r--src/core/lombok/javac/apt/EmptyLombokFileObject.java1
-rw-r--r--src/core/lombok/javac/apt/Processor.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java295
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleLog.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleSynchronized.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java13
-rw-r--r--src/core/lombok/javac/handlers/HandleWither.java4
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java152
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java300
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java45
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java45
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java194
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java138
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java120
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java198
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java57
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java193
20 files changed, 1724 insertions, 146 deletions
diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java
index 6eef36eb..727692ac 100644
--- a/src/core/lombok/javac/JavacNode.java
+++ b/src/core/lombok/javac/JavacNode.java
@@ -66,40 +66,40 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
public void traverse(JavacASTVisitor visitor) {
switch (this.getKind()) {
case COMPILATION_UNIT:
- visitor.visitCompilationUnit(this, (JCCompilationUnit)get());
+ visitor.visitCompilationUnit(this, (JCCompilationUnit) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitCompilationUnit(this, (JCCompilationUnit)get());
+ visitor.endVisitCompilationUnit(this, (JCCompilationUnit) get());
break;
case TYPE:
- visitor.visitType(this, (JCClassDecl)get());
+ visitor.visitType(this, (JCClassDecl) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitType(this, (JCClassDecl)get());
+ visitor.endVisitType(this, (JCClassDecl) get());
break;
case FIELD:
- visitor.visitField(this, (JCVariableDecl)get());
+ visitor.visitField(this, (JCVariableDecl) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitField(this, (JCVariableDecl)get());
+ visitor.endVisitField(this, (JCVariableDecl) get());
break;
case METHOD:
- visitor.visitMethod(this, (JCMethodDecl)get());
+ visitor.visitMethod(this, (JCMethodDecl) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitMethod(this, (JCMethodDecl)get());
+ visitor.endVisitMethod(this, (JCMethodDecl) get());
break;
case INITIALIZER:
- visitor.visitInitializer(this, (JCBlock)get());
+ visitor.visitInitializer(this, (JCBlock) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitInitializer(this, (JCBlock)get());
+ visitor.endVisitInitializer(this, (JCBlock) get());
break;
case ARGUMENT:
JCMethodDecl parentMethod = (JCMethodDecl) up().get();
- visitor.visitMethodArgument(this, (JCVariableDecl)get(), parentMethod);
+ visitor.visitMethodArgument(this, (JCVariableDecl) get(), parentMethod);
ast.traverseChildren(visitor, this);
- visitor.endVisitMethodArgument(this, (JCVariableDecl)get(), parentMethod);
+ visitor.endVisitMethodArgument(this, (JCVariableDecl) get(), parentMethod);
break;
case LOCAL:
- visitor.visitLocal(this, (JCVariableDecl)get());
+ visitor.visitLocal(this, (JCVariableDecl) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitLocal(this, (JCVariableDecl)get());
+ visitor.endVisitLocal(this, (JCVariableDecl) get());
break;
case STATEMENT:
visitor.visitStatement(this, get());
@@ -109,21 +109,21 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
case ANNOTATION:
switch (up().getKind()) {
case TYPE:
- visitor.visitAnnotationOnType((JCClassDecl)up().get(), this, (JCAnnotation)get());
+ visitor.visitAnnotationOnType((JCClassDecl) up().get(), this, (JCAnnotation) get());
break;
case FIELD:
- visitor.visitAnnotationOnField((JCVariableDecl)up().get(), this, (JCAnnotation)get());
+ visitor.visitAnnotationOnField((JCVariableDecl) up().get(), this, (JCAnnotation) get());
break;
case METHOD:
- visitor.visitAnnotationOnMethod((JCMethodDecl)up().get(), this, (JCAnnotation)get());
+ visitor.visitAnnotationOnMethod((JCMethodDecl) up().get(), this, (JCAnnotation) get());
break;
case ARGUMENT:
- JCVariableDecl argument = (JCVariableDecl)up().get();
- JCMethodDecl method = (JCMethodDecl)up().up().get();
- visitor.visitAnnotationOnMethodArgument(argument, method, this, (JCAnnotation)get());
+ JCVariableDecl argument = (JCVariableDecl) up().get();
+ JCMethodDecl method = (JCMethodDecl) up().up().get();
+ visitor.visitAnnotationOnMethodArgument(argument, method, this, (JCAnnotation) get());
break;
case LOCAL:
- visitor.visitAnnotationOnLocal((JCVariableDecl)up().get(), this, (JCAnnotation)get());
+ visitor.visitAnnotationOnLocal((JCVariableDecl) up().get(), this, (JCAnnotation) get());
break;
default:
throw new AssertionError("Annotion not expected as child of a " + up().getKind());
@@ -138,9 +138,9 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
@Override public String getName() {
final Name n;
- if (node instanceof JCClassDecl) n = ((JCClassDecl)node).name;
- else if (node instanceof JCMethodDecl) n = ((JCMethodDecl)node).name;
- else if (node instanceof JCVariableDecl) n = ((JCVariableDecl)node).name;
+ if (node instanceof JCClassDecl) n = ((JCClassDecl) node).name;
+ else if (node instanceof JCMethodDecl) n = ((JCMethodDecl) node).name;
+ else if (node instanceof JCVariableDecl) n = ((JCVariableDecl) node).name;
else n = null;
return n == null ? null : n.toString();
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
index 23d7d482..6231735c 100644
--- a/src/core/lombok/javac/JavacResolution.java
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2014 The Project Lombok Authors.
+ * Copyright (C) 2011-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,7 @@ package lombok.javac;
import static lombok.javac.Javac.*;
import static lombok.javac.JavacTreeMaker.TypeTag.typeTag;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
@@ -98,7 +99,7 @@ public class JavacResolution {
@Override public void visitClassDef(JCClassDecl tree) {
if (copyAt != null) return;
- env = enter.getClassEnv(tree.sym);
+ if (tree.sym != null) env = enter.getClassEnv(tree.sym);
}
@Override public void visitMethodDef(JCMethodDecl tree) {
@@ -138,16 +139,64 @@ public class JavacResolution {
EnvFinder finder = new EnvFinder(node.getContext());
while (!stack.isEmpty()) stack.pop().accept(finder);
- TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker());
+ TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker(), node.getContext());
JCTree copy = mirrorMaker.copy(finder.copyAt());
- attrib(copy, finder.get());
+ memberEnterAndAttribute(copy, finder.get(), node.getContext());
return mirrorMaker.getOriginalToCopyMap();
} finally {
messageSuppressor.enableLoggers();
}
}
+ private static Field memberEnterDotEnv;
+
+ private static Field getMemberEnterDotEnv() {
+ if (memberEnterDotEnv != null) return memberEnterDotEnv;
+ try {
+ Field f = MemberEnter.class.getDeclaredField("env");
+ f.setAccessible(true);
+ memberEnterDotEnv = f;
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+
+ return memberEnterDotEnv;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Env<AttrContext> getEnvOfMemberEnter(MemberEnter memberEnter) {
+ Field f = getMemberEnterDotEnv();
+ try {
+ return (Env<AttrContext>) f.get(memberEnter);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private static void setEnvOfMemberEnter(MemberEnter memberEnter, Env<AttrContext> env) {
+ Field f = getMemberEnterDotEnv();
+ try {
+ f.set(memberEnter, env);
+ } catch (Exception e) {
+ return;
+ }
+ }
+
+ private void memberEnterAndAttribute(JCTree copy, Env<AttrContext> env, Context context) {
+ MemberEnter memberEnter = MemberEnter.instance(context);
+ Env<AttrContext> oldEnv = getEnvOfMemberEnter(memberEnter);
+ setEnvOfMemberEnter(memberEnter, env);
+ try {
+ copy.accept(memberEnter);
+ } catch (Exception ignore) {
+ // intentionally ignored; usually even if this step fails, val will work (but not for val in method local inner classes and anonymous inner classes).
+ } finally {
+ setEnvOfMemberEnter(memberEnter, oldEnv);
+ }
+ attrib(copy, env);
+ }
+
public void resolveClassMember(JavacNode node) {
ArrayDeque<JCTree> stack = new ArrayDeque<JCTree>();
@@ -241,7 +290,7 @@ public class JavacResolution {
Type type0 = type;
while (type0 instanceof ArrayType) {
dims++;
- type0 = ((ArrayType)type0).elemtype;
+ type0 = ((ArrayType) type0).elemtype;
}
JCExpression result = typeToJCTree0(type0, ast, allowCompound, allowVoid);
diff --git a/src/core/lombok/javac/apt/EmptyLombokFileObject.java b/src/core/lombok/javac/apt/EmptyLombokFileObject.java
index 7298e920..5a3a7def 100644
--- a/src/core/lombok/javac/apt/EmptyLombokFileObject.java
+++ b/src/core/lombok/javac/apt/EmptyLombokFileObject.java
@@ -19,7 +19,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package lombok.javac.apt;
import java.io.ByteArrayInputStream;
diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java
index ce4d75ff..9979a066 100644
--- a/src/core/lombok/javac/apt/Processor.java
+++ b/src/core/lombok/javac/apt/Processor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index 1885b8b4..4f7f79d9 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,8 +21,8 @@
*/
package lombok.javac.handlers;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
-import java.util.Collections;
import org.mangosdk.spi.ProviderFor;
@@ -34,6 +34,8 @@ import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCIf;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
@@ -46,16 +48,21 @@ import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import lombok.AccessLevel;
+import lombok.Builder;
import lombok.ConfigurationKeys;
+import lombok.Singular;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
-import lombok.experimental.Builder;
+import lombok.core.handlers.HandlerUtil;
import lombok.experimental.NonFinal;
+import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import static lombok.javac.Javac.*;
@@ -64,10 +71,27 @@ import static lombok.javac.JavacTreeMaker.TypeTag.*;
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends JavacAnnotationHandler<Builder> {
- @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
+ private static final boolean toBoolean(Object expr, boolean defaultValue) {
+ if (expr == null) return defaultValue;
+ if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0;
+ return ((Boolean) expr).booleanValue();
+ }
+
+ private static class BuilderFieldData {
+ JCExpression type;
+ Name name;
+ SingularData singularData;
+ java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
+ }
+
+ @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
Builder builderInstance = annotation.getInstance();
+
+ // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them.
+ boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true);
+ boolean chain = toBoolean(annotation.getActualExpression("chain"), true);
+
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
@@ -82,20 +106,21 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
}
- deleteAnnotationIfNeccessary(annotationNode, Builder.class);
- deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder");
+ @SuppressWarnings("deprecation")
+ Class<? extends Annotation> oldExperimentalBuilder = lombok.experimental.Builder.class;
+ deleteAnnotationIfNeccessary(annotationNode, Builder.class, oldExperimentalBuilder);
JavacNode parent = annotationNode.up();
- java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>();
- java.util.List<Name> namesOfParameters = new ArrayList<Name>();
+ java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
JCExpression returnType;
List<JCTypeParameter> typeParams = List.nil();
List<JCExpression> thrownExceptions = List.nil();
Name nameOfStaticBuilderMethod;
JavacNode tdParent;
- JCMethodDecl fillParametersFrom = parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null;
+ JavacNode fillParametersFrom = parent.get() instanceof JCMethodDecl ? parent : null;
+ boolean addCleaning = false;
if (parent.get() instanceof JCClassDecl) {
tdParent = parent;
@@ -109,12 +134,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
// non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
// Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
- namesOfParameters.add(removePrefixFromField(fieldNode));
- typesOfParameters.add(fd.vartype);
+ BuilderFieldData bfd = new BuilderFieldData();
+ bfd.name = removePrefixFromField(fieldNode);
+ bfd.type = fd.vartype;
+ bfd.singularData = getSingularData(fieldNode);
+ builderFields.add(bfd);
allFields.append(fieldNode);
}
-
new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode);
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
@@ -123,28 +150,31 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
- if (!fillParametersFrom.typarams.isEmpty()) {
+ JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
+ if (!jmd.typarams.isEmpty()) {
annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
return;
}
+
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
- thrownExceptions = fillParametersFrom.thrown;
+ thrownExceptions = jmd.thrown;
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null) {
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
- if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) {
+ JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
+ if ((jmd.mods.flags & Flags.STATIC) == 0) {
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
return;
}
- returnType = fillParametersFrom.restype;
- typeParams = fillParametersFrom.typarams;
- thrownExceptions = fillParametersFrom.thrown;
- nameOfStaticBuilderMethod = fillParametersFrom.name;
+ returnType = jmd.restype;
+ typeParams = jmd.typarams;
+ thrownExceptions = jmd.thrown;
+ nameOfStaticBuilderMethod = jmd.name;
if (builderClassName.isEmpty()) {
if (returnType instanceof JCTypeApply) {
returnType = ((JCTypeApply) returnType).clazz;
@@ -179,9 +209,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (fillParametersFrom != null) {
- for (JCVariableDecl param : fillParametersFrom.params) {
- namesOfParameters.add(param.name);
- typesOfParameters.add(param.vartype);
+ for (JavacNode param : fillParametersFrom.down()) {
+ if (param.getKind() != Kind.ARGUMENT) continue;
+ BuilderFieldData bfd = new BuilderFieldData();
+ JCVariableDecl raw = (JCVariableDecl) param.get();
+ bfd.name = raw.name;
+ bfd.type = raw.vartype;
+ bfd.singularData = getSingularData(param);
+ builderFields.add(bfd);
}
}
@@ -190,12 +225,34 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
} else {
sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
+ /* generate errors for @Singular BFDs that have one already defined node. */ {
+ for (BuilderFieldData bfd : builderFields) {
+ SingularData sd = bfd.singularData;
+ if (sd == null) continue;
+ JavacSingularizer singularizer = sd.getSingularizer();
+ if (singularizer == null) continue;
+ if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) {
+ bfd.singularData = null;
+ }
+ }
+ }
+
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ if (bfd.singularData.getSingularizer().requiresCleaning()) {
+ addCleaning = true;
+ break;
+ }
+ }
}
- java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
- java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>();
- for (JavacNode fieldNode : fieldNodes) {
- JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, annotationNode, builderInstance.fluent(), builderInstance.chain());
- if (newMethod != null) newMethods.add(newMethod);
+
+ generateBuilderFields(builderType, builderFields, ast);
+ if (addCleaning) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null);
+ injectField(builderType, uncleanField);
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
@@ -203,38 +260,93 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (cd != null) injectMethod(builderType, cd);
}
- for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod);
+ for (BuilderFieldData bfd : builderFields) {
+ makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain);
+ }
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions);
+ JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
if (md != null) injectMethod(builderType, md);
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ java.util.List<JavacNode> fieldNodes = new ArrayList<JavacNode>();
+ for (BuilderFieldData bfd : builderFields) {
+ fieldNodes.addAll(bfd.createdFields);
+ }
JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
if (md != null) injectMethod(builderType, md);
}
+ if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
+
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
+ recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
if (md != null) injectMethod(tdParent, md);
}
+
+ recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
}
- public JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<Name> fieldNames, JavacNode type, List<JCExpression> thrownExceptions) {
+ private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
+ JavacTreeMaker maker = type.getTreeMaker();
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements);
+ }
+ }
+
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, false))));
+ JCBlock body = maker.Block(0, statements.toList());
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(maker, CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ /*
+ * if (shouldReturnThis) {
+ methodType = cloneSelfType(field);
+ }
+
+ if (methodType == null) {
+ //WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6.
+ methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID));
+ shouldReturnThis = false;
+ }
+
+ */
+ }
+
+ private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) {
JavacTreeMaker maker = type.getTreeMaker();
JCExpression call;
- JCStatement statement;
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ if (addCleaning) {
+ JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")));
+ JCStatement invokeClean = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName("$lombokClean")), List.<JCExpression>nil()));
+ JCIf ifUnclean = maker.If(notClean, invokeClean, null);
+ statements.append(ifUnclean);
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name);
+ }
+ }
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
- for (Name n : fieldNames) {
- args.append(maker.Ident(n));
+ for (BuilderFieldData bfd : builderFields) {
+ args.append(maker.Ident(bfd.name));
+ }
+
+ if (addCleaning) {
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true))));
}
if (staticName == null) {
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
- statement = maker.Return(call);
+ statements.append(maker.Return(call));
} else {
ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>();
for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
@@ -244,13 +356,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
call = maker.Apply(typeParams.toList(), fn, args.toList());
if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) {
- statement = maker.Exec(call);
+ statements.append(maker.Exec(call));
} else {
- statement = maker.Return(call);
+ statements.append(maker.Return(call));
}
}
- JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ JCBlock body = maker.Block(0, statements.toList());
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
@@ -270,50 +382,56 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
- public java.util.List<JavacNode> addFieldsToBuilder(JavacNode builderType, java.util.List<Name> namesOfParameters, java.util.List<JCExpression> typesOfParameters, JCTree source) {
- int len = namesOfParameters.size();
+ public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
+ int len = builderFields.size();
java.util.List<JavacNode> existing = new ArrayList<JavacNode>();
for (JavacNode child : builderType.down()) {
if (child.getKind() == Kind.FIELD) existing.add(child);
}
- java.util.List<JavacNode>out = new ArrayList<JavacNode>();
-
top:
for (int i = len - 1; i >= 0; i--) {
- Name name = namesOfParameters.get(i);
- for (JavacNode exists : existing) {
- Name n = ((JCVariableDecl) exists.get()).name;
- if (n.equals(name)) {
- out.add(exists);
- continue top;
+ BuilderFieldData bfd = builderFields.get(i);
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source));
+ } else {
+ for (JavacNode exists : existing) {
+ Name n = ((JCVariableDecl) exists.get()).name;
+ if (n.equals(bfd.name)) {
+ bfd.createdFields.add(exists);
+ continue top;
+ }
}
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
+ JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null);
+ bfd.createdFields.add(injectField(builderType, newField));
}
- JavacTreeMaker maker = builderType.getTreeMaker();
- JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
- JCVariableDecl newField = maker.VarDef(mods, name, cloneType(maker, typesOfParameters.get(i), source, builderType.getContext()), null);
- out.add(injectField(builderType, newField));
}
-
- Collections.reverse(out);
- return out;
}
+ public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain) {
+ if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
+ makeSimpleSetterMethodForBuilder(builderType, fieldNode.createdFields.get(0), source, fluent, chain);
+ } else {
+ fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, builderType, source.get(), fluent, chain);
+ }
+ }
- public JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) {
+ private void makeSimpleSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
for (JavacNode child : builderType.down()) {
if (child.getKind() != Kind.METHOD) continue;
Name existingName = ((JCMethodDecl) child.get()).name;
- if (existingName.equals(fieldName)) return null;
+ if (existingName.equals(fieldName)) return;
}
- boolean isBoolean = isBoolean(fieldNode);
- String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean);
+ String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
- JavacTreeMaker maker = builderType.getTreeMaker();
- return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ JavacTreeMaker maker = fieldNode.getTreeMaker();
+ JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ injectMethod(builderType, newMethod);
}
public JavacNode findInnerClass(JavacNode parent, String name) {
@@ -331,4 +449,59 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
return injectType(tdParent, builder);
}
+
+ /**
+ * Returns the explicitly requested singular annotation on this node (field
+ * or parameter), or null if there's no {@code @Singular} annotation on it.
+ *
+ * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation.
+ */
+ private SingularData getSingularData(JavacNode node) {
+ for (JavacNode child : node.down()) {
+ if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Singular.class, child)) {
+ Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
+ AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
+ deleteAnnotationIfNeccessary(child, Singular.class);
+ String explicitSingular = ann.getInstance().value();
+ if (explicitSingular.isEmpty()) {
+ if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
+ node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ explicitSingular = pluralName.toString();
+ } else {
+ explicitSingular = autoSingularize(node.getName());
+ if (explicitSingular == null) {
+ node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
+ explicitSingular = pluralName.toString();
+ }
+ }
+ }
+ Name singularName = node.toName(explicitSingular);
+
+ JCExpression type = null;
+ if (node.get() instanceof JCVariableDecl) {
+ type = ((JCVariableDecl) node.get()).vartype;
+ }
+
+ String name = null;
+ List<JCExpression> typeArgs = List.nil();
+ if (type instanceof JCTypeApply) {
+ typeArgs = ((JCTypeApply) type).arguments;
+ type = ((JCTypeApply) type).clazz;
+ }
+
+ name = type.toString();
+
+ String targetFqn = JavacSingularsRecipes.get().toQualified(name);
+ JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java
index 6043d1cb..c5b309c2 100644
--- a/src/core/lombok/javac/handlers/HandleConstructor.java
+++ b/src/core/lombok/javac/handlers/HandleConstructor.java
@@ -25,13 +25,13 @@ import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.ConfigurationKeys;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
import lombok.delombok.LombokOptionsFactory;
-import lombok.experimental.Builder;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java
index ee7268f7..06b7c7ef 100644
--- a/src/core/lombok/javac/handlers/HandleLog.java
+++ b/src/core/lombok/javac/handlers/HandleLog.java
@@ -105,7 +105,7 @@ public class HandleLog {
maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (useStatic ? Flags.STATIC : 0)),
typeNode.toName(logFieldName), loggerType, factoryMethodCall), source, typeNode.getContext());
- injectFieldSuppressWarnings(typeNode, fieldDecl);
+ injectFieldAndMarkGenerated(typeNode, fieldDecl);
return true;
}
diff --git a/src/core/lombok/javac/handlers/HandleSynchronized.java b/src/core/lombok/javac/handlers/HandleSynchronized.java
index fb6678e6..20e85d7e 100644
--- a/src/core/lombok/javac/handlers/HandleSynchronized.java
+++ b/src/core/lombok/javac/handlers/HandleSynchronized.java
@@ -99,7 +99,7 @@ public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> {
JCVariableDecl fieldDecl = recursiveSetGeneratedBy(maker.VarDef(
maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (isStatic ? Flags.STATIC : 0)),
methodNode.toName(lockName), objectType, newObjectArray), ast, context);
- injectFieldSuppressWarnings(methodNode.up(), fieldDecl);
+ injectFieldAndMarkGenerated(methodNode.up(), fieldDecl);
}
if (method.body == null) return;
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
index 0230e1b0..9eadd750 100644
--- a/src/core/lombok/javac/handlers/HandleVal.java
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Project Lombok Authors.
+ * Copyright (C) 2010-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,6 +35,7 @@ import lombok.javac.ResolutionResetNeeded;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
@@ -110,6 +111,16 @@ public class HandleVal extends JavacASTAdapter {
}
} else {
type = local.init.type;
+ if (type.isErroneous()) {
+ try {
+ JavacResolution resolver = new JavacResolution(localNode.getContext());
+ local.type = Symtab.instance(localNode.getContext()).unknownType;
+ type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type;
+ } catch (RuntimeException e) {
+ System.err.println("Exception while resolving: " + localNode);
+ throw e;
+ }
+ }
}
} else {
if (rhsOfEnhancedForLoop.type == null) {
diff --git a/src/core/lombok/javac/handlers/HandleWither.java b/src/core/lombok/javac/handlers/HandleWither.java
index 8a7b49b2..5de18686 100644
--- a/src/core/lombok/javac/handlers/HandleWither.java
+++ b/src/core/lombok/javac/handlers/HandleWither.java
@@ -129,8 +129,8 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
if (level == AccessLevel.NONE || node == null) return;
- List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod=", annotationNode);
- List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam=", annotationNode);
+ List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod=", annotationNode);
+ List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam=", annotationNode);
switch (node.getKind()) {
case FIELD:
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 6413e8ef..a073ac0d 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -41,6 +41,7 @@ import lombok.Data;
import lombok.Getter;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
+import lombok.core.LombokImmutableList;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.TypeResolver;
import lombok.core.configuration.NullCheckExceptionType;
@@ -68,7 +69,6 @@ import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
-import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
@@ -242,51 +242,45 @@ public class JavacHandlerUtil {
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
JCAnnotation anno = (JCAnnotation) node.get();
List<JCExpression> arguments = anno.getArguments();
- for (Method m : type.getDeclaredMethods()) {
- if (!Modifier.isPublic(m.getModifiers())) continue;
- String name = m.getName();
+
+ for (JCExpression arg : arguments) {
+ String mName;
+ JCExpression rhs;
java.util.List<String> raws = new ArrayList<String>();
java.util.List<Object> guesses = new ArrayList<Object>();
java.util.List<Object> expressions = new ArrayList<Object>();
final java.util.List<DiagnosticPosition> positions = new ArrayList<DiagnosticPosition>();
- boolean isExplicit = false;
- for (JCExpression arg : arguments) {
- String mName;
- JCExpression rhs;
-
- if (arg instanceof JCAssign) {
- JCAssign assign = (JCAssign) arg;
- mName = assign.lhs.toString();
- rhs = assign.rhs;
- } else {
- rhs = arg;
- mName = "value";
- }
-
- if (!mName.equals(name)) continue;
- isExplicit = true;
- if (rhs instanceof JCNewArray) {
- List<JCExpression> elems = ((JCNewArray)rhs).elems;
- for (JCExpression inner : elems) {
- raws.add(inner.toString());
- expressions.add(inner);
- guesses.add(calculateGuess(inner));
- positions.add(inner.pos());
- }
- } else {
- raws.add(rhs.toString());
- expressions.add(rhs);
- guesses.add(calculateGuess(rhs));
- positions.add(rhs.pos());
+ if (arg instanceof JCAssign) {
+ JCAssign assign = (JCAssign) arg;
+ mName = assign.lhs.toString();
+ rhs = assign.rhs;
+ } else {
+ rhs = arg;
+ mName = "value";
+ }
+
+ if (rhs instanceof JCNewArray) {
+ List<JCExpression> elems = ((JCNewArray)rhs).elems;
+ for (JCExpression inner : elems) {
+ raws.add(inner.toString());
+ expressions.add(inner);
+ guesses.add(calculateGuess(inner));
+ positions.add(inner.pos());
}
+ } else {
+ raws.add(rhs.toString());
+ expressions.add(rhs);
+ guesses.add(calculateGuess(rhs));
+ positions.add(rhs.pos());
}
- values.put(name, new AnnotationValue(node, raws, expressions, guesses, isExplicit) {
+ values.put(mName, new AnnotationValue(node, raws, expressions, guesses, true) {
@Override public void setError(String message, int valueIdx) {
if (valueIdx < 0) node.addError(message);
else node.addError(message, positions.get(valueIdx));
}
+
@Override public void setWarning(String message, int valueIdx) {
if (valueIdx < 0) node.addWarning(message);
else node.addWarning(message, positions.get(valueIdx));
@@ -294,6 +288,21 @@ public class JavacHandlerUtil {
});
}
+ for (Method m : type.getDeclaredMethods()) {
+ if (!Modifier.isPublic(m.getModifiers())) continue;
+ String name = m.getName();
+ if (!values.containsKey(name)) {
+ values.put(name, new AnnotationValue(node, new ArrayList<String>(), new ArrayList<Object>(), new ArrayList<Object>(), false) {
+ @Override public void setError(String message, int valueIdx) {
+ node.addError(message);
+ }
+ @Override public void setWarning(String message, int valueIdx) {
+ node.addWarning(message);
+ }
+ });
+ }
+ }
+
return new AnnotationValues<A>(type, values, node);
}
@@ -445,9 +454,9 @@ public class JavacHandlerUtil {
return HandlerUtil.shouldReturnThis0(accessors, field.getAst());
}
- public static JCExpression cloneSelfType(JavacNode field) {
- JavacNode typeNode = field;
- JavacTreeMaker maker = field.getTreeMaker();
+ public static JCExpression cloneSelfType(JavacNode childOfType) {
+ JavacNode typeNode = childOfType;
+ JavacTreeMaker maker = childOfType.getTreeMaker();
while (typeNode != null && typeNode.getKind() != Kind.TYPE) typeNode = typeNode.up();
if (typeNode != null && typeNode.get() instanceof JCClassDecl) {
JCClassDecl type = (JCClassDecl) typeNode.get();
@@ -808,7 +817,7 @@ public class JavacHandlerUtil {
* The field carries the &#64;{@link SuppressWarnings}("all") annotation.
* Also takes care of updating the JavacAST.
*/
- public static void injectFieldSuppressWarnings(JavacNode typeNode, JCVariableDecl field) {
+ public static void injectFieldAndMarkGenerated(JavacNode typeNode, JCVariableDecl field) {
injectField(typeNode, field, true);
}
@@ -821,10 +830,13 @@ public class JavacHandlerUtil {
return injectField(typeNode, field, false);
}
- private static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addSuppressWarnings) {
+ private static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addGenerated) {
JCClassDecl type = (JCClassDecl) typeNode.get();
- if (addSuppressWarnings) addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext());
+ if (addGenerated) {
+ addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext());
+ addGenerated(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext());
+ }
List<JCTree> insertAfter = null;
List<JCTree> insertBefore = type.defs;
@@ -883,6 +895,7 @@ public class JavacHandlerUtil {
}
addSuppressWarningsAll(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext());
+ addGenerated(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext());
type.defs = type.defs.append(method);
typeNode.add(method, Kind.METHOD);
@@ -898,6 +911,7 @@ public class JavacHandlerUtil {
public static JavacNode injectType(JavacNode typeNode, final JCClassDecl type) {
JCClassDecl typeDecl = (JCClassDecl) typeNode.get();
addSuppressWarningsAll(type.mods, typeNode, type.pos, getGeneratedBy(type), typeNode.getContext());
+ addGenerated(type.mods, typeNode, type.pos, getGeneratedBy(type), typeNode.getContext());
typeDecl.defs = typeDecl.defs.append(type);
return typeNode.add(type, Kind.TYPE);
}
@@ -938,20 +952,52 @@ public class JavacHandlerUtil {
public static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) {
if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateSuppressWarnings()) return;
+ addAnnotation(mods, node, pos, source, context, "java.lang.SuppressWarnings", node.getTreeMaker().Literal("all"));
+
+ if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_FINDBUGS_SUPPRESSWARNINGS_ANNOTATIONS))) {
+ JavacTreeMaker maker = node.getTreeMaker();
+ JCExpression arg = maker.Assign(maker.Ident(node.toName("justification")), maker.Literal("generated code"));
+ addAnnotation(mods, node, pos, source, context, "edu.umd.cs.findbugs.annotations.SuppressFBWarnings", arg);
+ }
+ }
+
+ public static void addGenerated(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) {
+ if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateGenerated()) return;
+
+ if (!Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_GENERATED_ANNOTATIONS))) {
+ addAnnotation(mods, node, pos, source, context, "javax.annotation.Generated", node.getTreeMaker().Literal("lombok"));
+ }
+ }
+
+ private static void addAnnotation(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context, String annotationTypeFqn, JCExpression arg) {
+ boolean isJavaLangBased;
+ String simpleName; {
+ int idx = annotationTypeFqn.lastIndexOf('.');
+ simpleName = idx == -1 ? annotationTypeFqn : annotationTypeFqn.substring(idx + 1);
+
+ isJavaLangBased = idx == 9 && annotationTypeFqn.regionMatches(0, "java.lang.", 0, 10);
+ }
+
for (JCAnnotation ann : mods.annotations) {
JCTree annType = ann.getAnnotationType();
Name lastPart = null;
if (annType instanceof JCIdent) lastPart = ((JCIdent) annType).name;
else if (annType instanceof JCFieldAccess) lastPart = ((JCFieldAccess) annType).name;
- if (lastPart != null && lastPart.contentEquals("SuppressWarnings")) return;
+ if (lastPart != null && lastPart.contentEquals(simpleName)) return;
}
JavacTreeMaker maker = node.getTreeMaker();
- JCExpression suppressWarningsType = genJavaLangTypeRef(node, "SuppressWarnings");
- JCLiteral allLiteral = maker.Literal("all");
- suppressWarningsType.pos = pos;
- allLiteral.pos = pos;
- JCAnnotation annotation = recursiveSetGeneratedBy(maker.Annotation(suppressWarningsType, List.<JCExpression>of(allLiteral)), source, context);
+ JCExpression annType = isJavaLangBased ? genJavaLangTypeRef(node, simpleName) : chainDotsString(node, annotationTypeFqn);
+ annType.pos = pos;
+ if (arg != null) {
+ arg.pos = pos;
+ if (arg instanceof JCAssign) {
+ ((JCAssign) arg).lhs.pos = pos;
+ ((JCAssign) arg).rhs.pos = pos;
+ }
+ }
+ List<JCExpression> argList = arg != null ? List.of(arg) : List.<JCExpression>nil();
+ JCAnnotation annotation = recursiveSetGeneratedBy(maker.Annotation(annType, argList), source, context);
annotation.pos = pos;
mods.annotations = mods.annotations.append(annotation);
}
@@ -985,6 +1031,17 @@ public class JavacHandlerUtil {
return chainDots(node, -1, null, null, elems);
}
+ public static JCExpression chainDots(JavacNode node, LombokImmutableList<String> elems) {
+ assert elems != null;
+
+ JavacTreeMaker maker = node.getTreeMaker();
+ JCExpression e = null;
+ for (String elem : elems) {
+ if (e == null) e = maker.Ident(node.toName(elem));
+ else e = maker.Select(e, node.toName(elem));
+ }
+ return e;
+ }
/**
* In javac, dotted access of any kind, from {@code java.lang.String} to {@code var.methodName}
* is represented by a fold-left of {@code Select} nodes with the leftmost string represented by
@@ -1013,7 +1070,6 @@ public class JavacHandlerUtil {
return e;
}
-
/**
* In javac, dotted access of any kind, from {@code java.lang.String} to {@code var.methodName}
diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
new file mode 100644
index 00000000..7fca01ae
--- /dev/null
+++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.SpiLoadUtil;
+import lombok.core.TypeLibrary;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+
+import com.sun.source.tree.Tree.Kind;
+import com.sun.tools.javac.code.BoundKind;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.JCWildcard;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+public class JavacSingularsRecipes {
+ private static final JavacSingularsRecipes INSTANCE = new JavacSingularsRecipes();
+ private final Map<String, JavacSingularizer> singularizers = new HashMap<String, JavacSingularizer>();
+ private final TypeLibrary singularizableTypes = new TypeLibrary();
+
+ private JavacSingularsRecipes() {
+ try {
+ loadAll(singularizableTypes, singularizers);
+ singularizableTypes.lock();
+ } catch (IOException e) {
+ System.err.println("Lombok's @Singularizable feature is broken due to misconfigured SPI files: " + e);
+ }
+ }
+
+ private static void loadAll(TypeLibrary library, Map<String, JavacSingularizer> map) throws IOException {
+ for (JavacSingularizer handler : SpiLoadUtil.findServices(JavacSingularizer.class, JavacSingularizer.class.getClassLoader())) {
+ for (String type : handler.getSupportedTypes()) {
+ JavacSingularizer existingSingularizer = map.get(type);
+ if (existingSingularizer != null) {
+ JavacSingularizer toKeep = existingSingularizer.getClass().getName().compareTo(handler.getClass().getName()) > 0 ? handler : existingSingularizer;
+ System.err.println("Multiple singularizers found for type " + type + "; the alphabetically first class is used: " + toKeep.getClass().getName());
+ map.put(type, toKeep);
+ } else {
+ map.put(type, handler);
+ library.addType(type);
+ }
+ }
+ }
+ }
+
+ public static JavacSingularsRecipes get() {
+ return INSTANCE;
+ }
+
+ public String toQualified(String typeReference) {
+ return singularizableTypes.toQualified(typeReference);
+ }
+
+ public JavacSingularizer getSingularizer(String fqn) {
+ return singularizers.get(fqn);
+ }
+
+ public static final class SingularData {
+ private final JavacNode annotation;
+ private final Name singularName;
+ private final Name pluralName;
+ private final List<JCExpression> typeArgs;
+ private final String targetFqn;
+ private final JavacSingularizer singularizer;
+
+ public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> typeArgs, String targetFqn, JavacSingularizer singularizer) {
+ this.annotation = annotation;
+ this.singularName = singularName;
+ this.pluralName = pluralName;
+ this.typeArgs = typeArgs;
+ this.targetFqn = targetFqn;
+ this.singularizer = singularizer;
+ }
+
+ public JavacNode getAnnotation() {
+ return annotation;
+ }
+
+ public Name getSingularName() {
+ return singularName;
+ }
+
+ public Name getPluralName() {
+ return pluralName;
+ }
+
+ public List<JCExpression> getTypeArgs() {
+ return typeArgs;
+ }
+
+ public String getTargetFqn() {
+ return targetFqn;
+ }
+
+ public JavacSingularizer getSingularizer() {
+ return singularizer;
+ }
+
+ public String getTargetSimpleType() {
+ int idx = targetFqn.lastIndexOf(".");
+ return idx == -1 ? targetFqn : targetFqn.substring(idx + 1);
+ }
+ }
+
+ public static abstract class JavacSingularizer {
+ public abstract LombokImmutableList<String> getSupportedTypes();
+
+ /** Checks if any of the to-be-generated nodes (fields, methods) already exist. If so, errors on these (singulars don't support manually writing some of it, and returns true). */
+ public boolean checkForAlreadyExistingNodesAndGenerateError(JavacNode builderType, SingularData data) {
+ for (JavacNode child : builderType.down()) {
+ switch (child.getKind()) {
+ case FIELD: {
+ JCVariableDecl field = (JCVariableDecl) child.get();
+ Name name = field.name;
+ if (name == null) break;
+ if (getGeneratedBy(field) != null) continue;
+ for (Name fieldToBeGenerated : listFieldsToBeGenerated(data, builderType)) {
+ if (!fieldToBeGenerated.equals(name)) continue;
+ child.addError("Manually adding a field that @Singular @Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use @Singular.");
+ return true;
+ }
+ break;
+ }
+ case METHOD: {
+ JCMethodDecl method = (JCMethodDecl) child.get();
+ Name name = method.name;
+ if (name == null) break;
+ if (getGeneratedBy(method) != null) continue;
+ for (Name methodToBeGenerated : listMethodsToBeGenerated(data, builderType)) {
+ if (!methodToBeGenerated.equals(name)) continue;
+ child.addError("Manually adding a method that @Singular @Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use @Singular.");
+ return true;
+ }
+ break;
+ }}
+ }
+
+ return false;
+ }
+
+ public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ return Collections.singletonList(data.pluralName);
+ }
+
+ public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ Name p = data.pluralName;
+ Name s = data.singularName;
+ if (p.equals(s)) return Collections.singletonList(p);
+ return Arrays.asList(p, s);
+ }
+
+ public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source);
+ public abstract void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain);
+ public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName);
+
+ public boolean requiresCleaning() {
+ try {
+ return !getClass().getMethod("appendCleaningCode", SingularData.class, JavacNode.class, JCTree.class, ListBuffer.class).getDeclaringClass().equals(JavacSingularizer.class);
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ public void appendCleaningCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements) {
+ }
+
+ // -- Utility methods --
+
+ /**
+ * Adds the requested number of type arguments to the provided type, copying each argument in {@code typeArgs}. If typeArgs is too long, the extra elements are ignored.
+ * If {@code typeArgs} is null or too short, {@code java.lang.Object} will be substituted for each missing type argument.
+ *
+ * @param count The number of type arguments requested.
+ * @param addExtends If {@code true}, all bounds are either '? extends X' or just '?'. If false, the reverse is applied, and '? extends Foo' is converted to Foo, '?' to Object, etc.
+ * @param node Some node in the same AST. Just used to obtain makers and contexts and such.
+ * @param type The type to add generics to.
+ * @param typeArgs the list of type args to clone.
+ * @param source The source annotation that is the root cause of this code generation.
+ */
+ protected JCExpression addTypeArgs(int count, boolean addExtends, JavacNode node, JCExpression type, List<JCExpression> typeArgs, JCTree source) {
+ JavacTreeMaker maker = node.getTreeMaker();
+ List<JCExpression> clonedAndFixedTypeArgs = createTypeArgs(count, addExtends, node, typeArgs, source);
+
+ return maker.TypeApply(type, clonedAndFixedTypeArgs);
+ }
+
+ protected List<JCExpression> createTypeArgs(int count, boolean addExtends, JavacNode node, List<JCExpression> typeArgs, JCTree source) {
+ JavacTreeMaker maker = node.getTreeMaker();
+ Context context = node.getContext();
+
+ if (count < 0) throw new IllegalArgumentException("count is negative");
+ if (count == 0) return List.nil();
+ ListBuffer<JCExpression> arguments = new ListBuffer<JCExpression>();
+
+ if (typeArgs != null) for (JCExpression orig : typeArgs) {
+ if (!addExtends) {
+ if (orig.getKind() == Kind.UNBOUNDED_WILDCARD || orig.getKind() == Kind.SUPER_WILDCARD) {
+ arguments.append(genJavaLangTypeRef(node, "Object"));
+ } else if (orig.getKind() == Kind.EXTENDS_WILDCARD) {
+ JCExpression inner;
+ try {
+ inner = (JCExpression) ((JCWildcard) orig).inner;
+ } catch (Exception e) {
+ inner = genJavaLangTypeRef(node, "Object");
+ }
+ arguments.append(cloneType(maker, inner, source, context));
+ } else {
+ arguments.append(cloneType(maker, orig, source, context));
+ }
+ } else {
+ if (orig.getKind() == Kind.UNBOUNDED_WILDCARD || orig.getKind() == Kind.SUPER_WILDCARD) {
+ arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
+ } else if (orig.getKind() == Kind.EXTENDS_WILDCARD) {
+ arguments.append(cloneType(maker, orig, source, context));
+ } else {
+ arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), cloneType(maker, orig, source, context)));
+ }
+ }
+ if (--count == 0) break;
+ }
+
+ while (count-- > 0) {
+ if (addExtends) {
+ arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
+ } else {
+ arguments.append(genJavaLangTypeRef(node, "Object"));
+ }
+ }
+
+ return arguments.toList();
+ }
+
+ /** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */
+ protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard) {
+ Name thisName = builderType.toName("this");
+ JCExpression fn = maker.Select(maker.Select(maker.Ident(thisName), name), builderType.toName("size"));
+ JCExpression sizeInvoke = maker.Apply(List.<JCExpression>nil(), fn, List.<JCExpression>nil());
+ if (nullGuard) {
+ JCExpression isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(thisName), name), maker.Literal(CTC_BOT, 0));
+ return maker.Conditional(isNull, maker.Literal(CTC_INT, 0), sizeInvoke);
+ }
+ return sizeInvoke;
+ }
+
+ protected JCExpression cloneParamType(int index, JavacTreeMaker maker, List<JCExpression> typeArgs, JavacNode builderType, JCTree source) {
+ if (typeArgs == null || typeArgs.size() <= index) {
+ return genJavaLangTypeRef(builderType, "Object");
+ } else {
+ JCExpression originalType = typeArgs.get(index);
+ if (originalType.getKind() == Kind.UNBOUNDED_WILDCARD || originalType.getKind() == Kind.SUPER_WILDCARD) {
+ return genJavaLangTypeRef(builderType, "Object");
+ } else if (originalType.getKind() == Kind.EXTENDS_WILDCARD) {
+ try {
+ return cloneType(maker, (JCExpression) ((JCWildcard) originalType).inner, source, builderType.getContext());
+ } catch (Exception e) {
+ return genJavaLangTypeRef(builderType, "Object");
+ }
+ } else {
+ return cloneType(maker, originalType, source, builderType.getContext());
+ }
+ }
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java
new file mode 100644
index 00000000..0700e2e5
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacGuavaMapSingularizer extends JavacGuavaSingularizer {
+ // TODO cgcc.ImmutableMultimap, cgcc.ImmutableListMultimap, cgcc.ImmutableSetMultimap
+ // TODO cgcc.ImmutableClassToInstanceMap
+ // TODO cgcc.ImmutableRangeMap
+
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of(
+ "com.google.common.collect.ImmutableMap",
+ "com.google.common.collect.ImmutableBiMap",
+ "com.google.common.collect.ImmutableSortedMap");
+ }
+
+ @Override protected boolean isMap() {
+ return true;
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java
new file mode 100644
index 00000000..2e404ca8
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacGuavaSetListSingularizer extends JavacGuavaSingularizer {
+ // TODO com.google.common.collect.ImmutableTable
+ // TODO com.google.common.collect.ImmutableRangeSet
+ // TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of(
+ "com.google.common.collect.ImmutableCollection",
+ "com.google.common.collect.ImmutableList",
+ "com.google.common.collect.ImmutableSet",
+ "com.google.common.collect.ImmutableSortedSet");
+ }
+
+ @Override protected boolean isMap() {
+ return false;
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
new file mode 100644
index 00000000..e23837dc
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Collections;
+
+import lombok.core.GuavaTypeMap;
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+abstract class JavacGuavaSingularizer extends JavacSingularizer {
+ protected String getSimpleTargetTypeName(SingularData data) {
+ return GuavaTypeMap.getGuavaTypeName(data.getTargetFqn());
+ }
+
+ protected String getBuilderMethodName(SingularData data) {
+ String simpleTypeName = getSimpleTargetTypeName(data);
+ if ("ImmutableSortedSet".equals(simpleTypeName) || "ImmutableSortedMap".equals(simpleTypeName)) return "naturalOrder";
+ return "builder";
+ }
+
+ protected abstract boolean isMap();
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), "Builder");
+ type = addTypeArgs(isMap() ? 2 : 1, false, builderType, type, data.getTypeArgs(), source);
+
+ JCVariableDecl buildField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), data.getPluralName(), type, null);
+ return Collections.singletonList(injectField(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+ }
+
+ void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ boolean mapMode = isMap();
+
+ Name keyName = !mapMode ? data.getSingularName() : builderType.toName(data.getSingularName() + "$key");
+ Name valueName = !mapMode ? null : builderType.toName(data.getSingularName() + "$value");
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, mapMode, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), mapMode ? "put" : "add");
+ List<JCExpression> invokeAddExpr;
+ if (mapMode) {
+ invokeAddExpr = List.<JCExpression>of(maker.Ident(keyName), maker.Ident(valueName));
+ } else {
+ invokeAddExpr = List.<JCExpression>of(maker.Ident(keyName));
+ }
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, invokeAddExpr);
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name methodName = data.getSingularName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(mapMode ? "put" : "add", methodName.toString()));
+ List<JCVariableDecl> params;
+ if (mapMode) {
+ JCExpression keyType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ JCExpression valueType = cloneParamType(1, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl paramKey = maker.VarDef(maker.Modifiers(paramFlags), keyName, keyType, null);
+ JCVariableDecl paramValue = maker.VarDef(maker.Modifiers(paramFlags), valueName, valueType, null);
+ params = List.of(paramKey, paramValue);
+ } else {
+ JCExpression paramType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ params = List.of(maker.VarDef(maker.Modifiers(paramFlags), data.getSingularName(), paramType, null));
+ }
+ JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ protected void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ boolean mapMode = isMap();
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, mapMode, source));
+ JCExpression thisDotFieldDotAddAll = chainDots(builderType, "this", data.getPluralName().toString(), mapMode ? "putAll" : "addAll");
+ JCExpression invokeAddAll = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAddAll, List.<JCExpression>of(maker.Ident(data.getPluralName())));
+ statements.append(maker.Exec(invokeAddAll));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name methodName = data.getPluralName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(mapMode ? "putAll" : "addAll", methodName.toString()));
+ JCExpression paramType;
+ if (mapMode) {
+ paramType = chainDots(builderType, "java", "util", "Map");
+ } else {
+ paramType = genJavaLangTypeRef(builderType, "Iterable");
+ }
+ paramType = addTypeArgs(mapMode ? 2 : 1, true, builderType, paramType, data.getTypeArgs(), source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, List.of(param), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ List<JCExpression> jceBlank = List.nil();
+ boolean mapMode = isMap();
+
+ JCExpression varType = chainDotsString(builderType, data.getTargetFqn());
+ varType = addTypeArgs(mapMode ? 2 : 1, false, builderType, varType, data.getTypeArgs(), source);
+
+ JCExpression empty; {
+ //ImmutableX.of()
+ JCExpression emptyMethod = chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), "of");
+ List<JCExpression> invokeTypeArgs = createTypeArgs(mapMode ? 2 : 1, false, builderType, data.getTypeArgs(), source);
+ empty = maker.Apply(invokeTypeArgs, emptyMethod, jceBlank);
+ }
+
+ JCExpression invokeBuild; {
+ //this.pluralName.build();
+ invokeBuild = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "build"), jceBlank);
+ }
+
+ JCExpression isNull; {
+ //this.pluralName == null
+ isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()), maker.Literal(CTC_BOT, null));
+ }
+
+ JCExpression init = maker.Conditional(isNull, empty, invokeBuild); // this.pluralName == null ? ImmutableX.of() : this.pluralName.build()
+
+ JCStatement jcs = maker.VarDef(maker.Modifiers(0), data.getPluralName(), varType, init);
+ statements.append(jcs);
+ }
+
+ protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+
+ JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
+ JCExpression thisDotField2 = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
+ JCExpression cond = maker.Binary(CTC_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+
+ JCExpression create = maker.Apply(jceBlank, chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), getBuilderMethodName(data)), jceBlank);
+ JCStatement thenPart = maker.Exec(maker.Assign(thisDotField2, create));
+
+ return maker.If(cond, thenPart, null);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
new file mode 100644
index 00000000..6f8ff705
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Collections;
+
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularizer {
+ @Override public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ return super.listFieldsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listMethodsToBeGenerated(data, builderType);
+ }
+
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.generateFields(data, builderType, source);
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs(), source);
+
+ JCVariableDecl buildField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), data.getPluralName(), type, null);
+ return Collections.singletonList(injectField(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.generateMethods(data, builderType, source, fluent, chain);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ Name thisName = builderType.toName("this");
+
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
+ generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
+ generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+ }
+
+ void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getSingularName())));
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getSingularName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("add", name.toString()));
+ JCExpression paramType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getSingularName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "addAll");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getPluralName())));
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getPluralName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("addAll", name.toString()));
+ JCExpression paramType = chainDots(builderType, "java", "util", "Collection");
+ paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs(), source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
new file mode 100644
index 00000000..9ec77e78
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable");
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ List<JCExpression> jceBlank = List.nil();
+ ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
+
+ /* case 0: (empty); break; */ {
+ JCStatement assignStat; {
+ // pluralName = java.util.Collections.emptyList();
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "emptyList"), jceBlank);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase emptyCase = maker.Case(maker.Literal(CTC_INT, 0), List.of(assignStat, breakStat));
+ cases.append(emptyCase);
+ }
+
+ /* case 1: (singletonList); break; */ {
+ JCStatement assignStat; {
+ // pluralName = java.util.Collections.singletonList(this.pluralName.get(0));
+ JCExpression zeroLiteral = maker.Literal(CTC_INT, 0);
+ JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "get"), List.of(zeroLiteral));
+ List<JCExpression> args = List.of(arg);
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "singletonList"), args);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase singletonCase = maker.Case(maker.Literal(CTC_INT, 1), List.of(assignStat, breakStat));
+ cases.append(singletonCase);
+ }
+
+ /* default: Create with right size, then addAll */ {
+ List<JCStatement> defStats = createListCopy(maker, data, builderType, source);
+ JCCase defaultCase = maker.Case(null, defStats);
+ cases.append(defaultCase);
+ }
+
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true), cases.toList());
+ JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
+ localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source);
+ JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
+ statements.append(varDefStat);
+ statements.append(switchStat);
+ }
+
+ private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ Name thisName = builderType.toName("this");
+
+ JCExpression argToUnmodifiable; {
+ // new java.util.ArrayList<Generics>(this.pluralName);
+ List<JCExpression> constructorArgs = List.nil();
+ JCExpression thisDotPluralName = maker.Select(maker.Ident(thisName), data.getPluralName());
+ constructorArgs = List.<JCExpression>of(thisDotPluralName);
+ JCExpression targetTypeExpr = chainDots(builderType, "java", "util", "ArrayList");
+ targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs(), source);
+ argToUnmodifiable = maker.NewClass(null, jceBlank, targetTypeExpr, constructorArgs, null);
+ }
+
+ JCStatement unmodifiableStat; {
+ // pluralname = Collections.unmodifiableInterfaceType(-newlist-);
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "unmodifiableList"), List.of(argToUnmodifiable));
+ unmodifiableStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+
+ return List.of(unmodifiableStat);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
new file mode 100644
index 00000000..ed91698d
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Arrays;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Map", "java.util.SortedMap", "java.util.NavigableMap");
+ }
+
+ @Override public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ String p = data.getPluralName().toString();
+ return Arrays.asList(builderType.toName(p + "$key"), builderType.toName(p + "$value"));
+ }
+
+ @Override public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listMethodsToBeGenerated(data, builderType);
+ }
+
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.generateFields(data, builderType, source);
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ JCVariableDecl buildKeyField; {
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs(), source);
+ buildKeyField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName(data.getPluralName() + "$key"), type, null);
+ }
+
+ JCVariableDecl buildValueField; {
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ List<JCExpression> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.size() > 1) tArgs = tArgs.tail;
+ else tArgs = List.nil();
+ type = addTypeArgs(1, false, builderType, type, tArgs, source);
+ buildValueField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName(data.getPluralName() + "$value"), type, null);
+ }
+
+ JavacNode valueFieldNode = injectField(builderType, buildValueField);
+ JavacNode keyFieldNode = injectField(builderType, buildKeyField);
+
+ return Arrays.asList(keyFieldNode, valueFieldNode);
+ }
+
+ @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.generateMethods(data, builderType, source, fluent, chain);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+ }
+
+ private void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source));
+ Name keyName = builderType.toName(data.getSingularName().toString() + "Key");
+ Name valueName = builderType.toName(data.getSingularName().toString() + "Value");
+ /* this.pluralname$key.add(singularnameKey); */ {
+ JCExpression thisDotKeyFieldDotAdd = chainDots(builderType, "this", data.getPluralName() + "$key", "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotKeyFieldDotAdd, List.<JCExpression>of(maker.Ident(keyName)));
+ statements.append(maker.Exec(invokeAdd));
+ }
+ /* this.pluralname$value.add(singularnameValue); */ {
+ JCExpression thisDotValueFieldDotAdd = chainDots(builderType, "this", data.getPluralName() + "$value", "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotValueFieldDotAdd, List.<JCExpression>of(maker.Ident(valueName)));
+ statements.append(maker.Exec(invokeAdd));
+ }
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+
+ Name name = data.getSingularName();
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("put", name.toString()));
+ JCExpression paramTypeKey = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ JCExpression paramTypeValue = cloneParamType(1, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl paramKey = maker.VarDef(maker.Modifiers(paramFlags), keyName, paramTypeKey, null);
+ JCVariableDecl paramValue = maker.VarDef(maker.Modifiers(paramFlags), valueName, paramTypeValue, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(paramKey, paramValue), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ private void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> jceBlank = List.nil();
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source));
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ long baseFlags = JavacHandlerUtil.addFinalIfNeeded(0, builderType.getContext());
+ Name entryName = builderType.toName("$lombokEntry");
+
+ JCExpression forEachType = chainDots(builderType, "java", "util", "Map", "Entry");
+ forEachType = addTypeArgs(2, true, builderType, forEachType, data.getTypeArgs(), source);
+ JCExpression keyArg = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(entryName), builderType.toName("getKey")), List.<JCExpression>nil());
+ JCExpression valueArg = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(entryName), builderType.toName("getValue")), List.<JCExpression>nil());
+ JCExpression addKey = maker.Apply(List.<JCExpression>nil(), chainDots(builderType, "this", data.getPluralName() + "$key", "add"), List.of(keyArg));
+ JCExpression addValue = maker.Apply(List.<JCExpression>nil(), chainDots(builderType, "this", data.getPluralName() + "$value", "add"), List.of(valueArg));
+ JCBlock forEachBody = maker.Block(0, List.<JCStatement>of(maker.Exec(addKey), maker.Exec(addValue)));
+ JCExpression entrySetInvocation = maker.Apply(jceBlank, maker.Select(maker.Ident(data.getPluralName()), builderType.toName("entrySet")), jceBlank);
+ JCStatement forEach = maker.ForeachLoop(maker.VarDef(maker.Modifiers(baseFlags), entryName, forEachType, null), entrySetInvocation, forEachBody);
+ statements.append(forEach);
+
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getPluralName();
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("putAll", name.toString()));
+ JCExpression paramType = chainDots(builderType, "java", "util", "Map");
+ paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs(), source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), jceBlank, body, null);
+ injectMethod(builderType, method);
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ if (data.getTargetFqn().equals("java.util.Map")) {
+ statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source));
+ } else {
+ statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source));
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
new file mode 100644
index 00000000..317233cb
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilSetSingularizer extends JavacJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet");
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ if (data.getTargetFqn().equals("java.util.Set")) {
+ statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source));
+ } else {
+ statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source));
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
new file mode 100644
index 00000000..f419a014
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+import lombok.ConfigurationKeys;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
+ protected final JavacSingularizer guavaListSetSingularizer = new JavacGuavaSetListSingularizer();
+ protected final JavacSingularizer guavaMapSingularizer = new JavacGuavaMapSingularizer();
+
+ protected boolean useGuavaInstead(JavacNode node) {
+ return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA));
+ }
+
+ protected List<JCStatement> createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
+
+ if (emptyCollectionMethod != null) { // case 0: (empty); break;
+ JCStatement assignStat; {
+ // pluralName = java.util.Collections.emptyCollectionMethod();
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", emptyCollectionMethod), jceBlank);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase emptyCase = maker.Case(maker.Literal(CTC_INT, 0), List.of(assignStat, breakStat));
+ cases.append(emptyCase);
+ }
+
+ if (singletonCollectionMethod != null) { // case 1: (singleton); break;
+ JCStatement assignStat; {
+ // !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0));
+ // mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0));
+ JCExpression zeroLiteral = maker.Literal(CTC_INT, 0);
+ JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral));
+ List<JCExpression> args;
+ if (mapMode) {
+ JCExpression zeroLiteralClone = maker.Literal(CTC_INT, 0);
+ JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$value" : ""), "get"), List.of(zeroLiteralClone));
+ args = List.of(arg, arg2);
+ } else {
+ args = List.of(arg);
+ }
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", singletonCollectionMethod), args);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase singletonCase = maker.Case(maker.Literal(CTC_INT, 1), List.of(assignStat, breakStat));
+ cases.append(singletonCase);
+ }
+
+ { // default:
+ List<JCStatement> statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source);
+ JCCase defaultCase = maker.Case(null, statements);
+ cases.append(defaultCase);
+ }
+
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true), cases.toList());
+ JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source);
+ JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
+ return List.of(varDefStat, switchStat);
+ }
+
+ protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+
+ Name v1Name = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName();
+ Name v2Name = mapMode ? builderType.toName(data.getPluralName() + "$value") : null;
+ JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v1Name);
+ JCExpression cond = maker.Binary(CTC_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+ thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v1Name);
+ JCExpression v1Type = chainDots(builderType, "java", "util", "ArrayList");
+ v1Type = addTypeArgs(1, false, builderType, v1Type, data.getTypeArgs(), source);
+ JCExpression constructArrayList = maker.NewClass(null, jceBlank, v1Type, jceBlank, null);
+ JCStatement initV1 = maker.Exec(maker.Assign(thisDotField, constructArrayList));
+ JCStatement thenPart;
+ if (mapMode) {
+ thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v2Name);
+ JCExpression v2Type = chainDots(builderType, "java", "util", "ArrayList");
+ List<JCExpression> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.tail != null) tArgs = tArgs.tail;
+ else tArgs = List.nil();
+ v2Type = addTypeArgs(1, false, builderType, v2Type, tArgs, source);
+ constructArrayList = maker.NewClass(null, jceBlank, v2Type, jceBlank, null);
+ JCStatement initV2 = maker.Exec(maker.Assign(thisDotField, constructArrayList));
+ thenPart = maker.Block(0, List.of(initV1, initV2));
+ } else {
+ thenPart = initV1;
+ }
+ return maker.If(cond, thenPart, null);
+ }
+
+ protected List<JCStatement> createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ Name thisName = builderType.toName("this");
+
+ JCStatement createStat; {
+ // pluralName = new java.util.TargetType(initialCap);
+ List<JCExpression> constructorArgs = List.nil();
+ if (addInitialCapacityArg) {
+ Name varName = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName();
+ // this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE;
+ // lessThanCutOff = this.varName.size() < MAX_POWER_OF_2
+ JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard), maker.Literal(CTC_INT, 0x40000000));
+ JCExpression integerMaxValue = genJavaLangTypeRef(builderType, "Integer", "MAX_VALUE");
+ JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard));
+ JCExpression sizeFormulaRightLeft = maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard), maker.Literal(CTC_INT, 3));
+ JCExpression sizeFormulaRight = maker.Binary(CTC_DIV, sizeFormulaRightLeft, maker.Literal(CTC_INT, 3));
+ JCExpression sizeFormula = maker.Binary(CTC_PLUS, sizeFormulaLeft, sizeFormulaRight);
+ constructorArgs = List.<JCExpression>of(maker.Conditional(lessThanCutoff, sizeFormula, integerMaxValue));
+ }
+
+ JCExpression targetTypeExpr = chainDots(builderType, "java", "util", targetType);
+ targetTypeExpr = addTypeArgs(mapMode ? 2 : 1, false, builderType, targetTypeExpr, data.getTypeArgs(), source);
+ JCExpression constructorCall = maker.NewClass(null, jceBlank, targetTypeExpr, constructorArgs, null);
+ if (defineVar) {
+ JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source);
+ createStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, constructorCall);
+ } else {
+ createStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), constructorCall));
+ }
+ }
+
+ JCStatement fillStat; {
+ if (mapMode) {
+ // for (int $i = 0; $i < this.pluralname$key.size(); i++) pluralname.put(this.pluralname$key.get($i), this.pluralname$value.get($i));
+ Name ivar = builderType.toName("$i");
+ Name keyVarName = builderType.toName(data.getPluralName() + "$key");
+ JCExpression pluralnameDotPut = maker.Select(maker.Ident(data.getPluralName()), builderType.toName("put"));
+ JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$key", "get"), List.<JCExpression>of(maker.Ident(ivar)));
+ JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar)));
+ JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2)));
+ JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0));
+ JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard));
+ JCExpression incrementExpr = maker.Unary(CTC_POSTINC, maker.Ident(ivar));
+ fillStat = maker.ForLoop(List.of(forInit), checkExpr, List.of(maker.Exec(incrementExpr)), putStatement);
+ } else {
+ // pluralname.addAll(this.pluralname);
+ JCExpression thisDotPluralName = maker.Select(maker.Ident(thisName), data.getPluralName());
+ fillStat = maker.Exec(maker.Apply(jceBlank, maker.Select(maker.Ident(data.getPluralName()), builderType.toName("addAll")), List.of(thisDotPluralName)));
+ }
+ if (nullGuard) {
+ JCExpression thisDotField = maker.Select(maker.Ident(thisName), mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName());
+ JCExpression nullCheck = maker.Binary(CTC_NOT_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+ fillStat = maker.If(nullCheck, fillStat, null);
+ }
+ }
+ JCStatement unmodifiableStat; {
+ // pluralname = Collections.unmodifiableInterfaceType(pluralname);
+ JCExpression arg = maker.Ident(data.getPluralName());
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "unmodifiable" + data.getTargetSimpleType()), List.of(arg));
+ unmodifiableStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+
+ return List.of(createStat, fillStat, unmodifiableStat);
+ }
+}