aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.xml11
-rw-r--r--buildScripts/ivy.xml1
-rw-r--r--doc/changelog.markdown9
-rw-r--r--src/core/lombok/Builder.java121
-rw-r--r--src/core/lombok/ConfigurationKeys.java59
-rw-r--r--src/core/lombok/Singular.java38
-rw-r--r--src/core/lombok/core/GuavaTypeMap.java58
-rw-r--r--src/core/lombok/core/LombokInternalAliasing.java1
-rw-r--r--src/core/lombok/core/TypeLibrary.java9
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java13
-rw-r--r--src/core/lombok/core/handlers/Singulars.java86
-rw-r--r--src/core/lombok/core/handlers/singulars.txt54
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java117
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java339
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java362
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java2
-rw-r--r--src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java651
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java45
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java45
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java246
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java158
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java136
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java270
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java52
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java300
-rw-r--r--src/core/lombok/experimental/Builder.java5
-rw-r--r--src/core/lombok/javac/JavacNode.java48
-rw-r--r--src/core/lombok/javac/apt/EmptyLombokFileObject.java1
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java293
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java2
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java96
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java292
-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.java193
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java138
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java127
-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
-rw-r--r--src/utils/lombok/core/LombokImmutableList.java6
-rw-r--r--src/utils/lombok/javac/Javac.java8
-rw-r--r--test/configuration/resource/configurationRoot/out.txt12
-rw-r--r--test/core/src/lombok/RunAllTests.java4
-rw-r--r--test/core/src/lombok/RunTestsViaEcj.java1
-rw-r--r--test/core/src/lombok/core/RunCoreTests.java31
-rw-r--r--test/core/src/lombok/core/TestSingulars.java52
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java93
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java76
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularLists.java111
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularMaps.java173
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularNoAutosingularize.java109
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java75
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularSets.java129
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java88
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java71
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularLists.java107
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularMaps.java177
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularNoAutosingularize.java105
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java71
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularSets.java125
-rw-r--r--test/transform/resource/after-ecj/BuilderWithExistingBuilderClass.java2
-rw-r--r--test/transform/resource/before/BuilderSingularGuavaListsSets.java14
-rw-r--r--test/transform/resource/before/BuilderSingularGuavaMaps.java12
-rw-r--r--test/transform/resource/before/BuilderSingularLists.java11
-rw-r--r--test/transform/resource/before/BuilderSingularMaps.java12
-rw-r--r--test/transform/resource/before/BuilderSingularNoAutoSingularize.java11
-rw-r--r--test/transform/resource/before/BuilderSingularRedirectToGuava.java13
-rw-r--r--test/transform/resource/before/BuilderSingularSets.java12
-rw-r--r--test/transform/resource/before/BuilderWithExistingBuilderClass.java2
-rw-r--r--test/transform/resource/messages-delombok/BuilderSingularNoAutosingularize.java.messages2
-rw-r--r--test/transform/resource/messages-ecj/BuilderComplex.java.messages1
-rw-r--r--test/transform/resource/messages-ecj/BuilderSingularNoAutosingularize.java.messages2
73 files changed, 5963 insertions, 671 deletions
diff --git a/build.xml b/build.xml
index 9fe952b9..271cf9d4 100644
--- a/build.xml
+++ b/build.xml
@@ -556,16 +556,7 @@ You can also create your own by writing a 'testenvironment.properties' file. The
<classpath path="build/tests" />
<batchtest>
<fileset dir="test/core/src">
- <include name="**/Test*.java" />
- </fileset>
- <fileset dir="test/transform/src">
- <include name="**/Test*.java" />
- </fileset>
- <fileset dir="test/bytecode/src">
- <include name="**/Test*.java" />
- </fileset>
- <fileset dir="test/configuration/src">
- <include name="**/Test*.java" />
+ <include name="lombok/RunAllTests.java" />
</fileset>
</batchtest>
</junit>
diff --git a/buildScripts/ivy.xml b/buildScripts/ivy.xml
index 1cc249e7..5d362aab 100644
--- a/buildScripts/ivy.xml
+++ b/buildScripts/ivy.xml
@@ -24,6 +24,7 @@
<dependency org="commons-logging" name="commons-logging" rev="1.1.1" conf="test->default; contrib->sources"/>
<dependency org="org.slf4j" name="slf4j-api" rev="1.6.1" conf="test->default; contrib->sources"/>
<dependency org="org.slf4j" name="slf4j-ext" rev="1.6.1" conf="test->default; contrib->sources"/>
+ <dependency org="com.google.guava" name="guava" rev="18.0" conf="test->default; contrib->sources" />
<dependency org="com.googlecode.jarjar" name="jarjar" rev="1.1" conf="buildBase->default" />
<dependency org="org.apache.ant" name="ant" rev="1.8.1" conf="buildBase->default; contrib->sources" />
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index 5974cf07..e7dc910d 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -5,6 +5,15 @@ Lombok Changelog
* Added a launcher to the lombok boot process which removes the need for `-Xbootclasspath` to be in your `eclipse.ini` file, and removes all non-public API and third party dependencies (such as ASM) from the lombok jar, thus removing them from your IDE's auto complete offerings in any project that uses lombok. For those debugging lombok, the launcher enables hot code replace which makes debugging a lot easier, as previously one was required to shut down the IDE, rebuild the jar, and relaunch. Add `-Dshadow.override.lombok=/path/to/lombok/bin` to the launch target for hot code replace.
* BUGFIX: delombok: Using exotic characters in your source files would overzealously backslash-u escape them. Now, all characters are printed unescaped, assuming your chosen encoding can support them. Otherwise, they are escaped. [Issue #759](https://code.google.com/p/projectlombok/issues/detail?id=759)
+* Builder __TODO TODO TODO TODO DO NOT SHIP YET__:
+
+ * features web page
+ * Check if the shadowed localvar names are properly typed; if compatible subtypes, we should cast these to avoid accidentally calling an overload.
+ * Review if there are nay potentially breaking changes in the pipeline for builder BEFORE moving it out of experimental.
+ * Make sure you cover the fact that builder has moved on from experimental in this issue, and on the features page /doc!
+
+* __TODO TODO TODO TODO DO NOT SHIP YET__: At least modify the deprecated warning of the constructorProperties thing that we're gonna kill it soon.
+
### v1.14.8 (September 15th, 2014)
* PERFORMANCE: The configuration system typically hit the filesystem twice per read configuration key instead of hardly ever. This is a continuation of [Issue #682](https://code.google.com/p/projectlombok/issues/detail?id=682).
diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java
new file mode 100644
index 00000000..9cbd2d58
--- /dev/null
+++ b/src/core/lombok/Builder.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013-2014 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;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class
+ * that contains a member which is annotated with {@code @Builder}.
+ * <p>
+ * If a member is annotated, it must be either a constructor or a static method. If a class is annotated,
+ * then a private constructor is generated with all fields as arguments
+ * (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present
+ * on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.
+ * <p>
+ * The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>,
+ * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static
+ * method named {@code builder()} which is also generated for you in the class itself (not in the builder class).
+ * <p>
+ * The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
+ * constructor / static method (each field, when annotating a class), which returns the builder itself.
+ * The builder also has a <code>build()</code> method which returns a completed instance of the original type,
+ * created by passing all parameters as set via the various other methods in the builder to the constructor
+ * or static method that was annotated with {@code @Builder}. The return type of this method will be the same
+ * as the relevant class, unless a static method has been annotated, in which case it'll be equal to the
+ * return type of that method.
+ * <p>
+ * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for &#64;Builder</a>.
+ * <p>
+ * <p>
+ * Before:
+ *
+ * <pre>
+ * &#064;Builder
+ * class Example {
+ * private int foo;
+ * private final String bar;
+ * }
+ * </pre>
+ *
+ * After:
+ *
+ * <pre>
+ * class Example&lt;T&gt; {
+ * private T foo;
+ * private final String bar;
+ *
+ * private Example(T foo, String bar) {
+ * this.foo = foo;
+ * this.bar = bar;
+ * }
+ *
+ * public static &lt;T&gt; ExampleBuilder&lt;T&gt; builder() {
+ * return new ExampleBuilder&lt;T&gt;();
+ * }
+ *
+ * public static class ExampleBuilder&lt;T&gt; {
+ * private T foo;
+ * private String bar;
+ *
+ * private ExampleBuilder() {}
+ *
+ * public ExampleBuilder foo(T foo) {
+ * this.foo = foo;
+ * return this;
+ * }
+ *
+ * public ExampleBuilder bar(String bar) {
+ * this.bar = bar;
+ * return this;
+ * }
+ *
+ * &#064;java.lang.Override public String toString() {
+ * return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
+ * }
+ *
+ * public Example build() {
+ * return new Example(foo, bar);
+ * }
+ * }
+ * }
+ * </pre>
+ */
+@Target({TYPE, METHOD, CONSTRUCTOR})
+@Retention(SOURCE)
+public @interface Builder {
+ /** Name of the static method that creates a new builder instance. Default: {@code builder}. */
+ String builderMethodName() default "builder";
+
+ /** Name of the instance method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
+ String buildMethodName() default "build";
+
+ /** Name of the builder class.
+ * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
+ * Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}.
+ */
+ String builderClassName() default "";
+}
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java
index 301563b8..9b7c6c0a 100644
--- a/src/core/lombok/ConfigurationKeys.java
+++ b/src/core/lombok/ConfigurationKeys.java
@@ -133,9 +133,9 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.equalsAndHashCode.doNotUseGetters} = {@code true} | {@code false}.
*
- * For any class without an {@code @EqualsAndHashCode} that explicitly defines the {@code doNotUseGetters} option, this value is used.
+ * For any class without an {@code @EqualsAndHashCode} that explicitly defines the {@code doNotUseGetters} option, this value is used (default = false).
*/
- public static final ConfigurationKey<Boolean> EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.equalsAndHashCode.doNotUseGetters", "Don't call the getters but use the fields directly in the generated equalsAndHashCode method.") {};
+ public static final ConfigurationKey<Boolean> EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.equalsAndHashCode.doNotUseGetters", "Don't call the getters but use the fields directly in the generated equalsAndHashCode method (default = false).") {};
/**
* lombok configuration: {@code lombok.equalsAndHashCode.flagUsage} = {@code WARNING} | {@code ERROR}.
@@ -149,9 +149,9 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.toString.doNotUseGetters} = {@code true} | {@code false}.
*
- * For any class without an {@code @ToString} that explicitly defines the {@code doNotUseGetters} option, this value is used.
+ * For any class without an {@code @ToString} that explicitly defines the {@code doNotUseGetters} option, this value is used (default = false).
*/
- public static final ConfigurationKey<Boolean> TO_STRING_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.toString.doNotUseGetters", "Don't call the getters but use the fields directly in the generated toString method.") {};
+ public static final ConfigurationKey<Boolean> TO_STRING_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.toString.doNotUseGetters", "Don't call the getters but use the fields directly in the generated toString method (default = false).") {};
/**
* lombok configuration: {@code lombok.toString.flagUsage} = {@code WARNING} | {@code ERROR}.
@@ -163,9 +163,35 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.toString.includeFieldNames} = {@code true} | {@code false}.
*
- * For any class without an {@code @ToString} that explicitly defines the {@code includeFieldNames} option, this value is used.
+ * For any class without an {@code @ToString} that explicitly defines the {@code includeFieldNames} option, this value is used (default = true).
*/
- public static final ConfigurationKey<Boolean> TO_STRING_INCLUDE_FIELD_NAMES = new ConfigurationKey<Boolean>("lombok.toString.includeFieldNames", "Include the field names in the generated toString method.") {};
+ public static final ConfigurationKey<Boolean> TO_STRING_INCLUDE_FIELD_NAMES = new ConfigurationKey<Boolean>("lombok.toString.includeFieldNames", "Include the field names in the generated toString method (default = true).") {};
+
+ // ----- Builder -----
+
+ /**
+ * lombok configuration: {@code lombok.builder.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @Builder} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> BUILDER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.builder.flagUsage", "Emit a warning or error if @Builder is used.") {};
+
+ /**
+ * lombok configuration: {@code lombok.builder.useGuava} = {@code true} | {@code false}.
+ *
+ * If explicitly set to {@code true}, guava's {@code ImmutableList} etc are used to implement the immutable collection datatypes generated by @Singular @Builder for fields/parameters of type {@code java.util.List} and such.
+ * By default, unmodifiable-wrapped versions of {@code java.util} types are used.
+ */
+ public static final ConfigurationKey<Boolean> BUILDER_USE_GUAVA = new ConfigurationKey<Boolean>("lombok.builder.useGuava", "Generate backing immutable implementations for @Singular on java.util.* types by using guava's ImmutableList, etc. Normally java.util's mutable types are used and wrapped to make them immutable.") {};
+
+ // ----- Singular -----
+
+ /**
+ * lombok configuration: {@code lombok.singular.auto} = {@code true} | {@code false}.
+ *
+ * By default or if explicitly set to {@code true}, lombok will attempt to automatically singularize the name of your variable/parameter when using {@code @Singular}; the name is assumed to be written in english, and a plural. If explicitly to {@code false}, you must always specify the singular form; this is especially useful if your identifiers are in a foreign language.
+ */
+ public static final ConfigurationKey<Boolean> SINGULAR_AUTO = new ConfigurationKey<Boolean>("lombok.singular.auto", "If true (default): Automatically singularize the assumed-to-be-plural name of your variable/parameter when using {@code @Singular}.") {};
// ##### Standalones #####
@@ -194,7 +220,7 @@ public class ConfigurationKeys {
*
* Sets the exception to throw if {@code @NonNull} is applied to a method parameter, and a caller passes in {@code null}.
*/
- public static final ConfigurationKey<NullCheckExceptionType> NON_NULL_EXCEPTION_TYPE = new ConfigurationKey<NullCheckExceptionType>("lombok.nonNull.exceptionType", "The type of the exception to throw if a passed-in argument is null. Default: NullPointerException.") {};
+ public static final ConfigurationKey<NullCheckExceptionType> NON_NULL_EXCEPTION_TYPE = new ConfigurationKey<NullCheckExceptionType>("lombok.nonNull.exceptionType", "The type of the exception to throw if a passed-in argument is null (Default: NullPointerException).") {};
/**
* lombok configuration: {@code lombok.nonNull.flagUsage} = {@code WARNING} | {@code ERROR}.
@@ -289,7 +315,7 @@ public class ConfigurationKeys {
*
* If set the various log annotations (which make a log field) will use the stated identifier instead of {@code log} as a name.
*/
- public static final ConfigurationKey<String> LOG_ANY_FIELD_NAME = new ConfigurationKey<String>("lombok.log.fieldName", "Use this name for the generated logger fields (default: 'log')") {};
+ public static final ConfigurationKey<String> LOG_ANY_FIELD_NAME = new ConfigurationKey<String>("lombok.log.fieldName", "Use this name for the generated logger fields (default: 'log').") {};
/**
* lombok configuration: {@code lombok.log.fieldIsStatic} = {@code true} | {@code false}.
@@ -329,25 +355,16 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.accessors.chain} = {@code true} | {@code false}.
*
- * For any class without an {@code @Accessors} that explicitly defines the {@code chain} option, this value is used.
+ * For any class without an {@code @Accessors} that explicitly defines the {@code chain} option, this value is used (default = false).
*/
- public static final ConfigurationKey<Boolean> ACCESSORS_CHAIN = new ConfigurationKey<Boolean>("lombok.accessors.chain", "Generate setters that return 'this' instead of 'void'.") {};
+ public static final ConfigurationKey<Boolean> ACCESSORS_CHAIN = new ConfigurationKey<Boolean>("lombok.accessors.chain", "Generate setters that return 'this' instead of 'void' (default: false).") {};
/**
* lombok configuration: {@code lombok.accessors.fluent} = {@code true} | {@code false}.
*
- * For any class without an {@code @Accessors} that explicitly defines the {@code fluent} option, this value is used.
+ * For any class without an {@code @Accessors} that explicitly defines the {@code fluent} option, this value is used (default = false).
*/
- public static final ConfigurationKey<Boolean> ACCESSORS_FLUENT = new ConfigurationKey<Boolean>("lombok.accessors.fluent", "Generate getters and setters using only the field name (no get/set prefix).") {};
-
- // ----- Builder -----
-
- /**
- * lombok configuration: {@code lombok.builder.flagUsage} = {@code WARNING} | {@code ERROR}.
- *
- * If set, <em>any</em> usage of {@code @Builder} results in a warning / error.
- */
- public static final ConfigurationKey<FlagUsageType> BUILDER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.builder.flagUsage", "Emit a warning or error if @Builder is used.") {};
+ public static final ConfigurationKey<Boolean> ACCESSORS_FLUENT = new ConfigurationKey<Boolean>("lombok.accessors.fluent", "Generate getters and setters using only the field name (no get/set prefix) (default: false).") {};
// ----- ExtensionMethod -----
diff --git a/src/core/lombok/Singular.java b/src/core/lombok/Singular.java
new file mode 100644
index 00000000..7f22b008
--- /dev/null
+++ b/src/core/lombok/Singular.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013-2014 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;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * The singular annotation is used together with {@code @Builder} to create single element 'add' methods in the builder for collections.
+ * <p>
+ */
+@Target({FIELD, PARAMETER})
+@Retention(SOURCE)
+public @interface Singular {
+ String value() default "";
+}
diff --git a/src/core/lombok/core/GuavaTypeMap.java b/src/core/lombok/core/GuavaTypeMap.java
new file mode 100644
index 00000000..900d2b72
--- /dev/null
+++ b/src/core/lombok/core/GuavaTypeMap.java
@@ -0,0 +1,58 @@
+/*
+ * 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.core;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class GuavaTypeMap {
+ private static final Map<String, String> TYPE_TO_GUAVA_TYPE; static {
+ Map<String, String> m = new HashMap<String, String>();
+
+ m.put("java.util.NavigableSet", "ImmutableSortedSet");
+ m.put("java.util.NavigableMap", "ImmutableSortedMap");
+ m.put("java.util.SortedSet", "ImmutableSortedSet");
+ m.put("java.util.SortedMap", "ImmutableSortedMap");
+ m.put("java.util.Set", "ImmutableSet");
+ m.put("java.util.Map", "ImmutableMap");
+ m.put("java.util.Collection", "ImmutableList");
+ m.put("java.util.List", "ImmutableList");
+
+ m.put("com.google.common.collect.ImmutableSet", "ImmutableSet");
+ m.put("com.google.common.collect.ImmutableSortedSet", "ImmutableSortedSet");
+ m.put("com.google.common.collect.ImmutableMap", "ImmutableMap");
+ m.put("com.google.common.collect.ImmutableBiMap", "ImmutableBiMap");
+ m.put("com.google.common.collect.ImmutableSortedMap", "ImmutableSortedMap");
+ m.put("com.google.common.collect.ImmutableList", "ImmutableList");
+ m.put("com.google.common.collect.ImmutableCollection", "ImmutableList");
+
+ TYPE_TO_GUAVA_TYPE = Collections.unmodifiableMap(m);
+ }
+
+ public static String getGuavaTypeName(String fqn) {
+ String target = TYPE_TO_GUAVA_TYPE.get(fqn);
+ return target != null ? target : "ImmutableList";
+ }
+
+ private GuavaTypeMap() {}
+}
diff --git a/src/core/lombok/core/LombokInternalAliasing.java b/src/core/lombok/core/LombokInternalAliasing.java
index 8d6794ae..08764a5c 100644
--- a/src/core/lombok/core/LombokInternalAliasing.java
+++ b/src/core/lombok/core/LombokInternalAliasing.java
@@ -50,6 +50,7 @@ public class LombokInternalAliasing {
Map<String, String> m2 = new HashMap<String, String>();
m2.put("lombok.experimental.Value", "lombok.Value");
+ m2.put("lombok.experimental.Builder", "lombok.Builder");
m2.put("lombok.Delegate", "lombok.experimental.Delegate");
ALIASES = Collections.unmodifiableMap(m2);
}
diff --git a/src/core/lombok/core/TypeLibrary.java b/src/core/lombok/core/TypeLibrary.java
index c0e9dc43..dc557c47 100644
--- a/src/core/lombok/core/TypeLibrary.java
+++ b/src/core/lombok/core/TypeLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 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
@@ -37,6 +37,7 @@ import java.util.Map;
public class TypeLibrary {
private final Map<String, String> unqualifiedToQualifiedMap;
private final String unqualified, qualified;
+ private boolean locked;
public TypeLibrary() {
unqualifiedToQualifiedMap = new HashMap<String, String>();
@@ -44,6 +45,10 @@ public class TypeLibrary {
qualified = null;
}
+ public void lock() {
+ this.locked = true;
+ }
+
private TypeLibrary(String fqnSingleton) {
unqualifiedToQualifiedMap = null;
qualified = fqnSingleton;
@@ -53,6 +58,7 @@ public class TypeLibrary {
} else {
unqualified = fqnSingleton.substring(idx + 1);
}
+ locked = true;
}
public static TypeLibrary createLibraryForSingleType(String fqnSingleton) {
@@ -65,6 +71,7 @@ public class TypeLibrary {
* @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'.
*/
public void addType(String fullyQualifiedTypeName) {
+ if (locked) throw new IllegalStateException("locked");
fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", ".");
int idx = fullyQualifiedTypeName.lastIndexOf('.');
if (idx == -1) throw new IllegalArgumentException(
diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
index 621d8760..87462921 100644
--- a/src/core/lombok/core/handlers/HandlerUtil.java
+++ b/src/core/lombok/core/handlers/HandlerUtil.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
@@ -87,6 +87,9 @@ public class HandlerUtil {
return true;
}
+ public static String autoSingularize(String plural) {
+ return Singulars.autoSingularize(plural);
+ }
public static void handleFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) {
FlagUsageType fut = node.getAst().readConfiguration(key);
@@ -303,7 +306,7 @@ public class HandlerUtil {
return booleanPrefix + fName.substring(2);
}
- return buildName(isBoolean ? booleanPrefix : normalPrefix, fName);
+ return buildAccessorName(isBoolean ? booleanPrefix : normalPrefix, fName);
}
/**
@@ -375,8 +378,8 @@ public class HandlerUtil {
if (adhereToFluent && fluent) {
names.add(baseName);
} else {
- names.add(buildName(normalPrefix, baseName));
- if (!normalPrefix.equals(booleanPrefix)) names.add(buildName(booleanPrefix, baseName));
+ names.add(buildAccessorName(normalPrefix, baseName));
+ if (!normalPrefix.equals(booleanPrefix)) names.add(buildAccessorName(booleanPrefix, baseName));
}
}
@@ -407,7 +410,7 @@ public class HandlerUtil {
* @param suffix Something like {@code running}.
* @return prefix + smartly title-cased suffix. For example, {@code setRunning}.
*/
- private static String buildName(String prefix, String suffix) {
+ public static String buildAccessorName(String prefix, String suffix) {
if (suffix.length() == 0) return prefix;
if (prefix.length() == 0) return suffix;
diff --git a/src/core/lombok/core/handlers/Singulars.java b/src/core/lombok/core/handlers/Singulars.java
new file mode 100644
index 00000000..87895790
--- /dev/null
+++ b/src/core/lombok/core/handlers/Singulars.java
@@ -0,0 +1,86 @@
+/*
+ * 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.core.handlers;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Singulars {
+ private static final List<String> SINGULAR_STORE; // intended to be immutable.
+
+ static {
+ SINGULAR_STORE = new ArrayList<String>();
+
+ try {
+ InputStream in = Singulars.class.getResourceAsStream("singulars.txt");
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ line = line.trim();
+ if (line.startsWith("#") || line.isEmpty()) continue;
+ if (line.endsWith(" =")) {
+ SINGULAR_STORE.add(line.substring(0, line.length() - 2));
+ SINGULAR_STORE.add("");
+ continue;
+ }
+
+ int idx = line.indexOf(" = ");
+ SINGULAR_STORE.add(line.substring(0, idx));
+ SINGULAR_STORE.add(line.substring(idx + 3));
+ }
+ } finally {
+ try {
+ in.close();
+ } catch (Throwable ignore) {}
+ }
+ } catch (IOException e) {
+ SINGULAR_STORE.clear();
+ }
+ }
+
+ public static String autoSingularize(String in) {
+ final int inLen = in.length();
+ for (int i = 0; i < SINGULAR_STORE.size(); i+= 2) {
+ final String lastPart = SINGULAR_STORE.get(i);
+ final boolean wholeWord = Character.isUpperCase(lastPart.charAt(0));
+ final int endingOnly = lastPart.charAt(0) == '-' ? 1 : 0;
+ final int len = lastPart.length();
+ if (inLen < len) continue;
+ if (!in.regionMatches(true, inLen - len + endingOnly, lastPart, endingOnly, len - endingOnly)) continue;
+ if (wholeWord && inLen != len && !Character.isUpperCase(in.charAt(inLen - len))) continue;
+
+ String replacement = SINGULAR_STORE.get(i + 1);
+ if (replacement.equals("!")) return null;
+
+ boolean capitalizeFirst = !replacement.isEmpty() && Character.isUpperCase(in.charAt(inLen - len + endingOnly));
+ String pre = in.substring(0, inLen - len + endingOnly);
+ String post = capitalizeFirst ? Character.toUpperCase(replacement.charAt(0)) + replacement.substring(1) : replacement;
+ return pre + post;
+ }
+
+ return null;
+ }
+}
diff --git a/src/core/lombok/core/handlers/singulars.txt b/src/core/lombok/core/handlers/singulars.txt
new file mode 100644
index 00000000..9976c76c
--- /dev/null
+++ b/src/core/lombok/core/handlers/singulars.txt
@@ -0,0 +1,54 @@
+#Based on https://github.com/rails/rails/blob/efff6c1fd4b9e2e4c9f705a45879373cb34a5b0e/activesupport/lib/active_support/inflections.rb
+
+quizzes = quiz
+matrices = matrix
+indices = index
+vertices = vertex
+statuses = status
+aliases = alias
+alias = !
+species = !
+Axes = !
+-axes = axe
+sexes = sex
+Testes = testis
+movies = movie
+octopodes = octopus
+buses = bus
+Mice = mouse
+Lice = louse
+News = !
+# We could add more detail (axemen, boatsmen, boogymen, cavemen, gentlemen, etc, but (A) there's stuff like 'cerumen', and (B) the 'men' ending is common in singulars and other languages.)
+# Therefore, the odds of a mistake are too high, so other than these 2 well known cases, so force the explicit singular.
+Men = man
+Women = woman
+minutiae = minutia
+shoes = shoe
+synopses = synopsis
+prognoses = prognosis
+theses = thesis
+diagnoses = diagnosis
+bases = base
+analyses = analysis
+Crises = crisis
+children = child
+moves = move
+zombies = zombie
+-quies = quy
+-us = !
+-is = !
+series = !
+-ies = y
+-oes = o
+hives = hive
+-tives = tive
+-sses = ss
+-ches = ch
+-xes = x
+-shes = sh
+-lves = lf
+-rves = rf
+-ves = fe
+-ss = !
+-us = !
+-s =
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 8326e1d0..426171c2 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.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
@@ -143,7 +143,7 @@ public class EclipseHandlerUtil {
return getGeneratedBy(node) != null;
}
- public static ASTNode setGeneratedBy(ASTNode node, ASTNode source) {
+ public static <T extends ASTNode> T setGeneratedBy(T node, ASTNode source) {
ASTNode_generatedBy.set(node, source);
return node;
}
@@ -201,6 +201,7 @@ public class EclipseHandlerUtil {
public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(EclipseNode typeNode, EclipseNode errorNode) {
List<String> disallowed = null;
for (EclipseNode child : typeNode.down()) {
+ if (child.getKind() != Kind.ANNOTATION) continue;
for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
if (annotationTypeMatches(annType, child)) {
if (disallowed == null) disallowed = new ArrayList<String>();
@@ -298,6 +299,10 @@ public class EclipseHandlerUtil {
return new SingleTypeReference(typeName, p);
}
+ public static TypeReference[] copyTypes(TypeReference[] refs) {
+ return copyTypes(refs, null);
+ }
+
/**
* Convenience method that creates a new array and copies each TypeReference in the source array via
* {@link #copyType(TypeReference, ASTNode)}.
@@ -312,6 +317,10 @@ public class EclipseHandlerUtil {
return outs;
}
+ public static TypeReference copyType(TypeReference ref) {
+ return copyType(ref, null);
+ }
+
/**
* You can't share TypeReference objects or subtle errors start happening.
* Unfortunately the TypeReference type hierarchy is complicated and there's no clone
@@ -336,22 +345,23 @@ public class EclipseHandlerUtil {
}
}
}
+
TypeReference typeRef = new ParameterizedQualifiedTypeReference(iRef.tokens, args, iRef.dimensions(), copy(iRef.sourcePositions));
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof ArrayQualifiedTypeReference) {
ArrayQualifiedTypeReference iRef = (ArrayQualifiedTypeReference) ref;
TypeReference typeRef = new ArrayQualifiedTypeReference(iRef.tokens, iRef.dimensions(), copy(iRef.sourcePositions));
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof QualifiedTypeReference) {
QualifiedTypeReference iRef = (QualifiedTypeReference) ref;
TypeReference typeRef = new QualifiedTypeReference(iRef.tokens, copy(iRef.sourcePositions));
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
@@ -368,14 +378,14 @@ public class EclipseHandlerUtil {
}
TypeReference typeRef = new ParameterizedSingleTypeReference(iRef.token, args, iRef.dimensions(), (long)iRef.sourceStart << 32 | iRef.sourceEnd);
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof ArrayTypeReference) {
ArrayTypeReference iRef = (ArrayTypeReference) ref;
TypeReference typeRef = new ArrayTypeReference(iRef.token, iRef.dimensions(), (long)iRef.sourceStart << 32 | iRef.sourceEnd);
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
@@ -386,14 +396,14 @@ public class EclipseHandlerUtil {
wildcard.sourceStart = original.sourceStart;
wildcard.sourceEnd = original.sourceEnd;
if (original.bound != null) wildcard.bound = copyType(original.bound, source);
- setGeneratedBy(wildcard, source);
+ if (source != null) setGeneratedBy(wildcard, source);
return wildcard;
}
if (ref instanceof SingleTypeReference) {
SingleTypeReference iRef = (SingleTypeReference) ref;
TypeReference typeRef = new SingleTypeReference(iRef.token, (long)iRef.sourceStart << 32 | iRef.sourceEnd);
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
@@ -444,8 +454,12 @@ public class EclipseHandlerUtil {
return typeMatches(type, node, ((Annotation)node.get()).type);
}
+ public static TypeReference cloneSelfType(EclipseNode context) {
+ return cloneSelfType(context, null);
+ }
+
public static TypeReference cloneSelfType(EclipseNode context, ASTNode source) {
- int pS = source.sourceStart, pE = source.sourceEnd;
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
long p = (long)pS << 32 | pE;
EclipseNode type = context;
TypeReference result = null;
@@ -457,7 +471,7 @@ public class EclipseHandlerUtil {
int idx = 0;
for (TypeParameter param : typeDecl.typeParameters) {
TypeReference typeRef = new SingleTypeReference(param.name, (long)param.sourceStart << 32 | param.sourceEnd);
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
refs[idx++] = typeRef;
}
result = new ParameterizedSingleTypeReference(typeDecl.name, refs, 0, p);
@@ -465,7 +479,7 @@ public class EclipseHandlerUtil {
result = new SingleTypeReference(((TypeDeclaration)type.get()).name, p);
}
}
- if (result != null) setGeneratedBy(result, source);
+ if (result != null && source != null) setGeneratedBy(result, source);
return result;
}
@@ -668,47 +682,39 @@ public class EclipseHandlerUtil {
*/
public static <A extends java.lang.annotation.Annotation> AnnotationValues<A>
createAnnotation(Class<A> type, final EclipseNode annotationNode) {
+
final Annotation annotation = (Annotation) annotationNode.get();
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
- final MemberValuePair[] pairs = annotation.memberValuePairs();
- for (Method m : type.getDeclaredMethods()) {
- if (!Modifier.isPublic(m.getModifiers())) continue;
- String name = m.getName();
+ MemberValuePair[] memberValuePairs = annotation.memberValuePairs();
+
+ if (memberValuePairs != null) for (final MemberValuePair pair : memberValuePairs) {
List<String> raws = new ArrayList<String>();
List<Object> expressionValues = new ArrayList<Object>();
List<Object> guesses = new ArrayList<Object>();
- Expression fullExpression = null;
Expression[] expressions = null;
- if (pairs != null) for (MemberValuePair pair : pairs) {
- char[] n = pair.name;
- String mName = n == null ? "value" : new String(pair.name);
- if (mName.equals(name)) fullExpression = pair.value;
+ char[] n = pair.name;
+ String mName = (n == null || n.length == 0) ? "value" : new String(pair.name);
+ final Expression rhs = pair.value;
+ if (rhs instanceof ArrayInitializer) {
+ expressions = ((ArrayInitializer)rhs).expressions;
+ } else if (rhs != null) {
+ expressions = new Expression[] { rhs };
}
-
- boolean isExplicit = fullExpression != null;
-
- if (isExplicit) {
- if (fullExpression instanceof ArrayInitializer) {
- expressions = ((ArrayInitializer)fullExpression).expressions;
- } else expressions = new Expression[] { fullExpression };
- if (expressions != null) for (Expression ex : expressions) {
- StringBuffer sb = new StringBuffer();
- ex.print(0, sb);
- raws.add(sb.toString());
- expressionValues.add(ex);
- guesses.add(calculateValue(ex));
- }
+ if (expressions != null) for (Expression ex : expressions) {
+ StringBuffer sb = new StringBuffer();
+ ex.print(0, sb);
+ raws.add(sb.toString());
+ expressionValues.add(ex);
+ guesses.add(calculateValue(ex));
}
- final Expression fullExpr = fullExpression;
final Expression[] exprs = expressions;
-
- values.put(name, new AnnotationValue(annotationNode, raws, expressionValues, guesses, isExplicit) {
+ values.put(mName, new AnnotationValue(annotationNode, raws, expressionValues, guesses, true) {
@Override public void setError(String message, int valueIdx) {
Expression ex;
- if (valueIdx == -1) ex = fullExpr;
+ if (valueIdx == -1) ex = rhs;
else ex = exprs != null ? exprs[valueIdx] : null;
if (ex == null) ex = annotation;
@@ -721,7 +727,7 @@ public class EclipseHandlerUtil {
@Override public void setWarning(String message, int valueIdx) {
Expression ex;
- if (valueIdx == -1) ex = fullExpr;
+ if (valueIdx == -1) ex = rhs;
else ex = exprs != null ? exprs[valueIdx] : null;
if (ex == null) ex = annotation;
@@ -734,6 +740,21 @@ public class EclipseHandlerUtil {
});
}
+ for (Method m : type.getDeclaredMethods()) {
+ if (!Modifier.isPublic(m.getModifiers())) continue;
+ String name = m.getName();
+ if (!values.containsKey(name)) {
+ values.put(name, new AnnotationValue(annotationNode, new ArrayList<String>(), new ArrayList<Object>(), new ArrayList<Object>(), false) {
+ @Override public void setError(String message, int valueIdx) {
+ annotationNode.addError(message);
+ }
+ @Override public void setWarning(String message, int valueIdx) {
+ annotationNode.addWarning(message);
+ }
+ });
+ }
+ }
+
return new AnnotationValues<A>(type, values, annotationNode);
}
@@ -865,7 +886,7 @@ public class EclipseHandlerUtil {
}
static Expression createFieldAccessor(EclipseNode field, FieldAccess fieldAccess, ASTNode source) {
- int pS = source.sourceStart, pE = source.sourceEnd;
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
long p = (long)pS << 32 | pE;
boolean lookForGetter = lookForGetter(field, fieldAccess);
@@ -881,14 +902,17 @@ public class EclipseHandlerUtil {
ref.receiver = new SingleNameReference(((TypeDeclaration)containerNode.get()).name, p);
} else {
Expression smallRef = new FieldReference(field.getName().toCharArray(), p);
- setGeneratedBy(smallRef, source);
+ if (source != null) setGeneratedBy(smallRef, source);
return smallRef;
}
} else {
ref.receiver = new ThisReference(pS, pE);
}
- setGeneratedBy(ref, source);
- setGeneratedBy(ref.receiver, source);
+
+ if (source != null) {
+ setGeneratedBy(ref, source);
+ setGeneratedBy(ref.receiver, source);
+ }
return ref;
}
@@ -1489,7 +1513,7 @@ public class EclipseHandlerUtil {
* with eclipse versions before 3.7.
*/
public static IntLiteral makeIntLiteral(char[] token, ASTNode source) {
- int pS = source.sourceStart, pE = source.sourceEnd;
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
IntLiteral result;
try {
if (intLiteralConstructor != null) {
@@ -1504,7 +1528,8 @@ public class EclipseHandlerUtil {
} catch (InstantiationException e) {
throw Lombok.sneakyThrow(e);
}
- setGeneratedBy(result, source);
+
+ if (source != null) setGeneratedBy(result, source);
return result;
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
new file mode 100644
index 00000000..df8bd665
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
@@ -0,0 +1,339 @@
+/*
+ * 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.eclipse.handlers;
+
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.SpiLoadUtil;
+import lombok.core.TypeLibrary;
+import lombok.eclipse.EclipseNode;
+
+public class EclipseSingularsRecipes {
+ private static final EclipseSingularsRecipes INSTANCE = new EclipseSingularsRecipes();
+ private final Map<String, EclipseSingularizer> singularizers = new HashMap<String, EclipseSingularizer>();
+ private final TypeLibrary singularizableTypes = new TypeLibrary();
+
+ private EclipseSingularsRecipes() {
+ 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, EclipseSingularizer> map) throws IOException {
+ for (EclipseSingularizer handler : SpiLoadUtil.findServices(EclipseSingularizer.class, EclipseSingularizer.class.getClassLoader())) {
+ for (String type : handler.getSupportedTypes()) {
+ EclipseSingularizer existingSingularizer = map.get(type);
+ if (existingSingularizer != null) {
+ EclipseSingularizer 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 EclipseSingularsRecipes get() {
+ return INSTANCE;
+ }
+
+ public String toQualified(String typeReference) {
+ return singularizableTypes.toQualified(typeReference);
+ }
+
+ public EclipseSingularizer getSingularizer(String fqn) {
+ return singularizers.get(fqn);
+ }
+
+ public static final class SingularData {
+ private final EclipseNode annotation;
+ private final char[] singularName;
+ private final char[] pluralName;
+ private final List<TypeReference> typeArgs;
+ private final String targetFqn;
+ private final EclipseSingularizer singularizer;
+ private final ASTNode source;
+
+ public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List<TypeReference> typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source) {
+ this.annotation = annotation;
+ this.singularName = singularName;
+ this.pluralName = pluralName;
+ this.typeArgs = typeArgs;
+ this.targetFqn = targetFqn;
+ this.singularizer = singularizer;
+ this.source = source;
+ }
+
+ public void setGeneratedByRecursive(ASTNode target) {
+ SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(source);
+
+ if (target instanceof AbstractMethodDeclaration) {
+ ((AbstractMethodDeclaration) target).traverse(visitor, (ClassScope) null);
+ } else if (target instanceof FieldDeclaration) {
+ ((FieldDeclaration) target).traverse(visitor, (MethodScope) null);
+ } else {
+ target.traverse(visitor, null);
+ }
+ }
+
+ public EclipseNode getAnnotation() {
+ return annotation;
+ }
+
+ public char[] getSingularName() {
+ return singularName;
+ }
+
+ public char[] getPluralName() {
+ return pluralName;
+ }
+
+ public List<TypeReference> getTypeArgs() {
+ return typeArgs;
+ }
+
+ public String getTargetFqn() {
+ return targetFqn;
+ }
+
+ public EclipseSingularizer getSingularizer() {
+ return singularizer;
+ }
+
+ public String getTargetSimpleType() {
+ int idx = targetFqn.lastIndexOf(".");
+ return idx == -1 ? targetFqn : targetFqn.substring(idx + 1);
+ }
+ }
+
+ public static abstract class EclipseSingularizer {
+ protected static final long[] NULL_POSS = {0L};
+ 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(EclipseNode builderType, SingularData data) {
+ for (EclipseNode child : builderType.down()) {
+ switch (child.getKind()) {
+ case FIELD: {
+ FieldDeclaration fd = (FieldDeclaration) child.get();
+ char[] name = fd.name;
+ if (name == null) continue;
+ if (getGeneratedBy(fd) != null) continue;
+ for (char[] fieldToBeGenerated : listFieldsToBeGenerated(data, builderType)) {
+ if (!Arrays.equals(name, fieldToBeGenerated)) 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: {
+ AbstractMethodDeclaration method = (AbstractMethodDeclaration) child.get();
+ char[] name = method.selector;
+ if (name == null) continue;
+ if (getGeneratedBy(method) != null) continue;
+ for (char[] methodToBeGenerated : listMethodsToBeGenerated(data, builderType)) {
+ if (!Arrays.equals(name, methodToBeGenerated)) 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 List<char[]> listFieldsToBeGenerated(SingularData data, EclipseNode builderType) {
+ return Collections.singletonList(data.pluralName);
+ }
+
+ public List<char[]> listMethodsToBeGenerated(SingularData data, EclipseNode builderType) {
+ char[] p = data.pluralName;
+ char[] s = data.singularName;
+ if (Arrays.equals(p, s)) return Collections.singletonList(p);
+ return Arrays.asList(p, s);
+ }
+
+ public abstract List<EclipseNode> generateFields(SingularData data, EclipseNode builderType);
+ public abstract void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain);
+ public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName);
+
+ public boolean requiresCleaning() {
+ try {
+ return !getClass().getMethod("appendCleaningCode", SingularData.class, EclipseNode.class, List.class).getDeclaringClass().equals(EclipseSingularizer.class);
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ public void appendCleaningCode(SingularData data, EclipseNode builderType, List<Statement> 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 TypeReference addTypeArgs(int count, boolean addExtends, EclipseNode node, TypeReference type, List<TypeReference> typeArgs) {
+ if (count < 0) throw new IllegalArgumentException("count is negative");
+ if (count == 0) return type;
+ List<TypeReference> arguments = new ArrayList<TypeReference>();
+
+ if (typeArgs != null) for (TypeReference orig : typeArgs) {
+ Wildcard wildcard = orig instanceof Wildcard ? (Wildcard) orig : null;
+ if (!addExtends) {
+ if (wildcard != null && (wildcard.kind == Wildcard.UNBOUND || wildcard.kind == Wildcard.SUPER)) {
+ arguments.add(new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS));
+ } else if (wildcard != null && wildcard.kind == Wildcard.EXTENDS) {
+ try {
+ arguments.add(copyType(wildcard.bound));
+ } catch (Exception e) {
+ arguments.add(new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS));
+ }
+ } else {
+ arguments.add(copyType(orig));
+ }
+ } else {
+ if (wildcard != null && (wildcard.kind == Wildcard.UNBOUND || wildcard.kind == Wildcard.SUPER)) {
+ Wildcard w = new Wildcard(Wildcard.UNBOUND);
+ arguments.add(w);
+ } else if (wildcard != null && wildcard.kind == Wildcard.EXTENDS) {
+ arguments.add(copyType(orig));
+ } else {
+ Wildcard w = new Wildcard(Wildcard.EXTENDS);
+ w.bound = copyType(orig);
+ arguments.add(w);
+ }
+ }
+ if (--count == 0) break;
+ }
+
+ while (count-- > 0) {
+ if (addExtends) {
+ arguments.add(new Wildcard(Wildcard.UNBOUND));
+ } else {
+ arguments.add(new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS));
+ }
+ }
+
+ if (type instanceof SingleTypeReference) {
+ type = new ParameterizedSingleTypeReference(((SingleTypeReference) type).token, arguments.toArray(new TypeReference[arguments.size()]), 0, 0L);
+ } else if (type instanceof QualifiedTypeReference) {
+ QualifiedTypeReference qtr = (QualifiedTypeReference) type;
+ TypeReference[][] trs = new TypeReference[qtr.tokens.length][];
+ trs[qtr.tokens.length - 1] = arguments.toArray(new TypeReference[arguments.size()]);
+ type = new ParameterizedQualifiedTypeReference(((QualifiedTypeReference) type).tokens, trs, 0, NULL_POSS);
+ } else {
+ node.addError("Don't know how to clone-and-parameterize type: " + type);
+ }
+
+ return type;
+ }
+
+ private static final char[] SIZE_TEXT = new char[] {'s', 'i', 'z', 'e'};
+
+ /** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */
+ protected Expression getSize(EclipseNode builderType, char[] name, boolean nullGuard) {
+ MessageSend invoke = new MessageSend();
+ ThisReference thisRef = new ThisReference(0, 0);
+ FieldReference thisDotName = new FieldReference(name, 0L);
+ thisDotName.receiver = thisRef;
+ invoke.receiver = thisDotName;
+ invoke.selector = SIZE_TEXT;
+ if (!nullGuard) return invoke;
+
+ ThisReference cdnThisRef = new ThisReference(0, 0);
+ FieldReference cdnThisDotName = new FieldReference(name, 0L);
+ cdnThisDotName.receiver = cdnThisRef;
+ NullLiteral nullLiteral = new NullLiteral(0, 0);
+ EqualExpression isNull = new EqualExpression(cdnThisDotName, nullLiteral, OperatorIds.EQUAL_EQUAL);
+ IntLiteral zeroLiteral = makeIntLiteral(new char[] {'0'}, null);
+ ConditionalExpression conditional = new ConditionalExpression(isNull, zeroLiteral, invoke);
+ return conditional;
+ }
+
+ protected TypeReference cloneParamType(int index, List<TypeReference> typeArgs, EclipseNode builderType) {
+ if (typeArgs != null && typeArgs.size() > index) {
+ TypeReference originalType = typeArgs.get(index);
+ if (originalType instanceof Wildcard) {
+ Wildcard wOriginalType = (Wildcard) originalType;
+ if (wOriginalType.kind == Wildcard.EXTENDS) {
+ try {
+ return copyType(wOriginalType.bound);
+ } catch (Exception e) {
+ // fallthrough
+ }
+ }
+ } else {
+ return copyType(originalType);
+ }
+ }
+
+ return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS);
+ }
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 522501f6..498808bc 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/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
@@ -32,50 +32,89 @@ import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.mangosdk.spi.ProviderFor;
import lombok.AccessLevel;
+import lombok.Builder;
import lombok.ConfigurationKeys;
+import lombok.Singular;
import lombok.core.AST.Kind;
+import lombok.core.handlers.HandlerUtil;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
-import lombok.experimental.Builder;
import lombok.experimental.NonFinal;
@ProviderFor(EclipseAnnotationHandler.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 EclipseAnnotationHandler<Builder> {
+ private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray();
+ private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray();
+
+ private static final boolean toBoolean(Object expr, boolean defaultValue) {
+ if (expr == null) return defaultValue;
+ if (expr instanceof FalseLiteral) return false;
+ if (expr instanceof TrueLiteral) return true;
+ return ((Boolean) expr).booleanValue();
+ }
+
+ private static class BuilderFieldData {
+ TypeReference type;
+ char[] name;
+ SingularData singularData;
+
+ List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
+ }
+
@Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
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();
@@ -92,21 +131,21 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
EclipseNode parent = annotationNode.up();
- List<TypeReference> typesOfParameters = new ArrayList<TypeReference>();
- List<char[]> namesOfParameters = new ArrayList<char[]>();
+ List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
TypeReference returnType;
TypeParameter[] typeParams;
TypeReference[] thrownExceptions;
char[] nameOfStaticBuilderMethod;
EclipseNode tdParent;
- AbstractMethodDeclaration fillParametersFrom = null;
+ EclipseNode fillParametersFrom = parent.get() instanceof AbstractMethodDeclaration ? parent : null;
+ boolean addCleaning = false;
if (parent.get() instanceof TypeDeclaration) {
tdParent = parent;
TypeDeclaration td = (TypeDeclaration) tdParent.get();
- List<EclipseNode> fields = new ArrayList<EclipseNode>();
+ List<EclipseNode> allFields = new ArrayList<EclipseNode>();
@SuppressWarnings("deprecation")
boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
@@ -115,12 +154,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<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.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
- namesOfParameters.add(removePrefixFromField(fieldNode));
- typesOfParameters.add(fd.type);
- fields.add(fieldNode);
+ BuilderFieldData bfd = new BuilderFieldData();
+ bfd.name = removePrefixFromField(fieldNode);
+ bfd.type = fd.type;
+ bfd.singularData = getSingularData(fieldNode, ast);
+ builderFields.add(bfd);
+ allFields.add(fieldNode);
}
- new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, fields, null, SkipIfConstructorExists.I_AM_BUILDER, null,
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, null, SkipIfConstructorExists.I_AM_BUILDER, null,
Collections.<Annotation>emptyList(), annotationNode);
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
@@ -137,7 +179,6 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
tdParent = parent.up();
TypeDeclaration td = (TypeDeclaration) tdParent.get();
- fillParametersFrom = cd;
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
typeParams = td.typeParameters;
thrownExceptions = cd.thrownExceptions;
@@ -150,7 +191,6 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
return;
}
- fillParametersFrom = md;
returnType = copyType(md.returnType, ast);
typeParams = md.typeParameters;
thrownExceptions = md.thrownExceptions;
@@ -190,9 +230,14 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
if (fillParametersFrom != null) {
- if (fillParametersFrom.arguments != null) for (Argument a : fillParametersFrom.arguments) {
- namesOfParameters.add(a.name);
- typesOfParameters.add(a.type);
+ for (EclipseNode param : fillParametersFrom.down()) {
+ if (param.getKind() != Kind.ARGUMENT) continue;
+ BuilderFieldData bfd = new BuilderFieldData();
+ Argument arg = (Argument) param.get();
+ bfd.name = arg.name;
+ bfd.type = arg.type;
+ bfd.singularData = getSingularData(param, ast);
+ builderFields.add(bfd);
}
}
@@ -201,12 +246,36 @@ public class HandleBuilder extends EclipseAnnotationHandler<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;
+ EclipseSingularizer singularizer = sd.getSingularizer();
+ if (singularizer == null) continue;
+ if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) {
+ bfd.singularData = null;
+ }
+ }
+ }
}
- List<EclipseNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
- List<AbstractMethodDeclaration> newMethods = new ArrayList<AbstractMethodDeclaration>();
- for (EclipseNode fieldNode : fieldNodes) {
- MethodDeclaration newMethod = makeSetterMethodForBuilder(builderType, fieldNode, annotationNode, builderInstance.fluent(), builderInstance.chain());
- if (newMethod != null) newMethods.add(newMethod);
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ if (bfd.singularData.getSingularizer().requiresCleaning()) {
+ addCleaning = true;
+ break;
+ }
+ }
+ }
+
+ generateBuilderFields(builderType, builderFields, ast);
+ if (addCleaning) {
+ FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1);
+ cleanDecl.declarationSourceEnd = -1;
+ cleanDecl.modifiers = ClassFileConstants.AccPrivate;
+ cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
+ System.out.println("INJECTING: cleaning");
+ injectField(builderType, cleanDecl);
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
@@ -216,128 +285,183 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (cd != null) injectMethod(builderType, cd);
}
- for (AbstractMethodDeclaration newMethod : newMethods) injectMethod(builderType, newMethod);
+ for (BuilderFieldData bfd : builderFields) {
+ makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain);
+ }
+
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, ast, thrownExceptions);
+ MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
if (md != null) injectMethod(builderType, md);
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ List<EclipseNode> fieldNodes = new ArrayList<EclipseNode>();
+ for (BuilderFieldData bfd : builderFields) {
+ fieldNodes.addAll(bfd.createdFields);
+ }
MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
if (md != null) injectMethod(builderType, md);
}
+ if (addCleaning) {
+ MethodDeclaration cleanMethod = generateCleanMethod(builderFields, builderType, ast);
+ if (cleanMethod != null) injectMethod(builderType, cleanMethod);
+ }
+
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
if (md != null) injectMethod(tdParent, md);
}
}
- public MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
- int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long) pS << 32 | pE;
+ private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) {
+ List<Statement> statements = new ArrayList<Statement>();
- MethodDeclaration out = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
- out.selector = builderMethodName.toCharArray();
- out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
- out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
- out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
- out.typeParameters = copyTypeParams(typeParams, source);
- AllocationExpression invoke = new AllocationExpression();
- invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
- out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)};
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements);
+ }
+ }
- out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
- return out;
+ FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0);
+ thisUnclean.receiver = new ThisReference(0, 0);
+ statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0));
+ MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ decl.selector = CLEAN_METHOD_NAME;
+ decl.modifiers = ClassFileConstants.AccPrivate;
+ decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ decl.statements = statements.toArray(new Statement[0]);
+ decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return decl;
}
- public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<char[]> fieldNames, EclipseNode type, ASTNode source, TypeReference[] thrownExceptions) {
- int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long) pS << 32 | pE;
-
+ public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) {
MethodDeclaration out = new MethodDeclaration(
((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ List<Statement> statements = new ArrayList<Statement>();
+
+ if (addCleaning) {
+ FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0);
+ thisUnclean.receiver = new ThisReference(0, 0);
+ Expression notClean = new UnaryExpression(thisUnclean, OperatorIds.NOT);
+ MessageSend invokeClean = new MessageSend();
+ invokeClean.selector = CLEAN_METHOD_NAME;
+ statements.add(new IfStatement(notClean, invokeClean, 0, 0));
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name);
+ }
+ }
+
+ List<Expression> args = new ArrayList<Expression>();
+ for (BuilderFieldData bfd : builderFields) {
+ args.add(new SingleNameReference(bfd.name, 0L));
+ }
+
+ if (addCleaning) {
+ FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0);
+ thisUnclean.receiver = new ThisReference(0, 0);
+ statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0));
+ }
out.modifiers = ClassFileConstants.AccPublic;
- TypeDeclaration typeDecl = (TypeDeclaration) type.get();
out.selector = name.toCharArray();
- out.thrownExceptions = copyTypes(thrownExceptions, source);
+ out.thrownExceptions = copyTypes(thrownExceptions);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
out.returnType = returnType;
- List<Expression> assigns = new ArrayList<Expression>();
- for (char[] fieldName : fieldNames) {
- SingleNameReference nameRef = new SingleNameReference(fieldName, p);
- assigns.add(nameRef);
- }
-
- Statement statement;
-
if (staticName == null) {
AllocationExpression allocationStatement = new AllocationExpression();
- allocationStatement.type = copyType(out.returnType, source);
- allocationStatement.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
- statement = new ReturnStatement(allocationStatement, (int)(p >> 32), (int)p);
+ allocationStatement.type = copyType(out.returnType);
+ allocationStatement.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]);
+ statements.add(new ReturnStatement(allocationStatement, 0, 0));
} else {
MessageSend invoke = new MessageSend();
invoke.selector = staticName;
- invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), p);
+ invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0);
TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters;
if (tps != null) {
TypeReference[] trs = new TypeReference[tps.length];
for (int i = 0; i < trs.length; i++) {
- trs[i] = new SingleTypeReference(tps[i].name, p);
+ trs[i] = new SingleTypeReference(tps[i].name, 0);
}
invoke.typeArguments = trs;
}
- invoke.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
+ invoke.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]);
if (returnType instanceof SingleTypeReference && Arrays.equals(TypeConstants.VOID, ((SingleTypeReference) returnType).token)) {
- statement = invoke;
+ statements.add(invoke);
} else {
- statement = new ReturnStatement(invoke, (int)(p >> 32), (int)p);
+ statements.add(new ReturnStatement(invoke, 0, 0));
}
}
+ out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[statements.size()]);
+ out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return out;
+ }
+
+ public MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
- out.statements = new Statement[] { statement };
+ MethodDeclaration out = new MethodDeclaration(
+ ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ out.selector = builderMethodName.toCharArray();
+ out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+ out.typeParameters = copyTypeParams(typeParams, source);
+ AllocationExpression invoke = new AllocationExpression();
+ invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+ out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)};
- out.traverse(new SetGeneratedByVisitor(source), typeDecl.scope);
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
}
- public List<EclipseNode> addFieldsToBuilder(EclipseNode builderType, List<char[]> namesOfParameters, List<TypeReference> typesOfParameters, ASTNode source) {
- int len = namesOfParameters.size();
- TypeDeclaration td = (TypeDeclaration) builderType.get();
- FieldDeclaration[] existing = td.fields;
- if (existing == null) existing = new FieldDeclaration[0];
-
- List<EclipseNode> out = new ArrayList<EclipseNode>();
+ public void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData> builderFields, ASTNode source) {
+ List<EclipseNode> existing = new ArrayList<EclipseNode>();
+ for (EclipseNode child : builderType.down()) {
+ if (child.getKind() == Kind.FIELD) existing.add(child);
+ }
top:
- for (int i = len - 1; i >= 0; i--) {
- char[] name = namesOfParameters.get(i);
- for (FieldDeclaration exists : existing) {
- if (Arrays.equals(exists.name, name)) {
- out.add(builderType.getNodeFor(exists));
- continue top;
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType));
+ } else {
+ for (EclipseNode exists : existing) {
+ char[] n = ((FieldDeclaration) exists.get()).name;
+ if (Arrays.equals(n, bfd.name)) {
+ bfd.createdFields.add(exists);
+ continue top;
+ }
}
+
+ FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0);
+ fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fd.modifiers = ClassFileConstants.AccPrivate;
+ fd.type = copyType(bfd.type);
+ fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
+ bfd.createdFields.add(injectField(builderType, fd));
}
- TypeReference fieldReference = copyType(typesOfParameters.get(i), source);
- FieldDeclaration newField = new FieldDeclaration(name, 0, 0);
- newField.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
- newField.modifiers = ClassFileConstants.AccPrivate;
- newField.type = fieldReference;
- out.add(injectField(builderType, newField));
}
-
- Collections.reverse(out);
-
- return out;
}
private static final AbstractMethodDeclaration[] EMPTY = {};
- public MethodDeclaration makeSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, EclipseNode sourceNode, boolean fluent, boolean chain) {
+ public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain) {
+ if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
+ makeSimpleSetterMethodForBuilder(builderType, bfd.createdFields.get(0), sourceNode, fluent, chain);
+ } else {
+ bfd.singularData.getSingularizer().generateMethods(bfd.singularData, builderType, fluent, chain);
+ }
+ }
+
+ private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, EclipseNode sourceNode, boolean fluent, boolean chain) {
TypeDeclaration td = (TypeDeclaration) builderType.get();
AbstractMethodDeclaration[] existing = td.methods;
if (existing == null) existing = EMPTY;
@@ -348,14 +472,14 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
for (int i = 0; i < len; i++) {
if (!(existing[i] instanceof MethodDeclaration)) continue;
char[] existingName = existing[i].selector;
- if (Arrays.equals(name, existingName)) return null;
+ if (Arrays.equals(name, existingName)) return;
}
- boolean isBoolean = isBoolean(fd.type);
- String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean);
+ String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
- return HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
+ MethodDeclaration setter = HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ injectMethod(builderType, setter);
}
public EclipseNode findInnerClass(EclipseNode parent, String name) {
@@ -378,4 +502,64 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
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(EclipseNode node, ASTNode source) {
+ for (EclipseNode child : node.down()) {
+ if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Singular.class, child)) {
+ char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name;
+ AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
+ 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 = new String(pluralName);
+ } 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 = new String(pluralName);
+ }
+ }
+ }
+ char[] singularName = explicitSingular.toCharArray();
+
+ TypeReference type = ((AbstractVariableDeclaration) node.get()).type;
+ TypeReference[] typeArgs = null;
+ String typeName;
+ if (type instanceof ParameterizedSingleTypeReference) {
+ typeArgs = ((ParameterizedSingleTypeReference) type).typeArguments;
+ typeName = new String(((ParameterizedSingleTypeReference) type).token);
+ } else if (type instanceof ParameterizedQualifiedTypeReference) {
+ TypeReference[][] tr = ((ParameterizedQualifiedTypeReference) type).typeArguments;
+ if (tr != null) typeArgs = tr[tr.length - 1];
+ char[][] tokens = ((ParameterizedQualifiedTypeReference) type).tokens;
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < tokens.length; i++) {
+ if (i > 0) sb.append(".");
+ sb.append(tokens[i]);
+ }
+ typeName = sb.toString();
+ } else {
+ typeName = type.toString();
+ }
+
+ String targetFqn = EclipseSingularsRecipes.get().toQualified(typeName);
+ EclipseSingularizer singularizer = EclipseSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + typeName + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source);
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java
index b72d000f..d19e95e4 100644
--- a/src/core/lombok/eclipse/handlers/HandleConstructor.java
+++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java
@@ -33,6 +33,7 @@ import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.ConfigurationKeys;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
@@ -40,7 +41,6 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.experimental.Builder;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
diff --git a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
index 7217a396..df839a94 100644
--- a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
+++ b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
@@ -23,6 +23,8 @@ package lombok.eclipse.handlers;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+import java.util.Arrays;
+
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
@@ -128,881 +130,802 @@ public final class SetGeneratedByVisitor extends ASTVisitor {
private static final long INT_TO_LONG_MASK = 0x00000000FFFFFFFFL;
private final ASTNode source;
- private final int newSourceStart;
- private final int newSourceEnd;
+ private final int sourceStart;
+ private final int sourceEnd;
+ private final long sourcePos;
public SetGeneratedByVisitor(ASTNode source) {
this.source = source;
- this.newSourceStart = this.source.sourceStart;
- this.newSourceEnd = this.source.sourceEnd;
+ this.sourceStart = this.source.sourceStart;
+ this.sourceEnd = this.source.sourceEnd;
+ this.sourcePos = (long)sourceStart << 32 | (sourceEnd & INT_TO_LONG_MASK);
}
- private void applyOffset(JavadocAllocationExpression node) {
- applyOffsetExpression(node);
- node.memberStart = newSourceStart;
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocAllocationExpression node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.memberStart = sourceStart;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
}
- private void applyOffset(JavadocMessageSend node) {
- applyOffsetMessageSend(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocMessageSend node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.nameSourcePosition = sourcePos;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
}
- private void applyOffset(JavadocSingleNameReference node) {
- applyOffsetExpression(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocSingleNameReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
}
- private void applyOffset(JavadocSingleTypeReference node) {
- applyOffsetExpression(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocSingleTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
}
- private void applyOffset(JavadocFieldReference node) {
- applyOffsetFieldReference(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocFieldReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.nameSourcePosition = sourcePos;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
+ }
+
+ private void fixPositions(JavadocArrayQualifiedTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
+ }
+
+ private void fixPositions(JavadocQualifiedTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
+ }
+
+ private void fixPositions(Annotation node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
}
-
- private void applyOffset(JavadocArrayQualifiedTypeReference node) {
- applyOffsetQualifiedTypeReference(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+
+ private void fixPositions(ArrayTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.originalSourceEnd = sourceEnd;
}
- private void applyOffset(JavadocQualifiedTypeReference node) {
- applyOffsetQualifiedTypeReference(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(AbstractMethodDeclaration node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.bodyEnd = sourceEnd;
+ node.bodyStart = sourceStart;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
}
- private void applyOffset(Annotation node) {
- applyOffsetExpression(node);
- node.declarationSourceEnd = newSourceEnd;
+ private void fixPositions(Javadoc node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.valuePositions = sourceStart;
}
- private void applyOffset(ArrayTypeReference node) {
- applyOffsetExpression(node);
- node.originalSourceEnd = newSourceEnd;
+ private void fixPositions(Initializer node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.declarationEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
+ node.endPart1Position = sourceEnd;
+ node.endPart2Position = sourceEnd;
+ node.bodyStart = sourceStart;
+ node.bodyEnd = sourceEnd;
}
-
- private void applyOffset(AbstractMethodDeclaration node) {
- applyOffsetASTNode(node);
- node.bodyEnd = newSourceEnd;
- node.bodyStart = newSourceStart;
- node.declarationSourceEnd = newSourceEnd;
- node.declarationSourceStart = newSourceStart;
- node.modifiersSourceStart = newSourceStart;
+
+ private void fixPositions(TypeDeclaration node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.bodyEnd = sourceEnd;
+ node.bodyStart = sourceStart;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
}
- private void applyOffset(Javadoc node) {
- applyOffsetASTNode(node);
- node.valuePositions = newSourceStart;
- for (int i = 0; i < node.inheritedPositions.length; i++) {
- node.inheritedPositions[i] = recalcSourcePosition(node.inheritedPositions[i]);
- }
+ private void fixPositions(ImportReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.declarationEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
}
-
- private void applyOffset(Initializer node) {
- applyOffsetFieldDeclaration(node);
- node.bodyStart = newSourceStart;
- node.bodyEnd = newSourceEnd;
- }
-
- private void applyOffset(TypeDeclaration node) {
- applyOffsetASTNode(node);
- node.bodyEnd = newSourceEnd;
- node.bodyStart = newSourceStart;
- node.declarationSourceEnd = newSourceEnd;
- node.declarationSourceStart = newSourceStart;
- node.modifiersSourceStart = newSourceStart;
- }
-
- private void applyOffset(ImportReference node) {
- applyOffsetASTNode(node);
- node.declarationEnd = newSourceEnd;
- node.declarationSourceEnd = newSourceEnd;
- node.declarationSourceStart = newSourceStart;
- for (int i = 0; i < node.sourcePositions.length; i++) {
- node.sourcePositions[i] = recalcSourcePosition(node.sourcePositions[i]);
- }
+
+ private void fixPositions(ASTNode node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
}
-
- private void applyOffsetASTNode(ASTNode node) {
- node.sourceEnd = newSourceEnd;
- node.sourceStart = newSourceStart;
+
+ private void fixPositions(SwitchStatement node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.blockStart = sourceStart;
}
-
- private void applyOffsetExpression(Expression node) {
- applyOffsetASTNode(node);
-// if (node.statementEnd != -1) {
- node.statementEnd = newSourceEnd;
-// }
+
+ private void fixPositions(Expression node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
}
-
- private void applyOffsetVariable(AbstractVariableDeclaration node) {
- applyOffsetASTNode(node);
- node.declarationEnd = newSourceEnd;
- node.declarationSourceEnd = newSourceEnd;
- node.declarationSourceStart = newSourceStart;
- node.modifiersSourceStart = newSourceStart;
+
+ private void fixPositions(AbstractVariableDeclaration node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.declarationEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
}
- private void applyOffsetFieldDeclaration(FieldDeclaration node) {
- applyOffsetVariable(node);
- node.endPart1Position = newSourceEnd;
- node.endPart2Position = newSourceEnd;
+ private void fixPositions(FieldDeclaration node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.declarationEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
+ node.endPart1Position = sourceEnd;
+ node.endPart2Position = sourceEnd;
}
- private void applyOffsetFieldReference(FieldReference node) {
- applyOffsetExpression(node);
- node.nameSourcePosition = recalcSourcePosition(node.nameSourcePosition);
+ private void fixPositions(FieldReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.nameSourcePosition = sourcePos;
}
- private void applyOffsetMessageSend(MessageSend node) {
- applyOffsetExpression(node);
- node.nameSourcePosition = recalcSourcePosition(node.nameSourcePosition);
+ private void fixPositions(MessageSend node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.nameSourcePosition = sourcePos;
}
- private void applyOffsetQualifiedNameReference(QualifiedNameReference node) {
- applyOffsetExpression(node);
- for (int i = 0; i < node.sourcePositions.length; i++) {
- node.sourcePositions[i] = recalcSourcePosition(node.sourcePositions[i]);
- }
+ private void fixPositions(QualifiedNameReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
}
- private void applyOffsetQualifiedTypeReference(QualifiedTypeReference node) {
- applyOffsetExpression(node);
- for (int i = 0; i < node.sourcePositions.length; i++) {
- node.sourcePositions[i] = recalcSourcePosition(node.sourcePositions[i]);
- }
- }
-
- /** See {@link FieldReference#nameSourcePosition} for explanation */
- private long recalcSourcePosition(long sourcePosition) {
-// long start = (sourcePosition >>> 32);
-// long end = (sourcePosition & 0x00000000FFFFFFFFL);
-// start = newSourceStart;
-// end = newSourceStart;
-// return ((start<<32)+end);
- return ((long)newSourceStart << 32) | (newSourceEnd & INT_TO_LONG_MASK);
+ private void fixPositions(QualifiedTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
}
@Override public boolean visit(AllocationExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
-
+
@Override public boolean visit(AND_AND_Expression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(AnnotationMethodDeclaration node, ClassScope classScope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, classScope);
}
@Override public boolean visit(Argument node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Argument node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayAllocationExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayInitializer node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayQualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayQualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(AssertStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Assignment node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(BinaryExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Block node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(BreakStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CaseStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CastExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CharLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ClassLiteralAccess node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Clinit node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CompilationUnitDeclaration node, CompilationUnitScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CompoundAssignment node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ConditionalExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ConstructorDeclaration node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ContinueStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(DoStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(DoubleLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(EmptyStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(EqualExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ExplicitConstructorCall node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ExtendedStringLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FalseLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FieldDeclaration node, MethodScope scope) {
- setGeneratedBy(node, source);
- applyOffsetFieldDeclaration(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FieldReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetFieldReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FieldReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetFieldReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FloatLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ForeachStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ForStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(IfStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ImportReference node, CompilationUnitScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Initializer node, MethodScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(InstanceOfExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(IntLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Javadoc node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Javadoc node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocAllocationExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocAllocationExpression node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArgumentExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArgumentExpression node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArrayQualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArrayQualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArraySingleTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArraySingleTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocFieldReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocFieldReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocImplicitTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocImplicitTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocMessageSend node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocMessageSend node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocQualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocQualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocReturnStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocReturnStatement node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocSingleNameReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocSingleNameReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocSingleTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocSingleTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(LabeledStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(LocalDeclaration node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(LongLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(MarkerAnnotation node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(MemberValuePair node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(MessageSend node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetMessageSend(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(MethodDeclaration node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(StringLiteralConcatenation node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(NormalAnnotation node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(NullLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(OR_OR_Expression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ParameterizedQualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ParameterizedQualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ParameterizedSingleTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ParameterizedSingleTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(PostfixExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(PrefixExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedAllocationExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedNameReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedNameReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedNameReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedSuperReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedSuperReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedThisReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedThisReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ReturnStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleMemberAnnotation node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleNameReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleNameReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(StringLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SuperReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SwitchStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
- node.blockStart = newSourceStart;
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SynchronizedStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ThisReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ThisReference node, ClassScope scope) {
- setGeneratedBy(node, source);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ThrowStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TrueLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TryStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeDeclaration node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeDeclaration node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeDeclaration node, CompilationUnitScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeParameter node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeParameter node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(UnaryExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(WhileStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Wildcard node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Wildcard node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
} \ No newline at end of file
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java
new file mode 100644
index 00000000..95fd8935
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.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.eclipse.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseGuavaMapSingularizer extends EclipseGuavaSingularizer {
+ // 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/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java
new file mode 100644
index 00000000..bc2893bf
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.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.eclipse.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseGuavaSetListSingularizer extends EclipseGuavaSingularizer {
+ // 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/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
new file mode 100644
index 00000000..8d54da6f
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
@@ -0,0 +1,246 @@
+/*
+ * 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.eclipse.handlers.singulars;
+
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import lombok.core.GuavaTypeMap;
+import lombok.core.handlers.HandlerUtil;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
+ protected static final char[][] JAVA_UTIL_MAP = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'}
+ };
+
+ protected String getSimpleTargetTypeName(SingularData data) {
+ return GuavaTypeMap.getGuavaTypeName(data.getTargetFqn());
+ }
+
+ protected char[] getBuilderMethodName(SingularData data) {
+ String simpleTypeName = getSimpleTargetTypeName(data);
+ if ("ImmutableSortedSet".equals(simpleTypeName) || "ImmutableSortedMap".equals(simpleTypeName)) return "naturalOrder".toCharArray();
+ return "builder".toCharArray();
+ }
+
+ protected abstract boolean isMap();
+
+ protected char[][] makeGuavaTypeName(String simpleName, boolean addBuilder) {
+ char[][] tokenizedName = new char[addBuilder ? 6 : 5][];
+ tokenizedName[0] = new char[] {'c', 'o', 'm'};
+ tokenizedName[1] = new char[] {'g', 'o', 'o', 'g', 'l', 'e'};
+ tokenizedName[2] = new char[] {'c', 'o', 'm', 'm', 'o', 'n'};
+ tokenizedName[3] = new char[] {'c', 'o', 'l', 'l', 'e', 'c', 't'};
+ tokenizedName[4] = simpleName.toCharArray();
+ if (addBuilder) tokenizedName[5] = new char[] { 'B', 'u', 'i', 'l', 'd', 'e', 'r'};
+ return tokenizedName;
+ }
+
+ @Override public List<EclipseNode> generateFields(SingularData data, EclipseNode builderType) {
+ char[][] tokenizedName = makeGuavaTypeName(getSimpleTargetTypeName(data), true);
+ TypeReference type = new QualifiedTypeReference(tokenizedName, NULL_POSS);
+ type = addTypeArgs(isMap() ? 2 : 1, false, builderType, type, data.getTypeArgs());
+
+ FieldDeclaration buildField = new FieldDeclaration(data.getPluralName(), 0, -1);
+ buildField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ buildField.modifiers = ClassFileConstants.AccPrivate;
+ buildField.declarationSourceEnd = -1;
+ buildField.type = type;
+ data.setGeneratedByRecursive(buildField);
+ return Collections.singletonList(injectField(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
+ TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
+ }
+
+ void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ boolean mapMode = isMap();
+ char[] keyName = !mapMode ? data.getSingularName() : (new String(data.getSingularName()) + "$key").toCharArray();
+ char[] valueName = !mapMode ? null : (new String(data.getSingularName()) + "$value").toCharArray();
+
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType));
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldDotAdd = new MessageSend();
+ if (mapMode) {
+ thisDotFieldDotAdd.arguments = new Expression[] {
+ new SingleNameReference(keyName, 0L),
+ new SingleNameReference(valueName, 0L)};
+ } else {
+ thisDotFieldDotAdd.arguments = new Expression[] {new SingleNameReference(keyName, 0L)};
+ }
+ thisDotFieldDotAdd.receiver = thisDotField;
+ thisDotFieldDotAdd.selector = (mapMode ? "put" : "add").toCharArray();
+ statements.add(thisDotFieldDotAdd);
+ if (returnStatement != null) statements.add(returnStatement);
+ md.statements = statements.toArray(new Statement[statements.size()]);
+
+ if (mapMode) {
+ TypeReference keyType = cloneParamType(0, data.getTypeArgs(), builderType);
+ Argument keyParam = new Argument(keyName, 0, keyType, 0);
+ TypeReference valueType = cloneParamType(1, data.getTypeArgs(), builderType);
+ Argument valueParam = new Argument(valueName, 0, valueType, 0);
+ md.arguments = new Argument[] {keyParam, valueParam};
+ } else {
+ TypeReference paramType = cloneParamType(0, data.getTypeArgs(), builderType);
+ Argument param = new Argument(keyName, 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ }
+ md.returnType = returnType;
+ md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(mapMode ? "put" : "add", new String(data.getSingularName())).toCharArray();
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ boolean mapMode = isMap();
+
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType));
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldDotAddAll = new MessageSend();
+ thisDotFieldDotAddAll.arguments = new Expression[] {new SingleNameReference(data.getPluralName(), 0L)};
+ thisDotFieldDotAddAll.receiver = thisDotField;
+ thisDotFieldDotAddAll.selector = (mapMode ? "putAll" : "addAll").toCharArray();
+ statements.add(thisDotFieldDotAddAll);
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+
+ TypeReference paramType;
+ if (mapMode) {
+ paramType = new QualifiedTypeReference(JAVA_UTIL_MAP, NULL_POSS);
+ paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs());
+ } else {
+ paramType = new QualifiedTypeReference(TypeConstants.JAVA_LANG_ITERABLE, NULL_POSS);
+ paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs());
+ }
+ Argument param = new Argument(data.getPluralName(), 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(mapMode ? "putAll" : "addAll", new String(data.getPluralName())).toCharArray();
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ boolean mapMode = isMap();
+ TypeReference varType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
+ varType = addTypeArgs(mapMode ? 2 : 1, false, builderType, varType, data.getTypeArgs());
+
+ MessageSend emptyInvoke; {
+ //ImmutableX.of()
+ emptyInvoke = new MessageSend();
+ emptyInvoke.selector = new char[] {'o', 'f'};
+ emptyInvoke.receiver = new QualifiedNameReference(makeGuavaTypeName(getSimpleTargetTypeName(data), false), NULL_POSS, 0, 0);
+ }
+
+ MessageSend invokeBuild; {
+ //this.pluralName.build();
+ invokeBuild = new MessageSend();
+ invokeBuild.selector = new char[] {'b', 'u', 'i', 'l', 'd'};
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ invokeBuild.receiver = thisDotField;
+ }
+
+ Expression isNull; {
+ //this.pluralName == null
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ isNull = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+ }
+
+ Expression init = new ConditionalExpression(isNull, emptyInvoke, invokeBuild);
+ LocalDeclaration varDefStat = new LocalDeclaration(data.getPluralName(), 0, 0);
+ varDefStat.type = varType;
+ varDefStat.initialization = init;
+ statements.add(varDefStat);
+ }
+
+ protected Statement createConstructBuilderVarIfNeeded(SingularData data, EclipseNode builderType) {
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ FieldReference thisDotField2 = new FieldReference(data.getPluralName(), 0L);
+ thisDotField2.receiver = new ThisReference(0, 0);
+ Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+
+ MessageSend createBuilderInvoke = new MessageSend();
+ char[][] tokenizedName = makeGuavaTypeName(getSimpleTargetTypeName(data), false);
+ createBuilderInvoke.receiver = new QualifiedNameReference(tokenizedName, NULL_POSS, 0, 0);
+ createBuilderInvoke.selector = getBuilderMethodName(data);
+ return new IfStatement(cond, new Assignment(thisDotField2, createBuilderInvoke, 0), 0, 0);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
new file mode 100644
index 00000000..1d1c4dbd
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
@@ -0,0 +1,158 @@
+/*
+ * 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.eclipse.handlers.singulars;
+
+import static lombok.eclipse.Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import lombok.core.handlers.HandlerUtil;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingularizer {
+ @Override public List<char[]> listFieldsToBeGenerated(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ return super.listFieldsToBeGenerated(data, builderType);
+ }
+
+ @Override public List<char[]> listMethodsToBeGenerated(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listMethodsToBeGenerated(data, builderType);
+ }
+
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+
+ @Override public List<EclipseNode> generateFields(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.generateFields(data, builderType);
+ }
+
+ TypeReference type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs());
+
+ FieldDeclaration buildField = new FieldDeclaration(data.getPluralName(), 0, -1);
+ buildField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ buildField.modifiers = ClassFileConstants.AccPrivate;
+ buildField.declarationSourceEnd = -1;
+ buildField.type = type;
+ data.setGeneratedByRecursive(buildField);
+ return Collections.singletonList(injectField(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.generateMethods(data, builderType, fluent, chain);
+ return;
+ }
+
+ TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
+ }
+
+ void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType, false));
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldDotAdd = new MessageSend();
+ thisDotFieldDotAdd.arguments = new Expression[] {new SingleNameReference(data.getSingularName(), 0L)};
+ thisDotFieldDotAdd.receiver = thisDotField;
+ thisDotFieldDotAdd.selector = "add".toCharArray();
+ statements.add(thisDotFieldDotAdd);
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+ TypeReference paramType = cloneParamType(0, data.getTypeArgs(), builderType);
+ Argument param = new Argument(data.getSingularName(), 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray();
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType, false));
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldDotAddAll = new MessageSend();
+ thisDotFieldDotAddAll.arguments = new Expression[] {new SingleNameReference(data.getPluralName(), 0L)};
+ thisDotFieldDotAddAll.receiver = thisDotField;
+ thisDotFieldDotAddAll.selector = "addAll".toCharArray();
+ statements.add(thisDotFieldDotAddAll);
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+
+ TypeReference paramType = new QualifiedTypeReference(TypeConstants.JAVA_UTIL_COLLECTION, NULL_POSS);
+ paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs());
+ Argument param = new Argument(data.getPluralName(), 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray();
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
new file mode 100644
index 00000000..0784ac4f
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
@@ -0,0 +1,136 @@
+/*
+ * 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.eclipse.handlers.singulars;
+
+import static lombok.eclipse.handlers.EclipseHandlerUtil.makeIntLiteral;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.BreakStatement;
+import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.mangosdk.spi.ProviderFor;
+
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.util.Iterable");
+ }
+
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ return;
+ }
+
+ List<Statement> switchContents = new ArrayList<Statement>();
+
+ /* case 0: (empty) break; */ {
+ switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'0'}, null), 0, 0));
+ MessageSend invoke = new MessageSend();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ invoke.selector = "emptyList".toCharArray();
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0));
+ switchContents.add(new BreakStatement(null, 0, 0));
+ }
+
+ /* case 1: (singleton) break; */ {
+ switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'1'}, null), 0, 0));
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldGet0 = new MessageSend();
+ thisDotFieldGet0.receiver = thisDotField;
+ thisDotFieldGet0.selector = new char[] {'g', 'e', 't'};
+ thisDotFieldGet0.arguments = new Expression[] {makeIntLiteral(new char[] {'0'}, null)};
+
+ Expression[] args = new Expression[] {thisDotFieldGet0};
+ MessageSend invoke = new MessageSend();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ invoke.selector = "singletonList".toCharArray();
+ invoke.arguments = args;
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0));
+ switchContents.add(new BreakStatement(null, 0, 0));
+ }
+
+ /* default: Create with right size, then add all */ {
+ switchContents.add(new CaseStatement(null, 0, 0));
+
+ /* pluralName = new j.u.ArrayList<Generics>(this.pluralName.size()); */ {
+ Expression[] args = new Expression[] {getSize(builderType, data.getPluralName(), false)};
+ TypeReference targetTypeExpr = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs());
+ AllocationExpression constructorCall = new AllocationExpression();
+ constructorCall.type = targetTypeExpr;
+ constructorCall.arguments = args;
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0L), constructorCall, 0));
+ }
+
+ /* pluralname.addAll(this.pluralname); */ {
+ FieldReference thisDotPluralName = new FieldReference(data.getPluralName(), 0L);
+ thisDotPluralName.receiver = new ThisReference(0, 0);
+ MessageSend addAllInvoke = new MessageSend();
+ addAllInvoke.receiver = new SingleNameReference(data.getPluralName(), 0L);
+ addAllInvoke.selector = new char[] { 'a', 'd', 'd', 'A', 'l', 'l' };
+ addAllInvoke.arguments = new Expression[] {thisDotPluralName};
+ switchContents.add(addAllInvoke);
+ }
+
+ /* pluralname = Collections.unmodifiableList(pluralname); */ {
+ MessageSend unmodInvoke = new MessageSend();
+ unmodInvoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ unmodInvoke.selector = "unmodifiableList".toCharArray();
+ unmodInvoke.arguments = new Expression[] {new SingleNameReference(data.getPluralName(), 0L)};
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), unmodInvoke, 0));
+ }
+ }
+
+ SwitchStatement switchStat = new SwitchStatement();
+ switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]);
+ switchStat.expression = getSize(builderType, data.getPluralName(), true);
+
+ TypeReference localShadowerType = new QualifiedTypeReference(JAVA_UTIL_LIST, NULL_POSS);
+ localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs());
+ LocalDeclaration varDefStat = new LocalDeclaration(data.getPluralName(), 0, 0);
+ varDefStat.type = localShadowerType;
+ statements.add(varDefStat);
+ statements.add(switchStat);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
new file mode 100644
index 00000000..640bd396
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
@@ -0,0 +1,270 @@
+/*
+ * 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.eclipse.handlers.singulars;
+
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.handlers.HandlerUtil;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Map", "java.util.SortedMap", "java.util.NavigableMap");
+ }
+
+ @Override public List<char[]> listFieldsToBeGenerated(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ char[] p = data.getPluralName();
+ int len = p.length;
+ char[] k = new char[len + 4];
+ char[] v = new char[len + 6];
+ System.arraycopy(p, 0, k, 0, len);
+ System.arraycopy(p, 0, v, 0, len);
+ k[len] = '$';
+ k[len + 1] = 'k';
+ k[len + 2] = 'e';
+ k[len + 3] = 'y';
+ v[len] = '$';
+ v[len + 1] = 'v';
+ v[len + 2] = 'a';
+ v[len + 3] = 'l';
+ v[len + 4] = 'u';
+ v[len + 5] = 'e';
+ return Arrays.asList(k, v);
+ }
+
+ @Override public List<char[]> listMethodsToBeGenerated(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listFieldsToBeGenerated(data, builderType);
+ } else {
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+ }
+
+ @Override public List<EclipseNode> generateFields(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.generateFields(data, builderType);
+ }
+
+ char[] keyName = (new String(data.getPluralName()) + "$key").toCharArray();
+ char[] valueName = (new String(data.getPluralName()) + "$value").toCharArray();
+ FieldDeclaration buildKeyField; {
+ TypeReference type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs());
+ buildKeyField = new FieldDeclaration(keyName, 0, -1);
+ buildKeyField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ buildKeyField.modifiers = ClassFileConstants.AccPrivate;
+ buildKeyField.declarationSourceEnd = -1;
+ buildKeyField.type = type;
+ }
+ FieldDeclaration buildValueField; {
+ TypeReference type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ List<TypeReference> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.size() > 1) tArgs = Collections.singletonList(tArgs.get(1));
+ else tArgs = Collections.emptyList();
+ type = addTypeArgs(1, false, builderType, type, tArgs);
+ buildValueField = new FieldDeclaration(valueName, 0, -1);
+ buildValueField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ buildValueField.modifiers = ClassFileConstants.AccPrivate;
+ buildValueField.declarationSourceEnd = -1;
+ buildValueField.type = type;
+ }
+ data.setGeneratedByRecursive(buildKeyField);
+ data.setGeneratedByRecursive(buildValueField);
+ EclipseNode keyFieldNode = injectField(builderType, buildKeyField);
+ EclipseNode valueFieldNode = injectField(builderType, buildValueField);
+ return Arrays.asList(keyFieldNode, valueFieldNode);
+ }
+
+ @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.generateMethods(data, builderType, fluent, chain);
+ return;
+ }
+
+ TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
+ }
+
+ private void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType, true));
+
+ String sN = new String(data.getSingularName());
+ String pN = new String(data.getPluralName());
+ char[] keyParamName = (sN + "Key").toCharArray();
+ char[] valueParamName = (sN + "Value").toCharArray();
+ char[] keyFieldName = (pN + "$key").toCharArray();
+ char[] valueFieldName = (pN + "$value").toCharArray();
+
+ /* this.pluralname$key.add(singularnameKey); */ {
+ FieldReference thisDotKeyField = new FieldReference(keyFieldName, 0L);
+ thisDotKeyField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotKeyFieldDotAdd = new MessageSend();
+ thisDotKeyFieldDotAdd.arguments = new Expression[] {new SingleNameReference(keyParamName, 0L)};
+ thisDotKeyFieldDotAdd.receiver = thisDotKeyField;
+ thisDotKeyFieldDotAdd.selector = "add".toCharArray();
+ statements.add(thisDotKeyFieldDotAdd);
+ }
+
+ /* this.pluralname$value.add(singularnameValue); */ {
+ FieldReference thisDotValueField = new FieldReference(valueFieldName, 0L);
+ thisDotValueField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotValueFieldDotAdd = new MessageSend();
+ thisDotValueFieldDotAdd.arguments = new Expression[] {new SingleNameReference(valueParamName, 0L)};
+ thisDotValueFieldDotAdd.receiver = thisDotValueField;
+ thisDotValueFieldDotAdd.selector = "add".toCharArray();
+ statements.add(thisDotValueFieldDotAdd);
+ }
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+ TypeReference keyParamType = cloneParamType(0, data.getTypeArgs(), builderType);
+ Argument keyParam = new Argument(keyParamName, 0, keyParamType, 0);
+ TypeReference valueParamType = cloneParamType(1, data.getTypeArgs(), builderType);
+ Argument valueParam = new Argument(valueParamName, 0, valueParamType, 0);
+ md.arguments = new Argument[] {keyParam, valueParam};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("put", new String(data.getSingularName())).toCharArray();
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ private void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ String pN = new String(data.getPluralName());
+ char[] keyFieldName = (pN + "$key").toCharArray();
+ char[] valueFieldName = (pN + "$value").toCharArray();
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType, true));
+
+ char[] entryName = "$lombokEntry".toCharArray();
+
+ TypeReference forEachType = new QualifiedTypeReference(JAVA_UTIL_MAP_ENTRY, NULL_POSS);
+ forEachType = addTypeArgs(2, true, builderType, forEachType, data.getTypeArgs());
+
+ MessageSend keyArg = new MessageSend();
+ keyArg.receiver = new SingleNameReference(entryName, 0L);
+ keyArg.selector = "getKey".toCharArray();
+ MessageSend addKey = new MessageSend();
+ FieldReference thisDotKeyField = new FieldReference(keyFieldName, 0L);
+ thisDotKeyField.receiver = new ThisReference(0, 0);
+ addKey.receiver = thisDotKeyField;
+ addKey.selector = new char[] {'a', 'd', 'd'};
+ addKey.arguments = new Expression[] {keyArg};
+
+ MessageSend valueArg = new MessageSend();
+ valueArg.receiver = new SingleNameReference(entryName, 0L);
+ valueArg.selector = "getValue".toCharArray();
+ MessageSend addValue = new MessageSend();
+ FieldReference thisDotValueField = new FieldReference(valueFieldName, 0L);
+ thisDotValueField.receiver = new ThisReference(0, 0);
+ addValue.receiver = thisDotValueField;
+ addValue.selector = new char[] {'a', 'd', 'd'};
+ addValue.arguments = new Expression[] {valueArg};
+
+ LocalDeclaration elementVariable = new LocalDeclaration(entryName, 0, 0);
+ elementVariable.type = forEachType;
+ ForeachStatement forEach = new ForeachStatement(elementVariable, 0);
+ MessageSend invokeEntrySet = new MessageSend();
+ invokeEntrySet.selector = new char[] { 'e', 'n', 't', 'r', 'y', 'S', 'e', 't'};
+ invokeEntrySet.receiver = new SingleNameReference(data.getPluralName(), 0L);
+ forEach.collection = invokeEntrySet;
+ Block forEachContent = new Block(0);
+ forEachContent.statements = new Statement[] {addKey, addValue};
+ forEach.action = forEachContent;
+ statements.add(forEach);
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+
+ TypeReference paramType = new QualifiedTypeReference(JAVA_UTIL_MAP, NULL_POSS);
+ paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs());
+ Argument param = new Argument(data.getPluralName(), 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("putAll", new String(data.getPluralName())).toCharArray();
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ return;
+ }
+
+ if (data.getTargetFqn().equals("java.util.Map")) {
+ statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap"));
+ } else {
+ statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap"));
+ }
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
new file mode 100644
index 00000000..2d16eae0
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
@@ -0,0 +1,52 @@
+/*
+ * 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.eclipse.handlers.singulars;
+
+import java.util.List;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseJavaUtilSetSingularizer extends EclipseJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet");
+ }
+
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ return;
+ }
+
+ if (data.getTargetFqn().equals("java.util.Set")) {
+ statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, false, "emptySet", "singleton", "LinkedHashSet"));
+ } else {
+ statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, false, true, false, true, "TreeSet"));
+ }
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
new file mode 100644
index 00000000..e4c399ed
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.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.eclipse.handlers.singulars;
+
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
+import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.BreakStatement;
+import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.ForStatement;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.PostfixExpression;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+import lombok.ConfigurationKeys;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
+ protected static final char[][] JAVA_UTIL_ARRAYLIST = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'A', 'r', 'r', 'a', 'y', 'L', 'i', 's', 't'}
+ };
+
+ protected static final char[][] JAVA_UTIL_LIST = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'L', 'i', 's', 't'}
+ };
+
+ protected static final char[][] JAVA_UTIL_MAP = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'}
+ };
+
+ protected static final char[][] JAVA_UTIL_MAP_ENTRY = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'}, {'E', 'n', 't', 'r', 'y'}
+ };
+
+ protected static final char[][] JAVA_UTIL_COLLECTIONS = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'C', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', 's'}
+ };
+
+ protected final EclipseSingularizer guavaListSetSingularizer = new EclipseGuavaSetListSingularizer();
+ protected final EclipseSingularizer guavaMapSingularizer = new EclipseGuavaMapSingularizer();
+
+ protected boolean useGuavaInstead(EclipseNode node) {
+ return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.BUILDER_USE_GUAVA));
+ }
+
+ protected List<Statement> createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType) {
+ List<Statement> switchContents = new ArrayList<Statement>();
+ char[] keyName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName();
+
+ if (emptyCollectionMethod != null) { // case 0: (empty); break;
+ switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'0'}, null), 0, 0));
+
+ /* pluralName = java.util.Collections.emptyCollectionMethod(); */ {
+ MessageSend invoke = new MessageSend();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ invoke.selector = emptyCollectionMethod.toCharArray();
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0));
+ }
+
+ switchContents.add(new BreakStatement(null, 0, 0));
+ }
+
+ if (singletonCollectionMethod != null) { // case 1: (singleton); break;
+ switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'1'}, null), 0, 0));
+ /* !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)); */ {
+ FieldReference thisDotKey = new FieldReference(keyName, 0L);
+ thisDotKey.receiver = new ThisReference(0, 0);
+ MessageSend thisDotKeyGet0 = new MessageSend();
+ thisDotKeyGet0.receiver = thisDotKey;
+ thisDotKeyGet0.selector = new char[] {'g', 'e', 't'};
+ thisDotKeyGet0.arguments = new Expression[] {makeIntLiteral(new char[] {'0'}, null)};
+
+ Expression[] args;
+ if (mapMode) {
+ char[] valueName = (new String(data.getPluralName()) + "$value").toCharArray();
+ FieldReference thisDotValue = new FieldReference(valueName, 0L);
+ thisDotValue.receiver = new ThisReference(0, 0);
+ MessageSend thisDotValueGet0 = new MessageSend();
+ thisDotValueGet0.receiver = thisDotValue;
+ thisDotValueGet0.selector = new char[] {'g', 'e', 't'};
+ thisDotValueGet0.arguments = new Expression[] {makeIntLiteral(new char[] {'0'}, null)};
+ args = new Expression[] {thisDotKeyGet0, thisDotValueGet0};
+ } else {
+ args = new Expression[] {thisDotKeyGet0};
+ }
+
+ MessageSend invoke = new MessageSend();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ invoke.selector = singletonCollectionMethod.toCharArray();
+ invoke.arguments = args;
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0));
+ }
+ switchContents.add(new BreakStatement(null, 0, 0));
+ }
+
+ { // default:
+ switchContents.add(new CaseStatement(null, 0, 0));
+ switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType));
+ }
+
+ SwitchStatement switchStat = new SwitchStatement();
+ switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]);
+ switchStat.expression = getSize(builderType, keyName, true);
+
+ TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs());
+ LocalDeclaration varDefStat = new LocalDeclaration(data.getPluralName(), 0, 0);
+ varDefStat.type = localShadowerType;
+ return Arrays.asList(varDefStat, switchStat);
+ }
+
+ protected List<Statement> createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType) {
+ char[] varName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName();
+
+ Statement createStat; {
+ // pluralName = new java.util.TargetType(initialCap);
+ Expression[] constructorArgs = null;
+ if (addInitialCapacityArg) {
+ // 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
+ Expression lessThanCutoff = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral("0x40000000".toCharArray(), null), OperatorIds.LESS);
+ FieldReference integerMaxValue = new FieldReference("MAX_VALUE".toCharArray(), 0L);
+ integerMaxValue.receiver = new QualifiedNameReference(TypeConstants.JAVA_LANG_INTEGER, NULL_POSS, 0, 0);
+ Expression sizeFormulaLeft = new BinaryExpression(makeIntLiteral(new char[] {'1'}, null), getSize(builderType, varName, nullGuard), OperatorIds.PLUS);
+ Expression sizeFormulaRightLeft = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral(new char[] {'3'}, null), OperatorIds.MINUS);
+ Expression sizeFormulaRight = new BinaryExpression(sizeFormulaRightLeft, makeIntLiteral(new char[] {'3'}, null), OperatorIds.DIVIDE);
+ Expression sizeFormula = new BinaryExpression(sizeFormulaLeft, sizeFormulaRight, OperatorIds.PLUS);
+ Expression cond = new ConditionalExpression(lessThanCutoff, sizeFormula, integerMaxValue);
+ constructorArgs = new Expression[] {cond};
+ }
+
+ TypeReference targetTypeRef = new QualifiedTypeReference(new char[][] {TypeConstants.JAVA, TypeConstants.UTIL, targetType.toCharArray()}, NULL_POSS);
+ targetTypeRef = addTypeArgs(mapMode ? 2 : 1, false, builderType, targetTypeRef, data.getTypeArgs());
+ AllocationExpression constructorCall = new AllocationExpression();
+ constructorCall.type = targetTypeRef;
+ constructorCall.arguments = constructorArgs;
+
+ if (defineVar) {
+ TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs());
+ LocalDeclaration localShadowerDecl = new LocalDeclaration(data.getPluralName(), 0, 0);
+ localShadowerDecl.type = localShadowerType;
+ localShadowerDecl.initialization = constructorCall;
+ createStat = localShadowerDecl;
+ } else {
+ createStat = new Assignment(new SingleNameReference(data.getPluralName(), 0L), constructorCall, 0);
+ }
+ }
+
+ Statement 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));
+ char[] iVar = new char[] {'$', 'i'};
+ MessageSend pluralnameDotPut = new MessageSend();
+ pluralnameDotPut.selector = new char[] {'p', 'u', 't'};
+ pluralnameDotPut.receiver = new SingleNameReference(data.getPluralName(), 0L);
+ FieldReference thisDotKey = new FieldReference(varName, 0L);
+ thisDotKey.receiver = new ThisReference(0, 0);
+ FieldReference thisDotValue = new FieldReference((new String(data.getPluralName()) + "$value").toCharArray(), 0L);
+ thisDotValue.receiver = new ThisReference(0, 0);
+ MessageSend keyArg = new MessageSend();
+ keyArg.receiver = thisDotKey;
+ keyArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)};
+ keyArg.selector = new char[] {'g', 'e', 't'};
+ MessageSend valueArg = new MessageSend();
+ valueArg.receiver = thisDotValue;
+ valueArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)};
+ valueArg.selector = new char[] {'g', 'e', 't'};
+ pluralnameDotPut.arguments = new Expression[] {keyArg, valueArg};
+
+ LocalDeclaration forInit = new LocalDeclaration(iVar, 0, 0);
+ forInit.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
+ forInit.initialization = makeIntLiteral(new char[] {'0'}, null);
+ Expression checkExpr = new BinaryExpression(new SingleNameReference(iVar, 0L), getSize(builderType, varName, nullGuard), OperatorIds.LESS);
+ Expression incrementExpr = new PostfixExpression(new SingleNameReference(iVar, 0L), IntLiteral.One, OperatorIds.PLUS, 0);
+ fillStat = new ForStatement(new Statement[] {forInit}, checkExpr, new Statement[] {incrementExpr}, pluralnameDotPut, true, 0, 0);
+ } else {
+ // pluralname.addAll(this.pluralname);
+ MessageSend pluralnameDotAddAll = new MessageSend();
+ pluralnameDotAddAll.selector = new char[] {'a', 'd', 'd', 'A', 'l', 'l'};
+ pluralnameDotAddAll.receiver = new SingleNameReference(data.getPluralName(), 0L);
+ FieldReference thisDotPluralname = new FieldReference(varName, 0L);
+ thisDotPluralname.receiver = new ThisReference(0, 0);
+ pluralnameDotAddAll.arguments = new Expression[] {thisDotPluralname};
+ fillStat = pluralnameDotAddAll;
+ }
+
+ if (nullGuard) {
+ FieldReference thisDotField = new FieldReference(varName, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL);
+ fillStat = new IfStatement(cond, fillStat, 0, 0);
+ }
+ }
+
+ Statement unmodifiableStat; {
+ // pluralname = Collections.unmodifiableInterfaceType(pluralname);
+ Expression arg = new SingleNameReference(data.getPluralName(), 0L);
+ MessageSend invoke = new MessageSend();
+ invoke.arguments = new Expression[] {arg};
+ invoke.selector = ("unmodifiable" + data.getTargetSimpleType()).toCharArray();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ unmodifiableStat = new Assignment(new SingleNameReference(data.getPluralName(), 0L), invoke, 0);
+ }
+
+ return Arrays.asList(createStat, fillStat, unmodifiableStat);
+ }
+
+ protected Statement createConstructBuilderVarIfNeeded(SingularData data, EclipseNode builderType, boolean mapMode) {
+ char[] v1Name, v2Name;
+ if (mapMode) {
+ String n = new String(data.getPluralName());
+ v1Name = (n + "$key").toCharArray();
+ v2Name = (n + "$value").toCharArray();
+ } else {
+ v1Name = data.getPluralName();
+ v2Name = null;
+ }
+
+ FieldReference thisDotField = new FieldReference(v1Name, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+
+ thisDotField = new FieldReference(v1Name, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ TypeReference v1Type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ v1Type = addTypeArgs(1, false, builderType, v1Type, data.getTypeArgs());
+ AllocationExpression constructArrayList = new AllocationExpression();
+ constructArrayList.type = v1Type;
+ Assignment initV1 = new Assignment(thisDotField, constructArrayList, 0);
+ Statement thenPart;
+ if (mapMode) {
+ thisDotField = new FieldReference(v2Name, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ TypeReference v2Type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ List<TypeReference> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.size() > 1) tArgs = Collections.singletonList(tArgs.get(1));
+ else tArgs = Collections.emptyList();
+ v2Type = addTypeArgs(1, false, builderType, v2Type, tArgs);
+ constructArrayList = new AllocationExpression();
+ constructArrayList.type = v2Type;
+ Assignment initV2 = new Assignment(thisDotField, constructArrayList, 0);
+ Block b = new Block(0);
+ b.statements = new Statement[] {initV1, initV2};
+ thenPart = b;
+ } else {
+ thenPart = initV1;
+ }
+
+ return new IfStatement(cond, thenPart, 0, 0);
+ }
+}
diff --git a/src/core/lombok/experimental/Builder.java b/src/core/lombok/experimental/Builder.java
index 1300e7d3..7d89109f 100644
--- a/src/core/lombok/experimental/Builder.java
+++ b/src/core/lombok/experimental/Builder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Project Lombok Authors.
+ * Copyright (C) 2013-2014 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
@@ -103,9 +103,12 @@ import java.lang.annotation.Target;
* }
* }
* </pre>
+ *
+ * @deprecated {@link lombok.Builder} has been promoted to the main package, so use that one instead.
*/
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
+@Deprecated
public @interface Builder {
/** Name of the static method that creates a new builder instance. Default: {@code builder}. */
String builderMethodName() default "builder";
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/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/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index 1885b8b4..e251e0ce 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,29 @@ 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> {
+ 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) {
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
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 +108,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 +136,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 +152,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 +211,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 +227,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;
+ }
+ }
+ }
+
}
- 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);
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ if (bfd.singularData.getSingularizer().requiresCleaning()) {
+ addCleaning = true;
+ break;
+ }
+ }
+ }
+
+ 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 +262,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());
+ }
+
+ 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;
+ }
+
+ */
}
- public JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<Name> fieldNames, JavacNode type, List<JCExpression> thrownExceptions) {
+ 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 +358,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 +384,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 +451,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/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 6413e8ef..8a8c5acd 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;
@@ -242,51 +243,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 +289,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 +455,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();
@@ -985,6 +995,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 +1034,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..e60bc247
--- /dev/null
+++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
@@ -0,0 +1,292 @@
+/*
+ * 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();
+ Context context = node.getContext();
+
+ if (count < 0) throw new IllegalArgumentException("count is negative");
+ if (count == 0) return type;
+ 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(chainDots(node, "java", "lang", "Object"));
+ } else if (orig.getKind() == Kind.EXTENDS_WILDCARD) {
+ JCExpression inner;
+ try {
+ inner = (JCExpression) ((JCWildcard) orig).inner;
+ } catch (Exception e) {
+ inner = chainDots(node, "java", "lang", "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(chainDots(node, "java", "lang", "Object"));
+ }
+ }
+ return maker.TypeApply(type, 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 chainDots(builderType, "java", "lang", "Object");
+ } else {
+ JCExpression originalType = typeArgs.get(index);
+ if (originalType.getKind() == Kind.UNBOUNDED_WILDCARD || originalType.getKind() == Kind.SUPER_WILDCARD) {
+ return chainDots(builderType, "java", "lang", "Object");
+ } else if (originalType.getKind() == Kind.EXTENDS_WILDCARD) {
+ try {
+ return cloneType(maker, (JCExpression) ((JCWildcard) originalType).inner, source, builderType.getContext());
+ } catch (Exception e) {
+ return chainDots(builderType, "java", "lang", "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..a45faae4
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.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 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 = chainDots(builderType, "java", "lang", "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");
+ empty = maker.Apply(jceBlank, 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..2cf34cf7
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
@@ -0,0 +1,127 @@
+/*
+ * 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.util.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 = 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<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ Name thisName = builderType.toName("this");
+
+ JCStatement createStat; {
+ // pluralName = new java.util.ArrayList<Generics>(this.pluralName.size());
+ List<JCExpression> constructorArgs = List.nil();
+ constructorArgs = List.<JCExpression>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/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..6aeadfea
--- /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.BUILDER_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 = chainDots(builderType, "java", "lang", "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);
+ }
+}
diff --git a/src/utils/lombok/core/LombokImmutableList.java b/src/utils/lombok/core/LombokImmutableList.java
index e0e1136c..4603f2ad 100644
--- a/src/utils/lombok/core/LombokImmutableList.java
+++ b/src/utils/lombok/core/LombokImmutableList.java
@@ -80,6 +80,12 @@ public final class LombokImmutableList<T> implements Iterable<T> {
return copyOf(list);
}
+ public static <T> LombokImmutableList<T> copyOf(T[] array) {
+ Object[] content = new Object[array.length];
+ System.arraycopy(array, 0, content, 0, array.length);
+ return new LombokImmutableList<T>(content);
+ }
+
private LombokImmutableList(Object[] content) {
this.content = content;
}
diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java
index e207c44a..41ff8242 100644
--- a/src/utils/lombok/javac/Javac.java
+++ b/src/utils/lombok/javac/Javac.java
@@ -165,6 +165,10 @@ public class Javac {
public static final TypeTag CTC_CLASS = typeTag("CLASS");
public static final TreeTag CTC_NOT_EQUAL = treeTag("NE");
+ public static final TreeTag CTC_LESS_THAN = treeTag("LT");
+ public static final TreeTag CTC_GREATER_THAN = treeTag("GT");
+ public static final TreeTag CTC_LESS_OR_EQUAL= treeTag("LE");
+ public static final TreeTag CTC_GREATER_OR_EQUAL = treeTag("GE");
public static final TreeTag CTC_POS = treeTag("POS");
public static final TreeTag CTC_NEG = treeTag("NEG");
public static final TreeTag CTC_NOT = treeTag("NOT");
@@ -172,10 +176,14 @@ public class Javac {
public static final TreeTag CTC_BITXOR = treeTag("BITXOR");
public static final TreeTag CTC_UNSIGNED_SHIFT_RIGHT = treeTag("USR");
public static final TreeTag CTC_MUL = treeTag("MUL");
+ public static final TreeTag CTC_DIV = treeTag("DIV");
public static final TreeTag CTC_PLUS = treeTag("PLUS");
+ public static final TreeTag CTC_MINUS = treeTag("MINUS");
public static final TreeTag CTC_EQUAL = treeTag("EQ");
public static final TreeTag CTC_PREINC = treeTag("PREINC");
public static final TreeTag CTC_PREDEC = treeTag("PREDEC");
+ public static final TreeTag CTC_POSTINC = treeTag("POSTINC");
+ public static final TreeTag CTC_POSTDEC = treeTag("POSTDEC");
private static final Method getExtendsClause, getEndPosition, storeEnd;
diff --git a/test/configuration/resource/configurationRoot/out.txt b/test/configuration/resource/configurationRoot/out.txt
index 625115a4..de219694 100644
--- a/test/configuration/resource/configurationRoot/out.txt
+++ b/test/configuration/resource/configurationRoot/out.txt
@@ -8,7 +8,7 @@ lombok.accessors.flagUsage = ERROR
# BASE/d1/d11/lombok.config:
# 3: lombok.accessors.flagUsage = ERROR
-# Generate setters that return 'this' instead of 'void'.
+# Generate setters that return 'this' instead of 'void' (default: false).
lombok.accessors.chain = false
# BASE/d1/lombok.config:
# <'lombok.accessors.chain' not mentioned>
@@ -24,7 +24,7 @@ lombok.accessors.prefix += f
# BASE/d1/d11/lombok.config:
# 4: lombok.accessors.prefix += f
-# Use this name for the generated logger fields (default: 'log')
+# Use this name for the generated logger fields (default: 'log').
clear lombok.log.fieldName
@@ -43,7 +43,7 @@ lombok.accessors.flagUsage = ERROR
# BASE/d1/d11/d111/lombok.config:
# <'lombok.accessors.flagUsage' not mentioned>
-# Generate setters that return 'this' instead of 'void'.
+# Generate setters that return 'this' instead of 'void' (default: false).
clear lombok.accessors.chain
# BASE/d1/lombok.config:
# <'lombok.accessors.chain' not mentioned>
@@ -66,7 +66,7 @@ lombok.accessors.prefix += m_
# BASE/d1/d11/d111/lombok.config:
# 2: lombok.accessors.prefix += m_
-# Use this name for the generated logger fields (default: 'log')
+# Use this name for the generated logger fields (default: 'log').
clear lombok.log.fieldName
@@ -75,7 +75,7 @@ Configuration for 'BASE/d1/d12'.
# Emit a warning or error if @Accessors is used.
clear lombok.accessors.flagUsage
-# Generate setters that return 'this' instead of 'void'.
+# Generate setters that return 'this' instead of 'void' (default: false).
lombok.accessors.chain = true
# BASE/d1/lombok.config:
# <'lombok.accessors.chain' not mentioned>
@@ -86,5 +86,5 @@ lombok.accessors.chain = true
# Strip this field prefix, like 'f' or 'm_', from the names of generated getters and setters.
clear lombok.accessors.prefix
-# Use this name for the generated logger fields (default: 'log')
+# Use this name for the generated logger fields (default: 'log').
clear lombok.log.fieldName \ No newline at end of file
diff --git a/test/core/src/lombok/RunAllTests.java b/test/core/src/lombok/RunAllTests.java
index 9f56b45b..1ca76af5 100644
--- a/test/core/src/lombok/RunAllTests.java
+++ b/test/core/src/lombok/RunAllTests.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
@@ -26,6 +26,6 @@ import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
-@SuiteClasses({lombok.transform.RunTransformTests.class, lombok.bytecode.RunBytecodeTests.class, lombok.core.configuration.RunConfigurationTests.class})
+@SuiteClasses({lombok.transform.RunTransformTests.class, lombok.bytecode.RunBytecodeTests.class, lombok.core.configuration.RunConfigurationTests.class, lombok.core.RunCoreTests.class})
public class RunAllTests {
}
diff --git a/test/core/src/lombok/RunTestsViaEcj.java b/test/core/src/lombok/RunTestsViaEcj.java
index 1a94baf1..2b83b296 100644
--- a/test/core/src/lombok/RunTestsViaEcj.java
+++ b/test/core/src/lombok/RunTestsViaEcj.java
@@ -143,6 +143,7 @@ public class RunTestsViaEcj extends AbstractRunTests {
classpath.add("lib/test/org.slf4j-slf4j-ext.jar");
classpath.add("lib/test/log4j-log4j.jar");
classpath.add("lib/test/org.apache.logging.log4j-log4j-api.jar");
+ classpath.add("lib/test/com.google.guava-guava.jar");
return new FileSystem(classpath.toArray(new String[0]), new String[] {file.getAbsolutePath()}, "UTF-8");
}
}
diff --git a/test/core/src/lombok/core/RunCoreTests.java b/test/core/src/lombok/core/RunCoreTests.java
new file mode 100644
index 00000000..8ac7cf81
--- /dev/null
+++ b/test/core/src/lombok/core/RunCoreTests.java
@@ -0,0 +1,31 @@
+/*
+ * 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.core;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({TestSingulars.class})
+public class RunCoreTests {
+}
diff --git a/test/core/src/lombok/core/TestSingulars.java b/test/core/src/lombok/core/TestSingulars.java
new file mode 100644
index 00000000..1134af08
--- /dev/null
+++ b/test/core/src/lombok/core/TestSingulars.java
@@ -0,0 +1,52 @@
+/*
+ * 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.core;
+
+import static lombok.core.handlers.Singulars.autoSingularize;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class TestSingulars {
+ @Test
+ public void testSingulars() {
+ assertEquals(null, autoSingularize("axes"));
+ assertEquals("adjective", autoSingularize("adjectives"));
+ assertEquals("bus", autoSingularize("buses"));
+ assertEquals("octopus", autoSingularize("octopodes"));
+ assertEquals(null, autoSingularize("octopi"));
+ assertEquals("elf", autoSingularize("elves"));
+ assertEquals("jack", autoSingularize("jacks"));
+ assertEquals("colloquy", autoSingularize("colloquies"));
+ assertEquals(null, autoSingularize("series"));
+ assertEquals("man", autoSingularize("men"));
+ assertEquals(null, autoSingularize("highwaymen"));
+ assertEquals("caveMan", autoSingularize("caveMen"));
+ assertEquals(null, autoSingularize("jackss"));
+ assertEquals(null, autoSingularize("virus"));
+ assertEquals("quiz", autoSingularize("quizzes"));
+ assertEquals("database", autoSingularize("databases"));
+ assertEquals("dataBase", autoSingularize("dataBases"));
+ assertEquals("Query", autoSingularize("Queries"));
+ assertEquals("Movie", autoSingularize("Movies"));
+ }
+}
diff --git a/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java b/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java
new file mode 100644
index 00000000..3fc969ef
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderSingularGuavaListsSets.java
@@ -0,0 +1,93 @@
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+class BuilderSingularGuavaListsSets<T> {
+ private ImmutableList<T> cards;
+ private ImmutableCollection<? extends Number> frogs;
+ @SuppressWarnings("all")
+ private ImmutableSet rawSet;
+ private ImmutableSortedSet<String> passes;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularGuavaListsSets(final ImmutableList<T> cards, final ImmutableCollection<? extends Number> frogs, final ImmutableSet rawSet, final ImmutableSortedSet<String> passes) {
+ this.cards = cards;
+ this.frogs = frogs;
+ this.rawSet = rawSet;
+ this.passes = passes;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static class BuilderSingularGuavaListsSetsBuilder<T> {
+ private com.google.common.collect.ImmutableList.Builder<T> cards;
+ private com.google.common.collect.ImmutableList.Builder<Number> frogs;
+ private com.google.common.collect.ImmutableSet.Builder<java.lang.Object> rawSet;
+ private com.google.common.collect.ImmutableSortedSet.Builder<String> passes;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularGuavaListsSetsBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSetsBuilder<T> card(final T card) {
+ if (this.cards == null) this.cards = com.google.common.collect.ImmutableList.builder();
+ this.cards.add(card);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSetsBuilder<T> cards(final java.lang.Iterable<? extends T> cards) {
+ if (this.cards == null) this.cards = com.google.common.collect.ImmutableList.builder();
+ this.cards.addAll(cards);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSetsBuilder<T> frog(final Number frog) {
+ if (this.frogs == null) this.frogs = com.google.common.collect.ImmutableList.builder();
+ this.frogs.add(frog);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSetsBuilder<T> frogs(final java.lang.Iterable<? extends Number> frogs) {
+ if (this.frogs == null) this.frogs = com.google.common.collect.ImmutableList.builder();
+ this.frogs.addAll(frogs);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSetsBuilder<T> rawSet(final java.lang.Object rawSet) {
+ if (this.rawSet == null) this.rawSet = com.google.common.collect.ImmutableSet.builder();
+ this.rawSet.add(rawSet);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSetsBuilder<T> rawSet(final java.lang.Iterable<?> rawSet) {
+ if (this.rawSet == null) this.rawSet = com.google.common.collect.ImmutableSet.builder();
+ this.rawSet.addAll(rawSet);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSetsBuilder<T> pass(final String pass) {
+ if (this.passes == null) this.passes = com.google.common.collect.ImmutableSortedSet.naturalOrder();
+ this.passes.add(pass);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSetsBuilder<T> passes(final java.lang.Iterable<? extends String> passes) {
+ if (this.passes == null) this.passes = com.google.common.collect.ImmutableSortedSet.naturalOrder();
+ this.passes.addAll(passes);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaListsSets<T> build() {
+ com.google.common.collect.ImmutableList<T> cards = this.cards == null ? com.google.common.collect.ImmutableList.of() : this.cards.build();
+ com.google.common.collect.ImmutableCollection<Number> frogs = this.frogs == null ? com.google.common.collect.ImmutableList.of() : this.frogs.build();
+ com.google.common.collect.ImmutableSet<java.lang.Object> rawSet = this.rawSet == null ? com.google.common.collect.ImmutableSet.of() : this.rawSet.build();
+ com.google.common.collect.ImmutableSortedSet<String> passes = this.passes == null ? com.google.common.collect.ImmutableSortedSet.of() : this.passes.build();
+ return new BuilderSingularGuavaListsSets<T>(cards, frogs, rawSet, passes);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder(cards=" + this.cards + ", frogs=" + this.frogs + ", rawSet=" + this.rawSet + ", passes=" + this.passes + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ public static <T> BuilderSingularGuavaListsSetsBuilder<T> builder() {
+ return new BuilderSingularGuavaListsSetsBuilder<T>();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java b/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java
new file mode 100644
index 00000000..c55dff28
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderSingularGuavaMaps.java
@@ -0,0 +1,76 @@
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableSortedMap;
+class BuilderSingularGuavaMaps<K, V> {
+ private ImmutableMap<K, V> battleaxes;
+ private ImmutableSortedMap<Integer, ? extends V> vertices;
+ @SuppressWarnings("all")
+ private ImmutableBiMap rawMap;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularGuavaMaps(final ImmutableMap<K, V> battleaxes, final ImmutableSortedMap<Integer, ? extends V> vertices, final ImmutableBiMap rawMap) {
+ this.battleaxes = battleaxes;
+ this.vertices = vertices;
+ this.rawMap = rawMap;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static class BuilderSingularGuavaMapsBuilder<K, V> {
+ private com.google.common.collect.ImmutableMap.Builder<K, V> battleaxes;
+ private com.google.common.collect.ImmutableSortedMap.Builder<Integer, V> vertices;
+ private com.google.common.collect.ImmutableBiMap.Builder<java.lang.Object, java.lang.Object> rawMap;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularGuavaMapsBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaMapsBuilder<K, V> battleaxe(final K battleaxe$key, final V battleaxe$value) {
+ if (this.battleaxes == null) this.battleaxes = com.google.common.collect.ImmutableMap.builder();
+ this.battleaxes.put(battleaxe$key, battleaxe$value);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaMapsBuilder<K, V> battleaxes(final java.util.Map<? extends K, ? extends V> battleaxes) {
+ if (this.battleaxes == null) this.battleaxes = com.google.common.collect.ImmutableMap.builder();
+ this.battleaxes.putAll(battleaxes);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaMapsBuilder<K, V> vertex(final Integer vertex$key, final V vertex$value) {
+ if (this.vertices == null) this.vertices = com.google.common.collect.ImmutableSortedMap.naturalOrder();
+ this.vertices.put(vertex$key, vertex$value);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaMapsBuilder<K, V> vertices(final java.util.Map<? extends Integer, ? extends V> vertices) {
+ if (this.vertices == null) this.vertices = com.google.common.collect.ImmutableSortedMap.naturalOrder();
+ this.vertices.putAll(vertices);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaMapsBuilder<K, V> rawMap(final java.lang.Object rawMap$key, final java.lang.Object rawMap$value) {
+ if (this.rawMap == null) this.rawMap = com.google.common.collect.ImmutableBiMap.builder();
+ this.rawMap.put(rawMap$key, rawMap$value);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaMapsBuilder<K, V> rawMap(final java.util.Map<?, ?> rawMap) {
+ if (this.rawMap == null) this.rawMap = com.google.common.collect.ImmutableBiMap.builder();
+ this.rawMap.putAll(rawMap);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularGuavaMaps<K, V> build() {
+ com.google.common.collect.ImmutableMap<K, V> battleaxes = this.battleaxes == null ? com.google.common.collect.ImmutableMap.of() : this.battleaxes.build();
+ com.google.common.collect.ImmutableSortedMap<Integer, V> vertices = this.vertices == null ? com.google.common.collect.ImmutableSortedMap.of() : this.vertices.build();
+ com.google.common.collect.ImmutableBiMap<java.lang.Object, java.lang.Object> rawMap = this.rawMap == null ? com.google.common.collect.ImmutableBiMap.of() : this.rawMap.build();
+ return new BuilderSingularGuavaMaps<K, V>(battleaxes, vertices, rawMap);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderSingularGuavaMaps.BuilderSingularGuavaMapsBuilder(battleaxes=" + this.battleaxes + ", vertices=" + this.vertices + ", rawMap=" + this.rawMap + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ public static <K, V> BuilderSingularGuavaMapsBuilder<K, V> builder() {
+ return new BuilderSingularGuavaMapsBuilder<K, V>();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/BuilderSingularLists.java b/test/transform/resource/after-delombok/BuilderSingularLists.java
new file mode 100644
index 00000000..aa5382c1
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderSingularLists.java
@@ -0,0 +1,111 @@
+import java.util.List;
+import java.util.Collection;
+class BuilderSingularLists<T> {
+ private List<T> children;
+ private Collection<? extends Number> scarves;
+ @SuppressWarnings("all")
+ private List rawList;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularLists(final List<T> children, final Collection<? extends Number> scarves, final List rawList) {
+ this.children = children;
+ this.scarves = scarves;
+ this.rawList = rawList;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static class BuilderSingularListsBuilder<T> {
+ private java.util.ArrayList<T> children;
+ private java.util.ArrayList<Number> scarves;
+ private java.util.ArrayList<java.lang.Object> rawList;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularListsBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularListsBuilder<T> child(final T child) {
+ if (this.children == null) this.children = new java.util.ArrayList<T>();
+ this.children.add(child);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularListsBuilder<T> children(final java.util.Collection<? extends T> children) {
+ if (this.children == null) this.children = new java.util.ArrayList<T>();
+ this.children.addAll(children);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularListsBuilder<T> scarf(final Number scarf) {
+ if (this.scarves == null) this.scarves = new java.util.ArrayList<Number>();
+ this.scarves.add(scarf);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularListsBuilder<T> scarves(final java.util.Collection<? extends Number> scarves) {
+ if (this.scarves == null) this.scarves = new java.util.ArrayList<Number>();
+ this.scarves.addAll(scarves);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularListsBuilder<T> rawList(final java.lang.Object rawList) {
+ if (this.rawList == null) this.rawList = new java.util.ArrayList<java.lang.Object>();
+ this.rawList.add(rawList);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularListsBuilder<T> rawList(final java.util.Collection<?> rawList) {
+ if (this.rawList == null) this.rawList = new java.util.ArrayList<java.lang.Object>();
+ this.rawList.addAll(rawList);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularLists<T> build() {
+ java.util.List<T> 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<T>(this.children.size());
+ children.addAll(this.children);
+ children = java.util.Collections.unmodifiableList(children);
+ }
+ java.util.List<Number> 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<Number>(this.scarves.size());
+ scarves.addAll(this.scarves);
+ scarves = java.util.Collections.unmodifiableList(scarves);
+ }
+ java.util.List<java.lang.Object> 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<java.lang.Object>(this.rawList.size());
+ rawList.addAll(this.rawList);
+ rawList = java.util.Collections.unmodifiableList(rawList);
+ }
+ return new BuilderSingularLists<T>(children, scarves, rawList);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderSingularLists.BuilderSingularListsBuilder(children=" + this.children + ", scarves=" + this.scarves + ", rawList=" + this.rawList + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ public static <T> BuilderSingularListsBuilder<T> builder() {
+ return new BuilderSingularListsBuilder<T>();
+ }
+}
diff --git a/test/transform/resource/after-delombok/BuilderSingularMaps.java b/test/transform/resource/after-delombok/BuilderSingularMaps.java
new file mode 100644
index 00000000..640ddb94
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderSingularMaps.java
@@ -0,0 +1,173 @@
+import java.util.Map;
+import java.util.SortedMap;
+class BuilderSingularMaps<K, V> {
+ private Map<K, V> women;
+ private SortedMap<K, ? extends Number> men;
+ @SuppressWarnings("all")
+ private Map rawMap;
+ private Map<String, V> stringMap;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularMaps(final Map<K, V> women, final SortedMap<K, ? extends Number> men, final Map rawMap, final Map<String, V> stringMap) {
+ this.women = women;
+ this.men = men;
+ this.rawMap = rawMap;
+ this.stringMap = stringMap;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static class BuilderSingularMapsBuilder<K, V> {
+ private java.util.ArrayList<K> women$key;
+ private java.util.ArrayList<V> women$value;
+ private java.util.ArrayList<K> men$key;
+ private java.util.ArrayList<Number> men$value;
+ private java.util.ArrayList<java.lang.Object> rawMap$key;
+ private java.util.ArrayList<java.lang.Object> rawMap$value;
+ private java.util.ArrayList<String> stringMap$key;
+ private java.util.ArrayList<V> stringMap$value;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularMapsBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMapsBuilder<K, V> woman(final K womanKey, final V womanValue) {
+ if (this.women$key == null) {
+ this.women$key = new java.util.ArrayList<K>();
+ this.women$value = new java.util.ArrayList<V>();
+ }
+ this.women$key.add(womanKey);
+ this.women$value.add(womanValue);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMapsBuilder<K, V> women(final java.util.Map<? extends K, ? extends V> women) {
+ if (this.women$key == null) {
+ this.women$key = new java.util.ArrayList<K>();
+ this.women$value = new java.util.ArrayList<V>();
+ }
+ for (final java.util.Map.Entry<? extends K, ? extends V> $lombokEntry : women.entrySet()) {
+ this.women$key.add($lombokEntry.getKey());
+ this.women$value.add($lombokEntry.getValue());
+ }
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMapsBuilder<K, V> man(final K manKey, final Number manValue) {
+ if (this.men$key == null) {
+ this.men$key = new java.util.ArrayList<K>();
+ this.men$value = new java.util.ArrayList<Number>();
+ }
+ this.men$key.add(manKey);
+ this.men$value.add(manValue);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMapsBuilder<K, V> men(final java.util.Map<? extends K, ? extends Number> men) {
+ if (this.men$key == null) {
+ this.men$key = new java.util.ArrayList<K>();
+ this.men$value = new java.util.ArrayList<Number>();
+ }
+ for (final java.util.Map.Entry<? extends K, ? extends Number> $lombokEntry : men.entrySet()) {
+ this.men$key.add($lombokEntry.getKey());
+ this.men$value.add($lombokEntry.getValue());
+ }
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMapsBuilder<K, V> rawMap(final java.lang.Object rawMapKey, final java.lang.Object rawMapValue) {
+ if (this.rawMap$key == null) {
+ this.rawMap$key = new java.util.ArrayList<java.lang.Object>();
+ this.rawMap$value = new java.util.ArrayList<java.lang.Object>();
+ }
+ this.rawMap$key.add(rawMapKey);
+ this.rawMap$value.add(rawMapValue);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMapsBuilder<K, V> rawMap(final java.util.Map<?, ?> rawMap) {
+ if (this.rawMap$key == null) {
+ this.rawMap$key = new java.util.ArrayList<java.lang.Object>();
+ this.rawMap$value = new java.util.ArrayList<java.lang.Object>();
+ }
+ for (final java.util.Map.Entry<?, ?> $lombokEntry : rawMap.entrySet()) {
+ this.rawMap$key.add($lombokEntry.getKey());
+ this.rawMap$value.add($lombokEntry.getValue());
+ }
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMapsBuilder<K, V> stringMap(final String stringMapKey, final V stringMapValue) {
+ if (this.stringMap$key == null) {
+ this.stringMap$key = new java.util.ArrayList<String>();
+ this.stringMap$value = new java.util.ArrayList<V>();
+ }
+ this.stringMap$key.add(stringMapKey);
+ this.stringMap$value.add(stringMapValue);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMapsBuilder<K, V> stringMap(final java.util.Map<? extends String, ? extends V> stringMap) {
+ if (this.stringMap$key == null) {
+ this.stringMap$key = new java.util.ArrayList<String>();
+ this.stringMap$value = new java.util.ArrayList<V>();
+ }
+ for (final java.util.Map.Entry<? extends String, ? extends V> $lombokEntry : stringMap.entrySet()) {
+ this.stringMap$key.add($lombokEntry.getKey());
+ this.stringMap$value.add($lombokEntry.getValue());
+ }
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularMaps<K, V> build() {
+ java.util.Map<K, V> women;
+ switch (this.women$key == null ? 0 : this.women$key.size()) {
+ case 0:
+ women = java.util.Collections.emptyMap();
+ break;
+ case 1:
+ women = java.util.Collections.singletonMap(this.women$key.get(0), this.women$value.get(0));
+ break;
+ default:
+ women = new java.util.LinkedHashMap<K, V>(this.women$key.size() < 1073741824 ? 1 + this.women$key.size() + (this.women$key.size() - 3) / 3 : java.lang.Integer.MAX_VALUE);
+ for (int $i = 0; $i < this.women$key.size(); $i++) women.put(this.women$key.get($i), this.women$value.get($i));
+ women = java.util.Collections.unmodifiableMap(women);
+ }
+ java.util.SortedMap<K, Number> men = new java.util.TreeMap<K, Number>();
+ if (this.men$key != null) for (int $i = 0; $i < (this.men$key == null ? 0 : this.men$key.size()); $i++) men.put(this.men$key.get($i), this.men$value.get($i));
+ men = java.util.Collections.unmodifiableSortedMap(men);
+ java.util.Map<java.lang.Object, java.lang.Object> rawMap;
+ switch (this.rawMap$key == null ? 0 : this.rawMap$key.size()) {
+ case 0:
+ rawMap = java.util.Collections.emptyMap();
+ break;
+ case 1:
+ rawMap = java.util.Collections.singletonMap(this.rawMap$key.get(0), this.rawMap$value.get(0));
+ break;
+ default:
+ rawMap = new java.util.LinkedHashMap<java.lang.Object, java.lang.Object>(this.rawMap$key.size() < 1073741824 ? 1 + this.rawMap$key.size() + (this.rawMap$key.size() - 3) / 3 : java.lang.Integer.MAX_VALUE);
+ for (int $i = 0; $i < this.rawMap$key.size(); $i++) rawMap.put(this.rawMap$key.get($i), this.rawMap$value.get($i));
+ rawMap = java.util.Collections.unmodifiableMap(rawMap);
+ }
+ java.util.Map<String, V> stringMap;
+ switch (this.stringMap$key == null ? 0 : this.stringMap$key.size()) {
+ case 0:
+ stringMap = java.util.Collections.emptyMap();
+ break;
+ case 1:
+ stringMap = java.util.Collections.singletonMap(this.stringMap$key.get(0), this.stringMap$value.get(0));
+ break;
+ default:
+ stringMap = new java.util.LinkedHashMap<String, V>(this.stringMap$key.size() < 1073741824 ? 1 + this.stringMap$key.size() + (this.stringMap$key.size() - 3) / 3 : java.lang.Integer.MAX_VALUE);
+ for (int $i = 0; $i < this.stringMap$key.size(); $i++) stringMap.put(this.stringMap$key.get($i), this.stringMap$value.get($i));
+ stringMap = java.util.Collections.unmodifiableMap(stringMap);
+ }
+ return new BuilderSingularMaps<K, V>(women, men, rawMap, stringMap);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderSingularMaps.BuilderSingularMapsBuilder(women$key=" + this.women$key + ", women$value=" + this.women$value + ", men$key=" + this.men$key + ", men$value=" + this.men$value + ", rawMap$key=" + this.rawMap$key + ", rawMap$value=" + this.rawMap$value + ", stringMap$key=" + this.stringMap$key + ", stringMap$value=" + this.stringMap$value + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ public static <K, V> BuilderSingularMapsBuilder<K, V> builder() {
+ return new BuilderSingularMapsBuilder<K, V>();
+ }
+}
diff --git a/test/transform/resource/after-delombok/BuilderSingularNoAutosingularize.java b/test/transform/resource/after-delombok/BuilderSingularNoAutosingularize.java
new file mode 100644
index 00000000..07bbef9c
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderSingularNoAutosingularize.java
@@ -0,0 +1,109 @@
+import java.util.List;
+class BuilderSingularNoAutosingularize {
+ private List<String> things;
+ private List<String> widgets;
+ private List<String> items;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularNoAutosingularize(final List<String> things, final List<String> widgets, final List<String> items) {
+ this.things = things;
+ this.widgets = widgets;
+ this.items = items;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static class BuilderSingularNoAutosingularizeBuilder {
+ private java.util.ArrayList<String> things;
+ private java.util.ArrayList<String> widgets;
+ private java.util.ArrayList<String> items;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularNoAutosingularizeBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularNoAutosingularizeBuilder things(final String things) {
+ if (this.things == null) this.things = new java.util.ArrayList<String>();
+ this.things.add(things);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularNoAutosingularizeBuilder things(final java.util.Collection<? extends String> things) {
+ if (this.things == null) this.things = new java.util.ArrayList<String>();
+ this.things.addAll(things);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularNoAutosingularizeBuilder widget(final String widget) {
+ if (this.widgets == null) this.widgets = new java.util.ArrayList<String>();
+ this.widgets.add(widget);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularNoAutosingularizeBuilder widgets(final java.util.Collection<? extends String> widgets) {
+ if (this.widgets == null) this.widgets = new java.util.ArrayList<String>();
+ this.widgets.addAll(widgets);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularNoAutosingularizeBuilder items(final String items) {
+ if (this.items == null) this.items = new java.util.ArrayList<String>();
+ this.items.add(items);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularNoAutosingularizeBuilder items(final java.util.Collection<? extends String> items) {
+ if (this.items == null) this.items = new java.util.ArrayList<String>();
+ this.items.addAll(items);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularNoAutosingularize build() {
+ java.util.List<String> things;
+ switch (this.things == null ? 0 : this.things.size()) {
+ case 0:
+ things = java.util.Collections.emptyList();
+ break;
+ case 1:
+ things = java.util.Collections.singletonList(this.things.get(0));
+ break;
+ default:
+ things = new java.util.ArrayList<String>(this.things.size());
+ things.addAll(this.things);
+ things = java.util.Collections.unmodifiableList(things);
+ }
+ java.util.List<String> widgets;
+ switch (this.widgets == null ? 0 : this.widgets.size()) {
+ case 0:
+ widgets = java.util.Collections.emptyList();
+ break;
+ case 1:
+ widgets = java.util.Collections.singletonList(this.widgets.get(0));
+ break;
+ default:
+ widgets = new java.util.ArrayList<String>(this.widgets.size());
+ widgets.addAll(this.widgets);
+ widgets = java.util.Collections.unmodifiableList(widgets);
+ }
+ java.util.List<String> items;
+ switch (this.items == null ? 0 : this.items.size()) {
+ case 0:
+ items = java.util.Collections.emptyList();
+ break;
+ case 1:
+ items = java.util.Collections.singletonList(this.items.get(0));
+ break;
+ default:
+ items = new java.util.ArrayList<String>(this.items.size());
+ items.addAll(this.items);
+ items = java.util.Collections.unmodifiableList(items);
+ }
+ return new BuilderSingularNoAutosingularize(things, widgets, items);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderSingularNoAutosingularize.BuilderSingularNoAutosingularizeBuilder(things=" + this.things + ", widgets=" + this.widgets + ", items=" + this.items + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ public static BuilderSingularNoAutosingularizeBuilder builder() {
+ return new BuilderSingularNoAutosingularizeBuilder();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java b/test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java
new file mode 100644
index 00000000..84dc9a26
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderSingularRedirectToGuava.java
@@ -0,0 +1,75 @@
+import java.util.Set;
+import java.util.NavigableMap;
+import java.util.Collection;
+class BuilderSingularRedirectToGuava {
+ private Set<String> dangerMice;
+ private NavigableMap<Integer, Number> things;
+ private Collection<Class<?>> doohickeys;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularRedirectToGuava(final Set<String> dangerMice, final NavigableMap<Integer, Number> things, final Collection<Class<?>> doohickeys) {
+ this.dangerMice = dangerMice;
+ this.things = things;
+ this.doohickeys = doohickeys;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static class BuilderSingularRedirectToGuavaBuilder {
+ private com.google.common.collect.ImmutableSet.Builder<String> dangerMice;
+ private com.google.common.collect.ImmutableSortedMap.Builder<Integer, Number> things;
+ private com.google.common.collect.ImmutableList.Builder<Class<?>> doohickeys;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularRedirectToGuavaBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularRedirectToGuavaBuilder dangerMouse(final String dangerMouse) {
+ if (this.dangerMice == null) this.dangerMice = com.google.common.collect.ImmutableSet.builder();
+ this.dangerMice.add(dangerMouse);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularRedirectToGuavaBuilder dangerMice(final java.lang.Iterable<? extends String> dangerMice) {
+ if (this.dangerMice == null) this.dangerMice = com.google.common.collect.ImmutableSet.builder();
+ this.dangerMice.addAll(dangerMice);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularRedirectToGuavaBuilder thing(final Integer thing$key, final Number thing$value) {
+ if (this.things == null) this.things = com.google.common.collect.ImmutableSortedMap.naturalOrder();
+ this.things.put(thing$key, thing$value);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularRedirectToGuavaBuilder things(final java.util.Map<? extends Integer, ? extends Number> things) {
+ if (this.things == null) this.things = com.google.common.collect.ImmutableSortedMap.naturalOrder();
+ this.things.putAll(things);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularRedirectToGuavaBuilder doohickey(final Class<?> doohickey) {
+ if (this.doohickeys == null) this.doohickeys = com.google.common.collect.ImmutableList.builder();
+ this.doohickeys.add(doohickey);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularRedirectToGuavaBuilder doohickeys(final java.lang.Iterable<? extends Class<?>> doohickeys) {
+ if (this.doohickeys == null) this.doohickeys = com.google.common.collect.ImmutableList.builder();
+ this.doohickeys.addAll(doohickeys);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularRedirectToGuava build() {
+ java.util.Set<String> dangerMice = this.dangerMice == null ? com.google.common.collect.ImmutableSet.of() : this.dangerMice.build();
+ java.util.NavigableMap<Integer, Number> things = this.things == null ? com.google.common.collect.ImmutableSortedMap.of() : this.things.build();
+ java.util.Collection<Class<?>> doohickeys = this.doohickeys == null ? com.google.common.collect.ImmutableList.of() : this.doohickeys.build();
+ return new BuilderSingularRedirectToGuava(dangerMice, things, doohickeys);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderSingularRedirectToGuava.BuilderSingularRedirectToGuavaBuilder(dangerMice=" + this.dangerMice + ", things=" + this.things + ", doohickeys=" + this.doohickeys + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ public static BuilderSingularRedirectToGuavaBuilder builder() {
+ return new BuilderSingularRedirectToGuavaBuilder();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/BuilderSingularSets.java b/test/transform/resource/after-delombok/BuilderSingularSets.java
new file mode 100644
index 00000000..d75c0fbf
--- /dev/null
+++ b/test/transform/resource/after-delombok/BuilderSingularSets.java
@@ -0,0 +1,129 @@
+import java.util.Set;
+import java.util.SortedSet;
+class BuilderSingularSets<T> {
+ private Set<T> dangerMice;
+ private SortedSet<? extends Number> octopodes;
+ @SuppressWarnings("all")
+ private Set rawSet;
+ private Set<String> stringSet;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularSets(final Set<T> dangerMice, final SortedSet<? extends Number> octopodes, final Set rawSet, final Set<String> stringSet) {
+ this.dangerMice = dangerMice;
+ this.octopodes = octopodes;
+ this.rawSet = rawSet;
+ this.stringSet = stringSet;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static class BuilderSingularSetsBuilder<T> {
+ private java.util.ArrayList<T> dangerMice;
+ private java.util.ArrayList<Number> octopodes;
+ private java.util.ArrayList<java.lang.Object> rawSet;
+ private java.util.ArrayList<String> stringSet;
+ @java.lang.SuppressWarnings("all")
+ BuilderSingularSetsBuilder() {
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSetsBuilder<T> dangerMouse(final T dangerMouse) {
+ if (this.dangerMice == null) this.dangerMice = new java.util.ArrayList<T>();
+ this.dangerMice.add(dangerMouse);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSetsBuilder<T> dangerMice(final java.util.Collection<? extends T> dangerMice) {
+ if (this.dangerMice == null) this.dangerMice = new java.util.ArrayList<T>();
+ this.dangerMice.addAll(dangerMice);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSetsBuilder<T> octopus(final Number octopus) {
+ if (this.octopodes == null) this.octopodes = new java.util.ArrayList<Number>();
+ this.octopodes.add(octopus);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSetsBuilder<T> octopodes(final java.util.Collection<? extends Number> octopodes) {
+ if (this.octopodes == null) this.octopodes = new java.util.ArrayList<Number>();
+ this.octopodes.addAll(octopodes);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSetsBuilder<T> rawSet(final java.lang.Object rawSet) {
+ if (this.rawSet == null) this.rawSet = new java.util.ArrayList<java.lang.Object>();
+ this.rawSet.add(rawSet);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSetsBuilder<T> rawSet(final java.util.Collection<?> rawSet) {
+ if (this.rawSet == null) this.rawSet = new java.util.ArrayList<java.lang.Object>();
+ this.rawSet.addAll(rawSet);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSetsBuilder<T> stringSet(final String stringSet) {
+ if (this.stringSet == null) this.stringSet = new java.util.ArrayList<String>();
+ this.stringSet.add(stringSet);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSetsBuilder<T> stringSet(final java.util.Collection<? extends String> stringSet) {
+ if (this.stringSet == null) this.stringSet = new java.util.ArrayList<String>();
+ this.stringSet.addAll(stringSet);
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public BuilderSingularSets<T> build() {
+ java.util.Set<T> dangerMice;
+ switch (this.dangerMice == null ? 0 : this.dangerMice.size()) {
+ case 0:
+ dangerMice = java.util.Collections.emptySet();
+ break;
+ case 1:
+ dangerMice = java.util.Collections.singleton(this.dangerMice.get(0));
+ break;
+ default:
+ dangerMice = new java.util.LinkedHashSet<T>(this.dangerMice.size() < 1073741824 ? 1 + this.dangerMice.size() + (this.dangerMice.size() - 3) / 3 : java.lang.Integer.MAX_VALUE);
+ dangerMice.addAll(this.dangerMice);
+ dangerMice = java.util.Collections.unmodifiableSet(dangerMice);
+ }
+ java.util.SortedSet<Number> octopodes = new java.util.TreeSet<Number>();
+ if (this.octopodes != null) octopodes.addAll(this.octopodes);
+ octopodes = java.util.Collections.unmodifiableSortedSet(octopodes);
+ java.util.Set<java.lang.Object> rawSet;
+ switch (this.rawSet == null ? 0 : this.rawSet.size()) {
+ case 0:
+ rawSet = java.util.Collections.emptySet();
+ break;
+ case 1:
+ rawSet = java.util.Collections.singleton(this.rawSet.get(0));
+ break;
+ default:
+ rawSet = new java.util.LinkedHashSet<java.lang.Object>(this.rawSet.size() < 1073741824 ? 1 + this.rawSet.size() + (this.rawSet.size() - 3) / 3 : java.lang.Integer.MAX_VALUE);
+ rawSet.addAll(this.rawSet);
+ rawSet = java.util.Collections.unmodifiableSet(rawSet);
+ }
+ java.util.Set<String> stringSet;
+ switch (this.stringSet == null ? 0 : this.stringSet.size()) {
+ case 0:
+ stringSet = java.util.Collections.emptySet();
+ break;
+ case 1:
+ stringSet = java.util.Collections.singleton(this.stringSet.get(0));
+ break;
+ default:
+ stringSet = new java.util.LinkedHashSet<String>(this.stringSet.size() < 1073741824 ? 1 + this.stringSet.size() + (this.stringSet.size() - 3) / 3 : java.lang.Integer.MAX_VALUE);
+ stringSet.addAll(this.stringSet);
+ stringSet = java.util.Collections.unmodifiableSet(stringSet);
+ }
+ return new BuilderSingularSets<T>(dangerMice, octopodes, rawSet, stringSet);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "BuilderSingularSets.BuilderSingularSetsBuilder(dangerMice=" + this.dangerMice + ", octopodes=" + this.octopodes + ", rawSet=" + this.rawSet + ", stringSet=" + this.stringSet + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ public static <T> BuilderSingularSetsBuilder<T> builder() {
+ return new BuilderSingularSetsBuilder<T>();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java b/test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java
new file mode 100644
index 00000000..f95ce1ea
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderSingularGuavaListsSets.java
@@ -0,0 +1,88 @@
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import lombok.Singular;
+@lombok.Builder class BuilderSingularGuavaListsSets<T> {
+ public static @java.lang.SuppressWarnings("all") class BuilderSingularGuavaListsSetsBuilder<T> {
+ private com.google.common.collect.ImmutableList.Builder<T> cards;
+ private com.google.common.collect.ImmutableList.Builder<Number> frogs;
+ private com.google.common.collect.ImmutableSet.Builder<java.lang.Object> rawSet;
+ private com.google.common.collect.ImmutableSortedSet.Builder<String> passes;
+ @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder<T> card(T card) {
+ if ((this.cards == null))
+ this.cards = com.google.common.collect.ImmutableList.builder();
+ this.cards.add(card);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder<T> cards(java.lang.Iterable<? extends T> cards) {
+ if ((this.cards == null))
+ this.cards = com.google.common.collect.ImmutableList.builder();
+ this.cards.addAll(cards);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder<T> frog(Number frog) {
+ if ((this.frogs == null))
+ this.frogs = com.google.common.collect.ImmutableList.builder();
+ this.frogs.add(frog);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder<T> frogs(java.lang.Iterable<? extends Number> frogs) {
+ if ((this.frogs == null))
+ this.frogs = com.google.common.collect.ImmutableList.builder();
+ this.frogs.addAll(frogs);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder<T> rawSet(java.lang.Object rawSet) {
+ if ((this.rawSet == null))
+ this.rawSet = com.google.common.collect.ImmutableSet.builder();
+ this.rawSet.add(rawSet);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder<T> rawSet(java.lang.Iterable<?> rawSet) {
+ if ((this.rawSet == null))
+ this.rawSet = com.google.common.collect.ImmutableSet.builder();
+ this.rawSet.addAll(rawSet);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder<T> pass(String pass) {
+ if ((this.passes == null))
+ this.passes = com.google.common.collect.ImmutableSortedSet.naturalOrder();
+ this.passes.add(pass);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSetsBuilder<T> passes(java.lang.Iterable<? extends String> passes) {
+ if ((this.passes == null))
+ this.passes = com.google.common.collect.ImmutableSortedSet.naturalOrder();
+ this.passes.addAll(passes);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSets<T> build() {
+ com.google.common.collect.ImmutableList<T> cards = ((this.cards == null) ? com.google.common.collect.ImmutableList.of() : this.cards.build());
+ com.google.common.collect.ImmutableCollection<Number> frogs = ((this.frogs == null) ? com.google.common.collect.ImmutableList.of() : this.frogs.build());
+ com.google.common.collect.ImmutableSet<java.lang.Object> rawSet = ((this.rawSet == null) ? com.google.common.collect.ImmutableSet.of() : this.rawSet.build());
+ com.google.common.collect.ImmutableSortedSet<String> passes = ((this.passes == null) ? com.google.common.collect.ImmutableSortedSet.of() : this.passes.build());
+ return new BuilderSingularGuavaListsSets<T>(cards, frogs, rawSet, passes);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((((("BuilderSingularGuavaListsSets.BuilderSingularGuavaListsSetsBuilder(cards=" + this.cards) + ", frogs=") + this.frogs) + ", rawSet=") + this.rawSet) + ", passes=") + this.passes) + ")");
+ }
+ }
+ private @Singular ImmutableList<T> cards;
+ private @Singular ImmutableCollection<? extends Number> frogs;
+ private @SuppressWarnings("all") @Singular("rawSet") ImmutableSet rawSet;
+ private @Singular ImmutableSortedSet<String> passes;
+ @java.lang.SuppressWarnings("all") BuilderSingularGuavaListsSets(final ImmutableList<T> cards, final ImmutableCollection<? extends Number> frogs, final ImmutableSet rawSet, final ImmutableSortedSet<String> passes) {
+ super();
+ this.cards = cards;
+ this.frogs = frogs;
+ this.rawSet = rawSet;
+ this.passes = passes;
+ }
+ public static @java.lang.SuppressWarnings("all") <T>BuilderSingularGuavaListsSetsBuilder<T> builder() {
+ return new BuilderSingularGuavaListsSetsBuilder<T>();
+ }
+}
diff --git a/test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java b/test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java
new file mode 100644
index 00000000..64f287bf
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderSingularGuavaMaps.java
@@ -0,0 +1,71 @@
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableSortedMap;
+import lombok.Singular;
+@lombok.Builder class BuilderSingularGuavaMaps<K, V> {
+ public static @java.lang.SuppressWarnings("all") class BuilderSingularGuavaMapsBuilder<K, V> {
+ private com.google.common.collect.ImmutableMap.Builder<K, V> battleaxes;
+ private com.google.common.collect.ImmutableSortedMap.Builder<Integer, V> vertices;
+ private com.google.common.collect.ImmutableBiMap.Builder<java.lang.Object, java.lang.Object> rawMap;
+ @java.lang.SuppressWarnings("all") BuilderSingularGuavaMapsBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaMapsBuilder<K, V> battleaxe(K battleaxe$key, V battleaxe$value) {
+ if ((this.battleaxes == null))
+ this.battleaxes = com.google.common.collect.ImmutableMap.builder();
+ this.battleaxes.put(battleaxe$key, battleaxe$value);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaMapsBuilder<K, V> battleaxes(java.util.Map<? extends K, ? extends V> battleaxes) {
+ if ((this.battleaxes == null))
+ this.battleaxes = com.google.common.collect.ImmutableMap.builder();
+ this.battleaxes.putAll(battleaxes);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaMapsBuilder<K, V> vertex(Integer vertex$key, V vertex$value) {
+ if ((this.vertices == null))
+ this.vertices = com.google.common.collect.ImmutableSortedMap.naturalOrder();
+ this.vertices.put(vertex$key, vertex$value);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaMapsBuilder<K, V> vertices(java.util.Map<? extends Integer, ? extends V> vertices) {
+ if ((this.vertices == null))
+ this.vertices = com.google.common.collect.ImmutableSortedMap.naturalOrder();
+ this.vertices.putAll(vertices);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaMapsBuilder<K, V> rawMap(java.lang.Object rawMap$key, java.lang.Object rawMap$value) {
+ if ((this.rawMap == null))
+ this.rawMap = com.google.common.collect.ImmutableBiMap.builder();
+ this.rawMap.put(rawMap$key, rawMap$value);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaMapsBuilder<K, V> rawMap(java.util.Map<?, ?> rawMap) {
+ if ((this.rawMap == null))
+ this.rawMap = com.google.common.collect.ImmutableBiMap.builder();
+ this.rawMap.putAll(rawMap);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularGuavaMaps<K, V> build() {
+ com.google.common.collect.ImmutableMap<K, V> battleaxes = ((this.battleaxes == null) ? com.google.common.collect.ImmutableMap.of() : this.battleaxes.build());
+ com.google.common.collect.ImmutableSortedMap<Integer, V> vertices = ((this.vertices == null) ? com.google.common.collect.ImmutableSortedMap.of() : this.vertices.build());
+ com.google.common.collect.ImmutableBiMap<java.lang.Object, java.lang.Object> rawMap = ((this.rawMap == null) ? com.google.common.collect.ImmutableBiMap.of() : this.rawMap.build());
+ return new BuilderSingularGuavaMaps<K, V>(battleaxes, vertices, rawMap);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((("BuilderSingularGuavaMaps.BuilderSingularGuavaMapsBuilder(battleaxes=" + this.battleaxes) + ", vertices=") + this.vertices) + ", rawMap=") + this.rawMap) + ")");
+ }
+ }
+ private @Singular ImmutableMap<K, V> battleaxes;
+ private @Singular ImmutableSortedMap<Integer, ? extends V> vertices;
+ private @SuppressWarnings("all") @Singular("rawMap") ImmutableBiMap rawMap;
+ @java.lang.SuppressWarnings("all") BuilderSingularGuavaMaps(final ImmutableMap<K, V> battleaxes, final ImmutableSortedMap<Integer, ? extends V> vertices, final ImmutableBiMap rawMap) {
+ super();
+ this.battleaxes = battleaxes;
+ this.vertices = vertices;
+ this.rawMap = rawMap;
+ }
+ public static @java.lang.SuppressWarnings("all") <K, V>BuilderSingularGuavaMapsBuilder<K, V> builder() {
+ return new BuilderSingularGuavaMapsBuilder<K, V>();
+ }
+}
diff --git a/test/transform/resource/after-ecj/BuilderSingularLists.java b/test/transform/resource/after-ecj/BuilderSingularLists.java
new file mode 100644
index 00000000..c0a3ef9a
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderSingularLists.java
@@ -0,0 +1,107 @@
+import java.util.List;
+import java.util.Collection;
+
+import lombok.Singular;
+@lombok.Builder class BuilderSingularLists<T> {
+ public static @java.lang.SuppressWarnings("all") class BuilderSingularListsBuilder<T> {
+ private java.util.ArrayList<T> children;
+ private java.util.ArrayList<Number> scarves;
+ private java.util.ArrayList<java.lang.Object> rawList;
+ @java.lang.SuppressWarnings("all") BuilderSingularListsBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularListsBuilder<T> child(T child) {
+ if ((this.children == null))
+ this.children = new java.util.ArrayList<T>();
+ this.children.add(child);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularListsBuilder<T> children(java.util.Collection<? extends T> children) {
+ if ((this.children == null))
+ this.children = new java.util.ArrayList<T>();
+ this.children.addAll(children);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularListsBuilder<T> scarf(Number scarf) {
+ if ((this.scarves == null))
+ this.scarves = new java.util.ArrayList<Number>();
+ this.scarves.add(scarf);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularListsBuilder<T> scarves(java.util.Collection<? extends Number> scarves) {
+ if ((this.scarves == null))
+ this.scarves = new java.util.ArrayList<Number>();
+ this.scarves.addAll(scarves);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularListsBuilder<T> rawList(java.lang.Object rawList) {
+ if ((this.rawList == null))
+ this.rawList = new java.util.ArrayList<java.lang.Object>();
+ this.rawList.add(rawList);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularListsBuilder<T> rawList(java.util.Collection<?> rawList) {
+ if ((this.rawList == null))
+ this.rawList = new java.util.ArrayList<java.lang.Object>();
+ this.rawList.addAll(rawList);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularLists<T> build() {
+ java.util.List<T> 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<T>(this.children.size());
+ children.addAll(this.children);
+ children = java.util.Collections.unmodifiableList(children);
+ }
+ java.util.List<Number> 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<Number>(this.scarves.size());
+ scarves.addAll(this.scarves);
+ scarves = java.util.Collections.unmodifiableList(scarves);
+ }
+ java.util.List<java.lang.Object> 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<java.lang.Object>(this.rawList.size());
+ rawList.addAll(this.rawList);
+ rawList = java.util.Collections.unmodifiableList(rawList);
+ }
+ return new BuilderSingularLists<T>(children, scarves, rawList);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((("BuilderSingularLists.BuilderSingularListsBuilder(children=" + this.children) + ", scarves=") + this.scarves) + ", rawList=") + this.rawList) + ")");
+ }
+ }
+ private @Singular List<T> children;
+ private @Singular Collection<? extends Number> scarves;
+ private @SuppressWarnings("all") @Singular("rawList") List rawList;
+ @java.lang.SuppressWarnings("all") BuilderSingularLists(final List<T> children, final Collection<? extends Number> scarves, final List rawList) {
+ super();
+ this.children = children;
+ this.scarves = scarves;
+ this.rawList = rawList;
+ }
+ public static @java.lang.SuppressWarnings("all") <T>BuilderSingularListsBuilder<T> builder() {
+ return new BuilderSingularListsBuilder<T>();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/BuilderSingularMaps.java b/test/transform/resource/after-ecj/BuilderSingularMaps.java
new file mode 100644
index 00000000..0ce5a0a9
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderSingularMaps.java
@@ -0,0 +1,177 @@
+import java.util.Map;
+import java.util.SortedMap;
+import lombok.Singular;
+@lombok.Builder class BuilderSingularMaps<K, V> {
+ public static @java.lang.SuppressWarnings("all") class BuilderSingularMapsBuilder<K, V> {
+ private java.util.ArrayList<K> women$key;
+ private java.util.ArrayList<V> women$value;
+ private java.util.ArrayList<K> men$key;
+ private java.util.ArrayList<Number> men$value;
+ private java.util.ArrayList<java.lang.Object> rawMap$key;
+ private java.util.ArrayList<java.lang.Object> rawMap$value;
+ private java.util.ArrayList<String> stringMap$key;
+ private java.util.ArrayList<V> stringMap$value;
+ @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder<K, V> woman(K womanKey, V womanValue) {
+ if ((this.women$key == null))
+ {
+ this.women$key = new java.util.ArrayList<K>();
+ this.women$value = new java.util.ArrayList<V>();
+ }
+ this.women$key.add(womanKey);
+ this.women$value.add(womanValue);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder<K, V> women(java.util.Map<? extends K, ? extends V> women) {
+ if ((this.women$key == null))
+ {
+ this.women$key = new java.util.ArrayList<K>();
+ this.women$value = new java.util.ArrayList<V>();
+ }
+ for (java.util.Map.Entry<? extends K, ? extends V> $lombokEntry : women.entrySet())
+ {
+ this.women$key.add($lombokEntry.getKey());
+ this.women$value.add($lombokEntry.getValue());
+ }
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder<K, V> man(K manKey, Number manValue) {
+ if ((this.men$key == null))
+ {
+ this.men$key = new java.util.ArrayList<K>();
+ this.men$value = new java.util.ArrayList<Number>();
+ }
+ this.men$key.add(manKey);
+ this.men$value.add(manValue);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder<K, V> men(java.util.Map<? extends K, ? extends Number> men) {
+ if ((this.men$key == null))
+ {
+ this.men$key = new java.util.ArrayList<K>();
+ this.men$value = new java.util.ArrayList<Number>();
+ }
+ for (java.util.Map.Entry<? extends K, ? extends Number> $lombokEntry : men.entrySet())
+ {
+ this.men$key.add($lombokEntry.getKey());
+ this.men$value.add($lombokEntry.getValue());
+ }
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder<K, V> rawMap(java.lang.Object rawMapKey, java.lang.Object rawMapValue) {
+ if ((this.rawMap$key == null))
+ {
+ this.rawMap$key = new java.util.ArrayList<java.lang.Object>();
+ this.rawMap$value = new java.util.ArrayList<java.lang.Object>();
+ }
+ this.rawMap$key.add(rawMapKey);
+ this.rawMap$value.add(rawMapValue);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder<K, V> rawMap(java.util.Map<?, ?> rawMap) {
+ if ((this.rawMap$key == null))
+ {
+ this.rawMap$key = new java.util.ArrayList<java.lang.Object>();
+ this.rawMap$value = new java.util.ArrayList<java.lang.Object>();
+ }
+ for (java.util.Map.Entry<?, ?> $lombokEntry : rawMap.entrySet())
+ {
+ this.rawMap$key.add($lombokEntry.getKey());
+ this.rawMap$value.add($lombokEntry.getValue());
+ }
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder<K, V> stringMap(String stringMapKey, V stringMapValue) {
+ if ((this.stringMap$key == null))
+ {
+ this.stringMap$key = new java.util.ArrayList<String>();
+ this.stringMap$value = new java.util.ArrayList<V>();
+ }
+ this.stringMap$key.add(stringMapKey);
+ this.stringMap$value.add(stringMapValue);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMapsBuilder<K, V> stringMap(java.util.Map<? extends String, ? extends V> stringMap) {
+ if ((this.stringMap$key == null))
+ {
+ this.stringMap$key = new java.util.ArrayList<String>();
+ this.stringMap$value = new java.util.ArrayList<V>();
+ }
+ for (java.util.Map.Entry<? extends String, ? extends V> $lombokEntry : stringMap.entrySet())
+ {
+ this.stringMap$key.add($lombokEntry.getKey());
+ this.stringMap$value.add($lombokEntry.getValue());
+ }
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularMaps<K, V> build() {
+ java.util.Map<K, V> women;
+ switch (((this.women$key == null) ? 0 : this.women$key.size())) {
+ case 0 :
+ women = java.util.Collections.emptyMap();
+ break;
+ case 1 :
+ women = java.util.Collections.singletonMap(this.women$key.get(0), this.women$value.get(0));
+ break;
+ default :
+ women = new java.util.LinkedHashMap<K, V>(((this.women$key.size() < 0x40000000) ? ((1 + this.women$key.size()) + ((this.women$key.size() - 3) / 3)) : java.lang.Integer.MAX_VALUE));
+ for (int $i = 0;; ($i < this.women$key.size()); $i ++)
+ women.put(this.women$key.get($i), this.women$value.get($i));
+ women = java.util.Collections.unmodifiableMap(women);
+ }
+ java.util.SortedMap<K, Number> men = new java.util.TreeMap<K, Number>();
+ if ((this.men$key != null))
+ for (int $i = 0;; ($i < ((this.men$key == null) ? 0 : this.men$key.size())); $i ++)
+ men.put(this.men$key.get($i), this.men$value.get($i));
+ men = java.util.Collections.unmodifiableSortedMap(men);
+ java.util.Map<java.lang.Object, java.lang.Object> rawMap;
+ switch (((this.rawMap$key == null) ? 0 : this.rawMap$key.size())) {
+ case 0 :
+ rawMap = java.util.Collections.emptyMap();
+ break;
+ case 1 :
+ rawMap = java.util.Collections.singletonMap(this.rawMap$key.get(0), this.rawMap$value.get(0));
+ break;
+ default :
+ rawMap = new java.util.LinkedHashMap<java.lang.Object, java.lang.Object>(((this.rawMap$key.size() < 0x40000000) ? ((1 + this.rawMap$key.size()) + ((this.rawMap$key.size() - 3) / 3)) : java.lang.Integer.MAX_VALUE));
+ for (int $i = 0;; ($i < this.rawMap$key.size()); $i ++)
+ rawMap.put(this.rawMap$key.get($i), this.rawMap$value.get($i));
+ rawMap = java.util.Collections.unmodifiableMap(rawMap);
+ }
+ java.util.Map<String, V> stringMap;
+ switch (((this.stringMap$key == null) ? 0 : this.stringMap$key.size())) {
+ case 0 :
+ stringMap = java.util.Collections.emptyMap();
+ break;
+ case 1 :
+ stringMap = java.util.Collections.singletonMap(this.stringMap$key.get(0), this.stringMap$value.get(0));
+ break;
+ default :
+ stringMap = new java.util.LinkedHashMap<String, V>(((this.stringMap$key.size() < 0x40000000) ? ((1 + this.stringMap$key.size()) + ((this.stringMap$key.size() - 3) / 3)) : java.lang.Integer.MAX_VALUE));
+ for (int $i = 0;; ($i < this.stringMap$key.size()); $i ++)
+ stringMap.put(this.stringMap$key.get($i), this.stringMap$value.get($i));
+ stringMap = java.util.Collections.unmodifiableMap(stringMap);
+ }
+ return new BuilderSingularMaps<K, V>(women, men, rawMap, stringMap);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((((((((((((("BuilderSingularMaps.BuilderSingularMapsBuilder(women$key=" + this.women$key) + ", women$value=") + this.women$value) + ", men$key=") + this.men$key) + ", men$value=") + this.men$value) + ", rawMap$key=") + this.rawMap$key) + ", rawMap$value=") + this.rawMap$value) + ", stringMap$key=") + this.stringMap$key) + ", stringMap$value=") + this.stringMap$value) + ")");
+ }
+ }
+ private @Singular Map<K, V> women;
+ private @Singular SortedMap<K, ? extends Number> men;
+ private @SuppressWarnings("all") @Singular("rawMap") Map rawMap;
+ private @Singular("stringMap") Map<String, V> stringMap;
+ @java.lang.SuppressWarnings("all") BuilderSingularMaps(final Map<K, V> women, final SortedMap<K, ? extends Number> men, final Map rawMap, final Map<String, V> stringMap) {
+ super();
+ this.women = women;
+ this.men = men;
+ this.rawMap = rawMap;
+ this.stringMap = stringMap;
+ }
+ public static @java.lang.SuppressWarnings("all") <K, V>BuilderSingularMapsBuilder<K, V> builder() {
+ return new BuilderSingularMapsBuilder<K, V>();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/BuilderSingularNoAutosingularize.java b/test/transform/resource/after-ecj/BuilderSingularNoAutosingularize.java
new file mode 100644
index 00000000..53bed757
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderSingularNoAutosingularize.java
@@ -0,0 +1,105 @@
+import java.util.List;
+import lombok.Singular;
+@lombok.Builder class BuilderSingularNoAutosingularize {
+ public static @java.lang.SuppressWarnings("all") class BuilderSingularNoAutosingularizeBuilder {
+ private java.util.ArrayList<String> things;
+ private java.util.ArrayList<String> widgets;
+ private java.util.ArrayList<String> items;
+ @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularizeBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularizeBuilder things(String things) {
+ if ((this.things == null))
+ this.things = new java.util.ArrayList<String>();
+ this.things.add(things);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularizeBuilder things(java.util.Collection<? extends String> things) {
+ if ((this.things == null))
+ this.things = new java.util.ArrayList<String>();
+ this.things.addAll(things);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularizeBuilder widget(String widget) {
+ if ((this.widgets == null))
+ this.widgets = new java.util.ArrayList<String>();
+ this.widgets.add(widget);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularizeBuilder widgets(java.util.Collection<? extends String> widgets) {
+ if ((this.widgets == null))
+ this.widgets = new java.util.ArrayList<String>();
+ this.widgets.addAll(widgets);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularizeBuilder items(String items) {
+ if ((this.items == null))
+ this.items = new java.util.ArrayList<String>();
+ this.items.add(items);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularizeBuilder items(java.util.Collection<? extends String> items) {
+ if ((this.items == null))
+ this.items = new java.util.ArrayList<String>();
+ this.items.addAll(items);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularize build() {
+ java.util.List<String> things;
+ switch (((this.things == null) ? 0 : this.things.size())) {
+ case 0 :
+ things = java.util.Collections.emptyList();
+ break;
+ case 1 :
+ things = java.util.Collections.singletonList(this.things.get(0));
+ break;
+ default :
+ things = new java.util.ArrayList<String>(this.things.size());
+ things.addAll(this.things);
+ things = java.util.Collections.unmodifiableList(things);
+ }
+ java.util.List<String> widgets;
+ switch (((this.widgets == null) ? 0 : this.widgets.size())) {
+ case 0 :
+ widgets = java.util.Collections.emptyList();
+ break;
+ case 1 :
+ widgets = java.util.Collections.singletonList(this.widgets.get(0));
+ break;
+ default :
+ widgets = new java.util.ArrayList<String>(this.widgets.size());
+ widgets.addAll(this.widgets);
+ widgets = java.util.Collections.unmodifiableList(widgets);
+ }
+ java.util.List<String> items;
+ switch (((this.items == null) ? 0 : this.items.size())) {
+ case 0 :
+ items = java.util.Collections.emptyList();
+ break;
+ case 1 :
+ items = java.util.Collections.singletonList(this.items.get(0));
+ break;
+ default :
+ items = new java.util.ArrayList<String>(this.items.size());
+ items.addAll(this.items);
+ items = java.util.Collections.unmodifiableList(items);
+ }
+ return new BuilderSingularNoAutosingularize(things, widgets, items);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((("BuilderSingularNoAutosingularize.BuilderSingularNoAutosingularizeBuilder(things=" + this.things) + ", widgets=") + this.widgets) + ", items=") + this.items) + ")");
+ }
+ }
+ private @Singular List<String> things;
+ private @Singular("widget") List<String> widgets;
+ private @Singular List<String> items;
+ @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularize(final List<String> things, final List<String> widgets, final List<String> items) {
+ super();
+ this.things = things;
+ this.widgets = widgets;
+ this.items = items;
+ }
+ public static @java.lang.SuppressWarnings("all") BuilderSingularNoAutosingularizeBuilder builder() {
+ return new BuilderSingularNoAutosingularizeBuilder();
+ }
+}
diff --git a/test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java b/test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java
new file mode 100644
index 00000000..eeff550b
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderSingularRedirectToGuava.java
@@ -0,0 +1,71 @@
+import java.util.Set;
+import java.util.NavigableMap;
+import java.util.Collection;
+import lombok.Singular;
+@lombok.Builder class BuilderSingularRedirectToGuava {
+ public static @java.lang.SuppressWarnings("all") class BuilderSingularRedirectToGuavaBuilder {
+ private com.google.common.collect.ImmutableSet.Builder<String> dangerMice;
+ private com.google.common.collect.ImmutableSortedMap.Builder<Integer, Number> things;
+ private com.google.common.collect.ImmutableList.Builder<Class<?>> doohickeys;
+ @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuavaBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuavaBuilder dangerMouse(String dangerMouse) {
+ if ((this.dangerMice == null))
+ this.dangerMice = com.google.common.collect.ImmutableSet.builder();
+ this.dangerMice.add(dangerMouse);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuavaBuilder dangerMice(java.lang.Iterable<? extends String> dangerMice) {
+ if ((this.dangerMice == null))
+ this.dangerMice = com.google.common.collect.ImmutableSet.builder();
+ this.dangerMice.addAll(dangerMice);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuavaBuilder thing(Integer thing$key, Number thing$value) {
+ if ((this.things == null))
+ this.things = com.google.common.collect.ImmutableSortedMap.naturalOrder();
+ this.things.put(thing$key, thing$value);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuavaBuilder things(java.util.Map<? extends Integer, ? extends Number> things) {
+ if ((this.things == null))
+ this.things = com.google.common.collect.ImmutableSortedMap.naturalOrder();
+ this.things.putAll(things);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuavaBuilder doohickey(Class<?> doohickey) {
+ if ((this.doohickeys == null))
+ this.doohickeys = com.google.common.collect.ImmutableList.builder();
+ this.doohickeys.add(doohickey);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuavaBuilder doohickeys(java.lang.Iterable<? extends Class<?>> doohickeys) {
+ if ((this.doohickeys == null))
+ this.doohickeys = com.google.common.collect.ImmutableList.builder();
+ this.doohickeys.addAll(doohickeys);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuava build() {
+ java.util.Set<String> dangerMice = ((this.dangerMice == null) ? com.google.common.collect.ImmutableSet.of() : this.dangerMice.build());
+ java.util.NavigableMap<Integer, Number> things = ((this.things == null) ? com.google.common.collect.ImmutableSortedMap.of() : this.things.build());
+ java.util.Collection<Class<?>> doohickeys = ((this.doohickeys == null) ? com.google.common.collect.ImmutableList.of() : this.doohickeys.build());
+ return new BuilderSingularRedirectToGuava(dangerMice, things, doohickeys);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((("BuilderSingularRedirectToGuava.BuilderSingularRedirectToGuavaBuilder(dangerMice=" + this.dangerMice) + ", things=") + this.things) + ", doohickeys=") + this.doohickeys) + ")");
+ }
+ }
+ private @Singular Set<String> dangerMice;
+ private @Singular NavigableMap<Integer, Number> things;
+ private @Singular Collection<Class<?>> doohickeys;
+ @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuava(final Set<String> dangerMice, final NavigableMap<Integer, Number> things, final Collection<Class<?>> doohickeys) {
+ super();
+ this.dangerMice = dangerMice;
+ this.things = things;
+ this.doohickeys = doohickeys;
+ }
+ public static @java.lang.SuppressWarnings("all") BuilderSingularRedirectToGuavaBuilder builder() {
+ return new BuilderSingularRedirectToGuavaBuilder();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/BuilderSingularSets.java b/test/transform/resource/after-ecj/BuilderSingularSets.java
new file mode 100644
index 00000000..5d2fcc59
--- /dev/null
+++ b/test/transform/resource/after-ecj/BuilderSingularSets.java
@@ -0,0 +1,125 @@
+import java.util.Set;
+import java.util.SortedSet;
+import lombok.Singular;
+@lombok.Builder class BuilderSingularSets<T> {
+ public static @java.lang.SuppressWarnings("all") class BuilderSingularSetsBuilder<T> {
+ private java.util.ArrayList<T> dangerMice;
+ private java.util.ArrayList<Number> octopodes;
+ private java.util.ArrayList<java.lang.Object> rawSet;
+ private java.util.ArrayList<String> stringSet;
+ @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder() {
+ super();
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder<T> dangerMouse(T dangerMouse) {
+ if ((this.dangerMice == null))
+ this.dangerMice = new java.util.ArrayList<T>();
+ this.dangerMice.add(dangerMouse);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder<T> dangerMice(java.util.Collection<? extends T> dangerMice) {
+ if ((this.dangerMice == null))
+ this.dangerMice = new java.util.ArrayList<T>();
+ this.dangerMice.addAll(dangerMice);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder<T> octopus(Number octopus) {
+ if ((this.octopodes == null))
+ this.octopodes = new java.util.ArrayList<Number>();
+ this.octopodes.add(octopus);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder<T> octopodes(java.util.Collection<? extends Number> octopodes) {
+ if ((this.octopodes == null))
+ this.octopodes = new java.util.ArrayList<Number>();
+ this.octopodes.addAll(octopodes);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder<T> rawSet(java.lang.Object rawSet) {
+ if ((this.rawSet == null))
+ this.rawSet = new java.util.ArrayList<java.lang.Object>();
+ this.rawSet.add(rawSet);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder<T> rawSet(java.util.Collection<?> rawSet) {
+ if ((this.rawSet == null))
+ this.rawSet = new java.util.ArrayList<java.lang.Object>();
+ this.rawSet.addAll(rawSet);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder<T> stringSet(String stringSet) {
+ if ((this.stringSet == null))
+ this.stringSet = new java.util.ArrayList<String>();
+ this.stringSet.add(stringSet);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSetsBuilder<T> stringSet(java.util.Collection<? extends String> stringSet) {
+ if ((this.stringSet == null))
+ this.stringSet = new java.util.ArrayList<String>();
+ this.stringSet.addAll(stringSet);
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") BuilderSingularSets<T> build() {
+ java.util.Set<T> dangerMice;
+ switch (((this.dangerMice == null) ? 0 : this.dangerMice.size())) {
+ case 0 :
+ dangerMice = java.util.Collections.emptySet();
+ break;
+ case 1 :
+ dangerMice = java.util.Collections.singleton(this.dangerMice.get(0));
+ break;
+ default :
+ dangerMice = new java.util.LinkedHashSet<T>(((this.dangerMice.size() < 0x40000000) ? ((1 + this.dangerMice.size()) + ((this.dangerMice.size() - 3) / 3)) : java.lang.Integer.MAX_VALUE));
+ dangerMice.addAll(this.dangerMice);
+ dangerMice = java.util.Collections.unmodifiableSet(dangerMice);
+ }
+ java.util.SortedSet<Number> octopodes = new java.util.TreeSet<Number>();
+ if ((this.octopodes != null))
+ octopodes.addAll(this.octopodes);
+ octopodes = java.util.Collections.unmodifiableSortedSet(octopodes);
+ java.util.Set<java.lang.Object> rawSet;
+ switch (((this.rawSet == null) ? 0 : this.rawSet.size())) {
+ case 0 :
+ rawSet = java.util.Collections.emptySet();
+ break;
+ case 1 :
+ rawSet = java.util.Collections.singleton(this.rawSet.get(0));
+ break;
+ default :
+ rawSet = new java.util.LinkedHashSet<java.lang.Object>(((this.rawSet.size() < 0x40000000) ? ((1 + this.rawSet.size()) + ((this.rawSet.size() - 3) / 3)) : java.lang.Integer.MAX_VALUE));
+ rawSet.addAll(this.rawSet);
+ rawSet = java.util.Collections.unmodifiableSet(rawSet);
+ }
+ java.util.Set<String> stringSet;
+ switch (((this.stringSet == null) ? 0 : this.stringSet.size())) {
+ case 0 :
+ stringSet = java.util.Collections.emptySet();
+ break;
+ case 1 :
+ stringSet = java.util.Collections.singleton(this.stringSet.get(0));
+ break;
+ default :
+ stringSet = new java.util.LinkedHashSet<String>(((this.stringSet.size() < 0x40000000) ? ((1 + this.stringSet.size()) + ((this.stringSet.size() - 3) / 3)) : java.lang.Integer.MAX_VALUE));
+ stringSet.addAll(this.stringSet);
+ stringSet = java.util.Collections.unmodifiableSet(stringSet);
+ }
+ return new BuilderSingularSets<T>(dangerMice, octopodes, rawSet, stringSet);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((((("BuilderSingularSets.BuilderSingularSetsBuilder(dangerMice=" + this.dangerMice) + ", octopodes=") + this.octopodes) + ", rawSet=") + this.rawSet) + ", stringSet=") + this.stringSet) + ")");
+ }
+ }
+ private @Singular Set<T> dangerMice;
+ private @Singular SortedSet<? extends Number> octopodes;
+ private @SuppressWarnings("all") @Singular("rawSet") Set rawSet;
+ private @Singular("stringSet") Set<String> stringSet;
+ @java.lang.SuppressWarnings("all") BuilderSingularSets(final Set<T> dangerMice, final SortedSet<? extends Number> octopodes, final Set rawSet, final Set<String> stringSet) {
+ super();
+ this.dangerMice = dangerMice;
+ this.octopodes = octopodes;
+ this.rawSet = rawSet;
+ this.stringSet = stringSet;
+ }
+ public static @java.lang.SuppressWarnings("all") <T>BuilderSingularSetsBuilder<T> builder() {
+ return new BuilderSingularSetsBuilder<T>();
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/BuilderWithExistingBuilderClass.java b/test/transform/resource/after-ecj/BuilderWithExistingBuilderClass.java
index 38cb0038..8fb42a59 100644
--- a/test/transform/resource/after-ecj/BuilderWithExistingBuilderClass.java
+++ b/test/transform/resource/after-ecj/BuilderWithExistingBuilderClass.java
@@ -1,4 +1,4 @@
-import lombok.experimental.Builder;
+import lombok.Builder;
class BuilderWithExistingBuilderClass<T, K extends Number> {
public static class BuilderWithExistingBuilderClassBuilder<Z extends Number> {
private boolean arg2;
diff --git a/test/transform/resource/before/BuilderSingularGuavaListsSets.java b/test/transform/resource/before/BuilderSingularGuavaListsSets.java
new file mode 100644
index 00000000..995c00e8
--- /dev/null
+++ b/test/transform/resource/before/BuilderSingularGuavaListsSets.java
@@ -0,0 +1,14 @@
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+
+import lombok.Singular;
+
+@lombok.Builder
+class BuilderSingularGuavaListsSets<T> {
+ @Singular private ImmutableList<T> cards;
+ @Singular private ImmutableCollection<? extends Number> frogs;
+ @SuppressWarnings("all") @Singular("rawSet") private ImmutableSet rawSet;
+ @Singular private ImmutableSortedSet<String> passes;
+}
diff --git a/test/transform/resource/before/BuilderSingularGuavaMaps.java b/test/transform/resource/before/BuilderSingularGuavaMaps.java
new file mode 100644
index 00000000..64a53570
--- /dev/null
+++ b/test/transform/resource/before/BuilderSingularGuavaMaps.java
@@ -0,0 +1,12 @@
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableSortedMap;
+
+import lombok.Singular;
+
+@lombok.Builder
+class BuilderSingularGuavaMaps<K, V> {
+ @Singular private ImmutableMap<K, V> battleaxes;
+ @Singular private ImmutableSortedMap<Integer, ? extends V> vertices;
+ @SuppressWarnings("all") @Singular("rawMap") private ImmutableBiMap rawMap;
+}
diff --git a/test/transform/resource/before/BuilderSingularLists.java b/test/transform/resource/before/BuilderSingularLists.java
new file mode 100644
index 00000000..93fced8e
--- /dev/null
+++ b/test/transform/resource/before/BuilderSingularLists.java
@@ -0,0 +1,11 @@
+import java.util.List;
+import java.util.Collection;
+
+import lombok.Singular;
+
+@lombok.Builder
+class BuilderSingularLists<T> {
+ @Singular private List<T> children;
+ @Singular private Collection<? extends Number> scarves;
+ @SuppressWarnings("all") @Singular("rawList") private List rawList;
+}
diff --git a/test/transform/resource/before/BuilderSingularMaps.java b/test/transform/resource/before/BuilderSingularMaps.java
new file mode 100644
index 00000000..e17f74fc
--- /dev/null
+++ b/test/transform/resource/before/BuilderSingularMaps.java
@@ -0,0 +1,12 @@
+import java.util.Map;
+import java.util.SortedMap;
+
+import lombok.Singular;
+
+@lombok.Builder
+class BuilderSingularMaps<K, V> {
+ @Singular private Map<K, V> women;
+ @Singular private SortedMap<K, ? extends Number> men;
+ @SuppressWarnings("all") @Singular("rawMap") private Map rawMap;
+ @Singular("stringMap") private Map<String, V> stringMap;
+}
diff --git a/test/transform/resource/before/BuilderSingularNoAutoSingularize.java b/test/transform/resource/before/BuilderSingularNoAutoSingularize.java
new file mode 100644
index 00000000..31e2c3ca
--- /dev/null
+++ b/test/transform/resource/before/BuilderSingularNoAutoSingularize.java
@@ -0,0 +1,11 @@
+//CONF: lombok.singular.auto = false
+import java.util.List;
+
+import lombok.Singular;
+
+@lombok.Builder
+class BuilderSingularNoAutosingularize {
+ @Singular private List<String> things;
+ @Singular("widget") private List<String> widgets;
+ @Singular private List<String> items;
+}
diff --git a/test/transform/resource/before/BuilderSingularRedirectToGuava.java b/test/transform/resource/before/BuilderSingularRedirectToGuava.java
new file mode 100644
index 00000000..7e1e2c23
--- /dev/null
+++ b/test/transform/resource/before/BuilderSingularRedirectToGuava.java
@@ -0,0 +1,13 @@
+//CONF: lombok.builder.useGuava = true
+import java.util.Set;
+import java.util.NavigableMap;
+import java.util.Collection;
+
+import lombok.Singular;
+
+@lombok.Builder
+class BuilderSingularRedirectToGuava {
+ @Singular private Set<String> dangerMice;
+ @Singular private NavigableMap<Integer, Number> things;
+ @Singular private Collection<Class<?>> doohickeys;
+}
diff --git a/test/transform/resource/before/BuilderSingularSets.java b/test/transform/resource/before/BuilderSingularSets.java
new file mode 100644
index 00000000..68c4510a
--- /dev/null
+++ b/test/transform/resource/before/BuilderSingularSets.java
@@ -0,0 +1,12 @@
+import java.util.Set;
+import java.util.SortedSet;
+
+import lombok.Singular;
+
+@lombok.Builder
+class BuilderSingularSets<T> {
+ @Singular private Set<T> dangerMice;
+ @Singular private SortedSet<? extends Number> octopodes;
+ @SuppressWarnings("all") @Singular("rawSet") private Set rawSet;
+ @Singular("stringSet") private Set<String> stringSet;
+}
diff --git a/test/transform/resource/before/BuilderWithExistingBuilderClass.java b/test/transform/resource/before/BuilderWithExistingBuilderClass.java
index 262e3b85..c8e0a24b 100644
--- a/test/transform/resource/before/BuilderWithExistingBuilderClass.java
+++ b/test/transform/resource/before/BuilderWithExistingBuilderClass.java
@@ -1,4 +1,4 @@
-import lombok.experimental.Builder;
+import lombok.Builder;
class BuilderWithExistingBuilderClass<T, K extends Number> {
@Builder
diff --git a/test/transform/resource/messages-delombok/BuilderSingularNoAutosingularize.java.messages b/test/transform/resource/messages-delombok/BuilderSingularNoAutosingularize.java.messages
new file mode 100644
index 00000000..8719789b
--- /dev/null
+++ b/test/transform/resource/messages-delombok/BuilderSingularNoAutosingularize.java.messages
@@ -0,0 +1,2 @@
+8 The singular must be specified explicitly (e.g. @Singular("task")) because auto singularization is disabled.
+10 The singular must be specified explicitly (e.g. @Singular("task")) because auto singularization is disabled.
diff --git a/test/transform/resource/messages-ecj/BuilderComplex.java.messages b/test/transform/resource/messages-ecj/BuilderComplex.java.messages
new file mode 100644
index 00000000..4856a80d
--- /dev/null
+++ b/test/transform/resource/messages-ecj/BuilderComplex.java.messages
@@ -0,0 +1 @@
+2 The type Builder is deprecated
diff --git a/test/transform/resource/messages-ecj/BuilderSingularNoAutosingularize.java.messages b/test/transform/resource/messages-ecj/BuilderSingularNoAutosingularize.java.messages
new file mode 100644
index 00000000..8719789b
--- /dev/null
+++ b/test/transform/resource/messages-ecj/BuilderSingularNoAutosingularize.java.messages
@@ -0,0 +1,2 @@
+8 The singular must be specified explicitly (e.g. @Singular("task")) because auto singularization is disabled.
+10 The singular must be specified explicitly (e.g. @Singular("task")) because auto singularization is disabled.