From acd00976628b1ef7be6ed5aacb3637e993575c26 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Mon, 12 Jan 2015 05:26:24 +0100 Subject: added singleton builder support for javac: java.util lists --- .../singulars/JavacJavaUtilListSingularizer.java | 194 +++++++++++++++++++++ .../singulars/JavacJavaUtilSetSingularizer.java | 6 +- .../singulars/JavacJavaUtilSingularizer.java | 6 +- .../after-delombok/BuilderSingletonLists.java | 110 ++++++++++++ .../resource/before/BuilderSingletonLists.java | 11 ++ 5 files changed, 322 insertions(+), 5 deletions(-) create mode 100644 src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java create mode 100644 test/transform/resource/after-delombok/BuilderSingletonLists.java create mode 100644 test/transform/resource/before/BuilderSingletonLists.java 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..3e5b322c --- /dev/null +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.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 org.mangosdk.spi.ProviderFor; + +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 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.JCCase; +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 JavacJavaUtilListSingularizer extends JavacJavaUtilSingularizer { + @Override public LombokImmutableList getSupportedTypes() { + return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.util.Iterable"); + } + + @Override public java.util.List generateFields(SingularData data, JavacNode builderType, JCTree 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) { + 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 typeParams = List.nil(); + List thrown = List.nil(); + + JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + ListBuffer statements = new ListBuffer(); + statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source)); + JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "add"); + JCExpression invokeAdd = maker.Apply(List.nil(), thisDotFieldDotAdd, List.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); + } + + private void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + List typeParams = List.nil(); + List thrown = List.nil(); + JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + ListBuffer statements = new ListBuffer(); + statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source)); + JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "addAll"); + JCExpression invokeAdd = maker.Apply(List.nil(), thisDotFieldDotAdd, List.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); + } + + @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer statements, Name targetVariableName) { + JavacTreeMaker maker = builderType.getTreeMaker(); + List jceBlank = List.nil(); + ListBuffer cases = new ListBuffer(); + + /* 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 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 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 = chainDots(builderType, "java", "util", "List"); + 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 createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) { + List jceBlank = List.nil(); + Name thisName = builderType.toName("this"); + + JCStatement createStat; { + // pluralName = new java.util.ArrayList(this.pluralName.size()); + List constructorArgs = List.nil(); + constructorArgs = List.of(getSize(maker, builderType, data.getPluralName(), false)); + JCExpression targetTypeExpr = chainDots(builderType, "java", "util", "ArrayList"); + targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs(), source); + JCExpression constructorCall = maker.NewClass(null, jceBlank, targetTypeExpr, constructorArgs, null); + createStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), constructorCall)); + } + + JCStatement fillStat; { + // 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))); + } + + JCStatement unmodifiableStat; { + // pluralname = Collections.unmodifiableInterfaceType(pluralname); + JCExpression arg = maker.Ident(data.getPluralName()); + JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "unmodifiableList"), List.of(arg)); + unmodifiableStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke)); + } + + return List.of(createStat, fillStat, unmodifiableStat); + } +} diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java index 16055675..ce70f8b6 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java @@ -66,12 +66,14 @@ public class JavacJavaUtilSetSingularizer extends JavacJavaUtilSingularizer { @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { 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(builderType.toName("this"))) : null; + 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(builderType.toName("this"))) : null; + returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); } diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java index 81e277e9..f89df80e 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java @@ -37,7 +37,7 @@ import lombok.javac.JavacTreeMaker; import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer; import lombok.javac.handlers.JavacSingularsRecipes.SingularData; -public abstract class JavacJavaUtilSingularizer extends JavacSingularizer { +abstract class JavacJavaUtilSingularizer extends JavacSingularizer { protected List createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source) { List jceBlank = List.nil(); ListBuffer cases = new ListBuffer(); @@ -58,11 +58,11 @@ public abstract class JavacJavaUtilSingularizer extends JavacSingularizer { // !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" : "").toString(), "get"), List.of(zeroLiteral)); + JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral)); List args; if (mapMode) { JCExpression zeroLiteralClone = maker.Literal(CTC_INT, 0); - JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$value" : "").toString(), "get"), List.of(zeroLiteralClone)); + 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); diff --git a/test/transform/resource/after-delombok/BuilderSingletonLists.java b/test/transform/resource/after-delombok/BuilderSingletonLists.java new file mode 100644 index 00000000..394bd482 --- /dev/null +++ b/test/transform/resource/after-delombok/BuilderSingletonLists.java @@ -0,0 +1,110 @@ +import java.util.List; +import java.util.Collection; +class BuilderSingletonLists { + private List children; + private Collection scarves; + private List rawList; + @java.lang.SuppressWarnings("all") + BuilderSingletonLists(final List children, final Collection scarves, final List rawList) { + this.children = children; + this.scarves = scarves; + this.rawList = rawList; + } + @java.lang.SuppressWarnings("all") + public static class BuilderSingletonListsBuilder { + private java.util.ArrayList children; + private java.util.ArrayList scarves; + private java.util.ArrayList rawList; + @java.lang.SuppressWarnings("all") + BuilderSingletonListsBuilder() { + } + @java.lang.SuppressWarnings("all") + public BuilderSingletonListsBuilder child(final T child) { + if (this.children == null) this.children = new java.util.ArrayList(); + this.children.add(child); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingletonListsBuilder children(final java.util.Collection children) { + if (this.children == null) this.children = new java.util.ArrayList(); + this.children.addAll(children); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingletonListsBuilder scarf(final Number scarf) { + if (this.scarves == null) this.scarves = new java.util.ArrayList(); + this.scarves.add(scarf); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingletonListsBuilder scarves(final java.util.Collection scarves) { + if (this.scarves == null) this.scarves = new java.util.ArrayList(); + this.scarves.addAll(scarves); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingletonListsBuilder rawList(final java.lang.Object rawList) { + if (this.rawList == null) this.rawList = new java.util.ArrayList(); + this.rawList.add(rawList); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingletonListsBuilder rawList(final java.util.Collection rawList) { + if (this.rawList == null) this.rawList = new java.util.ArrayList(); + this.rawList.addAll(rawList); + return this; + } + @java.lang.SuppressWarnings("all") + public BuilderSingletonLists build() { + java.util.List children; + switch (this.children == null ? 0 : this.children.size()) { + case 0: + children = java.util.Collections.emptyList(); + break; + case 1: + children = java.util.Collections.singletonList(this.children.get(0)); + break; + default: + children = new java.util.ArrayList(this.children.size()); + children.addAll(this.children); + children = java.util.Collections.unmodifiableList(children); + } + java.util.List scarves; + switch (this.scarves == null ? 0 : this.scarves.size()) { + case 0: + scarves = java.util.Collections.emptyList(); + break; + case 1: + scarves = java.util.Collections.singletonList(this.scarves.get(0)); + break; + default: + scarves = new java.util.ArrayList(this.scarves.size()); + scarves.addAll(this.scarves); + scarves = java.util.Collections.unmodifiableList(scarves); + } + java.util.List rawList; + switch (this.rawList == null ? 0 : this.rawList.size()) { + case 0: + rawList = java.util.Collections.emptyList(); + break; + case 1: + rawList = java.util.Collections.singletonList(this.rawList.get(0)); + break; + default: + rawList = new java.util.ArrayList(this.rawList.size()); + rawList.addAll(this.rawList); + rawList = java.util.Collections.unmodifiableList(rawList); + } + return new BuilderSingletonLists(children, scarves, rawList); + } + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "BuilderSingletonLists.BuilderSingletonListsBuilder(children=" + this.children + ", scarves=" + this.scarves + ", rawList=" + this.rawList + ")"; + } + } + @java.lang.SuppressWarnings("all") + public static BuilderSingletonListsBuilder builder() { + return new BuilderSingletonListsBuilder(); + } +} diff --git a/test/transform/resource/before/BuilderSingletonLists.java b/test/transform/resource/before/BuilderSingletonLists.java new file mode 100644 index 00000000..4f783244 --- /dev/null +++ b/test/transform/resource/before/BuilderSingletonLists.java @@ -0,0 +1,11 @@ +import java.util.List; +import java.util.Collection; + +import lombok.Singular; + +@lombok.Builder +class BuilderSingletonLists { + @Singular private List children; + @Singular private Collection scarves; + @Singular("rawList") private List rawList; +} -- cgit