aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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/bytecode/PostCompilerApp.java22
-rw-r--r--src/core/lombok/core/AgentLauncher.java (renamed from src/core/lombok/core/Agent.java)74
-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/Main.java3
-rw-r--r--src/core/lombok/core/PostCompiler.java8
-rw-r--r--src/core/lombok/core/TypeLibrary.java9
-rw-r--r--src/core/lombok/core/Version.java4
-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.java127
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java345
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java364
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java12
-rw-r--r--src/core/lombok/eclipse/handlers/HandleGetter.java4
-rw-r--r--src/core/lombok/eclipse/handlers/HandlePrintAST.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSetter.java9
-rw-r--r--src/core/lombok/eclipse/handlers/HandleWither.java8
-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.java247
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java158
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java129
-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.java295
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java2
-rw-r--r--src/core/lombok/javac/handlers/HandlePrintAST.java2
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java96
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java300
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java45
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java45
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java194
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java138
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java120
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java198
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java57
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java193
-rw-r--r--src/delombok/lombok/delombok/Delombok.java6
-rw-r--r--src/delombok/lombok/delombok/DelombokApp.java16
-rw-r--r--src/delombok/lombok/delombok/PrettyCommentsPrinter.java43
-rw-r--r--src/delombok/lombok/delombok/UnicodeEscapeWriter.java2
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java106
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java137
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java7
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java384
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java53
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java9
-rw-r--r--src/eclipseAgent/lombok/launch/PatchFixesHider.java600
-rw-r--r--src/installer/lombok/installer/InstallerGUI.java5
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseLocation.java5
-rw-r--r--src/launch/lombok/launch/Agent.java47
-rw-r--r--src/launch/lombok/launch/AnnotationProcessor.java77
-rw-r--r--src/launch/lombok/launch/Main.java40
-rw-r--r--src/launch/lombok/launch/ShadowClassLoader.java401
-rw-r--r--src/utils/lombok/core/LombokImmutableList.java6
-rw-r--r--src/utils/lombok/javac/Javac.java8
66 files changed, 5770 insertions, 1239 deletions
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..8d8fb03b 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.") {};
+
+ // ----- Singular -----
+
+ /**
+ * lombok configuration: {@code lombok.singular.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> SINGULAR_USE_GUAVA = new ConfigurationKey<Boolean>("lombok.singular.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.") {};
+
+ /**
+ * 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..15dec4a5
--- /dev/null
+++ b/src/core/lombok/Singular.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+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/bytecode/PostCompilerApp.java b/src/core/lombok/bytecode/PostCompilerApp.java
index d2c3157c..5f49bd81 100644
--- a/src/core/lombok/bytecode/PostCompilerApp.java
+++ b/src/core/lombok/bytecode/PostCompilerApp.java
@@ -98,7 +98,7 @@ public class PostCompilerApp extends LombokApp {
byte[] original = readFile(file);
byte[] clone = original.clone();
byte[] transformed = PostCompiler.applyTransformations(clone, file.toString(), DiagnosticsReceiver.CONSOLE);
- if (clone != transformed && !Arrays.equals(clone, transformed)) {
+ if (clone != transformed && !Arrays.equals(original, transformed)) {
filesTouched++;
if (args.verbose) System.out.println("Rewriting " + file.getAbsolutePath());
writeFile(file, transformed);
@@ -138,18 +138,24 @@ public class PostCompilerApp extends LombokApp {
byte[] buffer = new byte[1024];
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
FileInputStream fileInputStream = new FileInputStream(file);
- while (true) {
- int read = fileInputStream.read(buffer);
- if (read == -1) break;
- bytes.write(buffer, 0, read);
+ try {
+ while (true) {
+ int read = fileInputStream.read(buffer);
+ if (read == -1) break;
+ bytes.write(buffer, 0, read);
+ }
+ } finally {
+ fileInputStream.close();
}
- fileInputStream.close();
return bytes.toByteArray();
}
static void writeFile(File file, byte[] transformed) throws IOException {
FileOutputStream out = new FileOutputStream(file);
- out.write(transformed);
- out.close();
+ try {
+ out.write(transformed);
+ } finally {
+ out.close();
+ }
}
}
diff --git a/src/core/lombok/core/Agent.java b/src/core/lombok/core/AgentLauncher.java
index 49c03bf3..1d5ab3e6 100644
--- a/src/core/lombok/core/Agent.java
+++ b/src/core/lombok/core/AgentLauncher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-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
@@ -21,45 +21,32 @@
*/
package lombok.core;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
-import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
-
-public abstract class Agent {
- protected abstract void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected) throws Exception;
-
- public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Throwable {
- runAgents(agentArgs, instrumentation, true);
+public class AgentLauncher {
+ public interface AgentLaunchable {
+ void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Exception;
}
- public static void premain(String agentArgs, Instrumentation instrumentation) throws Throwable {
- runAgents(agentArgs, instrumentation, false);
- }
-
- private static final List<AgentInfo> AGENTS = Collections.unmodifiableList(Arrays.asList(
- new NetbeansPatcherInfo(),
- new EclipsePatcherInfo()
- ));
-
- private static void runAgents(String agentArgs, Instrumentation instrumentation, boolean injected) throws Throwable {
+ public static void runAgents(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Throwable {
for (AgentInfo info : AGENTS) {
try {
Class<?> agentClass = Class.forName(info.className());
- Agent agent = (Agent) agentClass.newInstance();
- agent.runAgent(agentArgs, instrumentation, injected);
+ AgentLaunchable agent = (AgentLaunchable) agentClass.newInstance();
+ agent.runAgent(agentArgs, instrumentation, injected, launchingContext);
} catch (Throwable t) {
info.problem(t, instrumentation);
}
}
}
+ private static final List<AgentInfo> AGENTS = Collections.unmodifiableList(Arrays.<AgentInfo>asList(
+ new EclipsePatcherInfo()
+ ));
+
private static abstract class AgentInfo {
abstract String className();
@@ -91,45 +78,6 @@ public abstract class Agent {
}
}
- private static class NetbeansPatcherInfo extends AgentInfo {
- @Override String className() {
- return "lombok.netbeans.agent.NetbeansPatcher";
- }
-
- @Override void problem(Throwable in, Instrumentation instrumentation) throws Throwable {
- try {
- super.problem(in, instrumentation);
- } catch (InternalError ie) {
- throw ie;
- } catch (Throwable t) {
- final String error;
-
- if (t instanceof UnsupportedClassVersionError) {
- error = "Lombok only works on netbeans if you start netbeans using a 1.6 or higher JVM.\n" +
- "Change your platform's default JVM, or edit etc/netbeans.conf\n" +
- "and explicitly tell netbeans your 1.6 JVM's location.";
- } else {
- error = "Lombok disabled due to error: " + t;
- }
-
- instrumentation.addTransformer(new ClassFileTransformer() {
- @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
- if ("org/netbeans/modules/java/source/parsing/JavacParser".equals(className)) {
- //If that class gets loaded, this is definitely a netbeans(-esque) environment, and thus we SHOULD tell the user that lombok is not in fact loaded.
- SwingUtilities.invokeLater(new Runnable() {
- @Override public void run() {
- JOptionPane.showMessageDialog(null, error, "Lombok Disabled", JOptionPane.ERROR_MESSAGE);
- }
- });
- }
-
- return null;
- }
- });
- }
- }
- }
-
private static class EclipsePatcherInfo extends AgentInfo {
@Override String className() {
return "lombok.eclipse.agent.EclipsePatcher";
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/Main.java b/src/core/lombok/core/Main.java
index d62fe3e4..dc613b0d 100644
--- a/src/core/lombok/core/Main.java
+++ b/src/core/lombok/core/Main.java
@@ -38,6 +38,7 @@ public class Main {
));
public static void main(String[] args) throws IOException {
+ Thread.currentThread().setContextClassLoader(Main.class.getClassLoader());
int err = new Main(SpiLoadUtil.readAllFromIterator(
SpiLoadUtil.findServices(LombokApp.class)), Arrays.asList(args)).go();
System.exit(err);
@@ -142,7 +143,7 @@ public class Main {
out.println("------------------------------");
}
out.println("projectlombok.org " + Version.getFullVersion());
- out.println("Copyright (C) 2009-2012 The Project Lombok Authors.");
+ out.println("Copyright (C) 2009-2015 The Project Lombok Authors.");
out.println("Run 'lombok license' to see the lombok license agreement.");
out.println();
out.println("Run lombok without any parameters to start the graphical installer.");
diff --git a/src/core/lombok/core/PostCompiler.java b/src/core/lombok/core/PostCompiler.java
index 11091cb8..237867cb 100644
--- a/src/core/lombok/core/PostCompiler.java
+++ b/src/core/lombok/core/PostCompiler.java
@@ -24,6 +24,8 @@ package lombok.core;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Collections;
import java.util.List;
@@ -55,10 +57,12 @@ public final class PostCompiler {
transformations = SpiLoadUtil.readAllFromIterator(SpiLoadUtil.findServices(PostCompilerTransformation.class, PostCompilerTransformation.class.getClassLoader()));
} catch (IOException e) {
transformations = Collections.emptyList();
- diagnostics.addWarning("Could not load post-compile transformers: " + e.getMessage());
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw, true));
+ diagnostics.addWarning("Could not load post-compile transformers: " + e.getMessage() + "\n" + sw.toString());
}
}
-
+
public static OutputStream wrapOutputStream(final OutputStream originalStream, final String fileName, final DiagnosticsReceiver diagnostics) throws IOException {
if (System.getProperty("lombok.disablePostCompiler", null) != null) return originalStream;
return new ByteArrayOutputStream() {
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/Version.java b/src/core/lombok/core/Version.java
index 9f810525..b0f12916 100644
--- a/src/core/lombok/core/Version.java
+++ b/src/core/lombok/core/Version.java
@@ -30,9 +30,9 @@ public class Version {
// ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries).
// Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example.
// Official builds always end in an even number. (Since 0.10.2).
- private static final String VERSION = "1.14.9";
+ private static final String VERSION = "1.16.1";
private static final String RELEASE_NAME = "Edgy Guinea Pig";
-// private static final String RELEASE_NAME = "Branching Cobra";
+// private static final String RELEASE_NAME = "Candid Duck";
private Version() {
//Prevent instantiation
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..87e35269 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;
}
@@ -401,19 +411,17 @@ public class EclipseHandlerUtil {
}
public static Annotation[] copyAnnotations(ASTNode source, Annotation[]... allAnnotations) {
- boolean allNull = true;
-
- List<Annotation> result = new ArrayList<Annotation>();
+ List<Annotation> result = null;
for (Annotation[] annotations : allAnnotations) {
if (annotations != null) {
- allNull = false;
for (Annotation annotation : annotations) {
+ if (result == null) result = new ArrayList<Annotation>();
result.add(copyAnnotation(annotation, source));
}
}
}
- if (allNull) return null;
- return result.toArray(new Annotation[0]);
+
+ return result == null ? null : result.toArray(new Annotation[0]);
}
public static boolean hasAnnotation(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) {
@@ -444,8 +452,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 +469,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 +477,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 +680,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 +725,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 +738,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 +884,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 +900,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 +1511,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 +1526,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..4cb41d4f
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
@@ -0,0 +1,345 @@
+/*
+ * 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) {
+ TypeReference[] clonedAndFixedArgs = createTypeArgs(count, addExtends, node, typeArgs);
+ if (type instanceof SingleTypeReference) {
+ type = new ParameterizedSingleTypeReference(((SingleTypeReference) type).token, clonedAndFixedArgs, 0, 0L);
+ } else if (type instanceof QualifiedTypeReference) {
+ QualifiedTypeReference qtr = (QualifiedTypeReference) type;
+ TypeReference[][] trs = new TypeReference[qtr.tokens.length][];
+ trs[qtr.tokens.length - 1] = clonedAndFixedArgs;
+ 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;
+ }
+
+ protected TypeReference[] createTypeArgs(int count, boolean addExtends, EclipseNode node, List<TypeReference> typeArgs) {
+ if (count < 0) throw new IllegalArgumentException("count is negative");
+ if (count == 0) return null;
+ 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 (arguments.isEmpty()) return null;
+ return arguments.toArray(new TypeReference[arguments.size()]);
+ }
+
+ 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..45f4342e 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,87 @@ 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> {
- @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@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) {
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 +129,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 +152,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 +177,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 +189,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 +228,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 +244,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;
+ }
+ }
+ }
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ if (bfd.singularData.getSingularizer().requiresCleaning()) {
+ addCleaning = true;
+ break;
+ }
+ }
}
- 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);
+
+ 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 +283,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 +470,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 +500,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..5bcc803a 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;
@@ -333,8 +333,7 @@ public class HandleConstructor {
Statement nullCheck = generateNullCheck(field, sourceNode);
if (nullCheck != null) nullChecks.add(nullCheck);
}
- Annotation[] copiedAnnotations = copyAnnotations(source, nonNulls, nullables);
- if (copiedAnnotations.length != 0) parameter.annotations = copiedAnnotations;
+ parameter.annotations = copyAnnotations(source, nonNulls, nullables);
params.add(parameter);
}
@@ -348,10 +347,9 @@ public class HandleConstructor {
constructorProperties = createConstructorProperties(source, fields);
}
- Annotation[] copiedAnnotations = copyAnnotations(source,
+ constructor.annotations = copyAnnotations(source,
onConstructor.toArray(new Annotation[0]),
constructorProperties);
- if (copiedAnnotations.length != 0) constructor.annotations = copiedAnnotations;
}
constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
@@ -396,9 +394,7 @@ public class HandleConstructor {
assigns.add(nameRef);
Argument parameter = new Argument(field.name, fieldPos, copyType(field.type, source), Modifier.FINAL);
-
- Annotation[] copiedAnnotations = copyAnnotations(source, findAnnotations(field, NON_NULL_PATTERN), findAnnotations(field, NULLABLE_PATTERN));
- if (copiedAnnotations.length != 0) parameter.annotations = copiedAnnotations;
+ parameter.annotations = copyAnnotations(source, findAnnotations(field, NON_NULL_PATTERN), findAnnotations(field, NULLABLE_PATTERN));
params.add(parameter);
}
diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java
index 031fff82..14f2fb72 100644
--- a/src/core/lombok/eclipse/handlers/HandleGetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleGetter.java
@@ -270,14 +270,12 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
- Annotation[] copiedAnnotations = copyAnnotations(source,
+ method.annotations = copyAnnotations(source,
onMethod.toArray(new Annotation[0]),
findAnnotations(field, NON_NULL_PATTERN),
findAnnotations(field, NULLABLE_PATTERN),
findDelegatesAndMarkAsHandled(fieldNode),
deprecated);
-
- if (copiedAnnotations.length != 0) method.annotations = copiedAnnotations;
}
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
diff --git a/src/core/lombok/eclipse/handlers/HandlePrintAST.java b/src/core/lombok/eclipse/handlers/HandlePrintAST.java
index 0b61bc4d..234e29b8 100644
--- a/src/core/lombok/eclipse/handlers/HandlePrintAST.java
+++ b/src/core/lombok/eclipse/handlers/HandlePrintAST.java
@@ -59,7 +59,7 @@ public class HandlePrintAST extends EclipseAnnotationHandler<PrintAST> {
try {
stream.close();
} catch (Exception e) {
- Lombok.sneakyThrow(e);
+ throw Lombok.sneakyThrow(e);
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index c22af676..1fcf751d 100644
--- a/src/core/lombok/eclipse/handlers/HandleSetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleSetter.java
@@ -216,10 +216,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
if (isFieldDeprecated(fieldNode)) {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
- Annotation[] copiedAnnotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
- if (copiedAnnotations.length != 0) {
- method.annotations = copiedAnnotations;
- }
+ method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
Argument param = new Argument(field.name, p, copyType(field.type, source), Modifier.FINAL);
param.sourceStart = pS; param.sourceEnd = pE;
method.arguments = new Argument[] { param };
@@ -252,9 +249,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
statements.add(returnThis);
}
method.statements = statements.toArray(new Statement[0]);
-
- Annotation[] copiedAnnotationsParam = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
- if (copiedAnnotationsParam.length != 0) param.annotations = copiedAnnotationsParam;
+ param.annotations = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
return method;
diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java
index 8b038676..cb06d888 100644
--- a/src/core/lombok/eclipse/handlers/HandleWither.java
+++ b/src/core/lombok/eclipse/handlers/HandleWither.java
@@ -227,10 +227,7 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
if (isFieldDeprecated(fieldNode)) {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
- Annotation[] copiedAnnotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
- if (copiedAnnotations.length != 0) {
- method.annotations = copiedAnnotations;
- }
+ method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
Argument param = new Argument(field.name, p, copyType(field.type, source), Modifier.FINAL);
param.sourceStart = pS; param.sourceEnd = pE;
method.arguments = new Argument[] { param };
@@ -283,8 +280,7 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
method.statements = statements.toArray(new Statement[0]);
- Annotation[] copiedAnnotationsParam = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
- if (copiedAnnotationsParam.length != 0) param.annotations = copiedAnnotationsParam;
+ param.annotations = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
return method;
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..3b2ca875
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
@@ -0,0 +1,247 @@
+/*
+ * 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);
+ emptyInvoke.typeArguments = createTypeArgs(mapMode ? 2 : 1, false, builderType, data.getTypeArgs());
+ }
+
+ 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..0a9eaf75
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
@@ -0,0 +1,129 @@
+/*
+ * 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.Eclipse;
+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 by passing builder field to constructor. */ {
+ switchContents.add(new CaseStatement(null, 0, 0));
+
+ Expression argToUnmodifiable;
+ /* new j.u.ArrayList<Generics>(this.pluralName); */ {
+ FieldReference thisDotPluralName = new FieldReference(data.getPluralName(), 0L);
+ thisDotPluralName.receiver = new ThisReference(0, 0);
+ 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 = new Expression[] {thisDotPluralName};
+ argToUnmodifiable = constructorCall;
+ }
+
+ /* pluralname = Collections.unmodifiableList(-newlist-); */ {
+ MessageSend unmodInvoke = new MessageSend();
+ unmodInvoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ unmodInvoke.selector = "unmodifiableList".toCharArray();
+ unmodInvoke.arguments = new Expression[] {argToUnmodifiable};
+ 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(Eclipse.fromQualifiedName(data.getTargetFqn()), 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..6661f4af
--- /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.SINGULAR_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..4f7f79d9 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,8 +21,8 @@
*/
package lombok.javac.handlers;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
-import java.util.Collections;
import org.mangosdk.spi.ProviderFor;
@@ -34,6 +34,8 @@ import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCIf;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
@@ -46,16 +48,21 @@ import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import lombok.AccessLevel;
+import lombok.Builder;
import lombok.ConfigurationKeys;
+import lombok.Singular;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
-import lombok.experimental.Builder;
+import lombok.core.handlers.HandlerUtil;
import lombok.experimental.NonFinal;
+import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import static lombok.javac.Javac.*;
@@ -64,10 +71,27 @@ import static lombok.javac.JavacTreeMaker.TypeTag.*;
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends JavacAnnotationHandler<Builder> {
- @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
+ private static final boolean toBoolean(Object expr, boolean defaultValue) {
+ if (expr == null) return defaultValue;
+ if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0;
+ return ((Boolean) expr).booleanValue();
+ }
+
+ private static class BuilderFieldData {
+ JCExpression type;
+ Name name;
+ SingularData singularData;
+ java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
+ }
+
+ @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
Builder builderInstance = annotation.getInstance();
+
+ // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them.
+ boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true);
+ boolean chain = toBoolean(annotation.getActualExpression("chain"), true);
+
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
@@ -82,20 +106,21 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
}
- deleteAnnotationIfNeccessary(annotationNode, Builder.class);
- deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder");
+ @SuppressWarnings("deprecation")
+ Class<? extends Annotation> oldExperimentalBuilder = lombok.experimental.Builder.class;
+ deleteAnnotationIfNeccessary(annotationNode, Builder.class, oldExperimentalBuilder);
JavacNode parent = annotationNode.up();
- java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>();
- java.util.List<Name> namesOfParameters = new ArrayList<Name>();
+ java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
JCExpression returnType;
List<JCTypeParameter> typeParams = List.nil();
List<JCExpression> thrownExceptions = List.nil();
Name nameOfStaticBuilderMethod;
JavacNode tdParent;
- JCMethodDecl fillParametersFrom = parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null;
+ JavacNode fillParametersFrom = parent.get() instanceof JCMethodDecl ? parent : null;
+ boolean addCleaning = false;
if (parent.get() instanceof JCClassDecl) {
tdParent = parent;
@@ -109,12 +134,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
// non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
// Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
- namesOfParameters.add(removePrefixFromField(fieldNode));
- typesOfParameters.add(fd.vartype);
+ BuilderFieldData bfd = new BuilderFieldData();
+ bfd.name = removePrefixFromField(fieldNode);
+ bfd.type = fd.vartype;
+ bfd.singularData = getSingularData(fieldNode);
+ builderFields.add(bfd);
allFields.append(fieldNode);
}
-
new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode);
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
@@ -123,28 +150,31 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
- if (!fillParametersFrom.typarams.isEmpty()) {
+ JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
+ if (!jmd.typarams.isEmpty()) {
annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
return;
}
+
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
- thrownExceptions = fillParametersFrom.thrown;
+ thrownExceptions = jmd.thrown;
nameOfStaticBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null) {
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
- if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) {
+ JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
+ if ((jmd.mods.flags & Flags.STATIC) == 0) {
annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
return;
}
- returnType = fillParametersFrom.restype;
- typeParams = fillParametersFrom.typarams;
- thrownExceptions = fillParametersFrom.thrown;
- nameOfStaticBuilderMethod = fillParametersFrom.name;
+ returnType = jmd.restype;
+ typeParams = jmd.typarams;
+ thrownExceptions = jmd.thrown;
+ nameOfStaticBuilderMethod = jmd.name;
if (builderClassName.isEmpty()) {
if (returnType instanceof JCTypeApply) {
returnType = ((JCTypeApply) returnType).clazz;
@@ -179,9 +209,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (fillParametersFrom != null) {
- for (JCVariableDecl param : fillParametersFrom.params) {
- namesOfParameters.add(param.name);
- typesOfParameters.add(param.vartype);
+ for (JavacNode param : fillParametersFrom.down()) {
+ if (param.getKind() != Kind.ARGUMENT) continue;
+ BuilderFieldData bfd = new BuilderFieldData();
+ JCVariableDecl raw = (JCVariableDecl) param.get();
+ bfd.name = raw.name;
+ bfd.type = raw.vartype;
+ bfd.singularData = getSingularData(param);
+ builderFields.add(bfd);
}
}
@@ -190,12 +225,34 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
} else {
sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode);
+ /* generate errors for @Singular BFDs that have one already defined node. */ {
+ for (BuilderFieldData bfd : builderFields) {
+ SingularData sd = bfd.singularData;
+ if (sd == null) continue;
+ JavacSingularizer singularizer = sd.getSingularizer();
+ if (singularizer == null) continue;
+ if (singularizer.checkForAlreadyExistingNodesAndGenerateError(builderType, sd)) {
+ bfd.singularData = null;
+ }
+ }
+ }
+
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ if (bfd.singularData.getSingularizer().requiresCleaning()) {
+ addCleaning = true;
+ break;
+ }
+ }
}
- java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
- java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>();
- for (JavacNode fieldNode : fieldNodes) {
- JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, annotationNode, builderInstance.fluent(), builderInstance.chain());
- if (newMethod != null) newMethods.add(newMethod);
+
+ generateBuilderFields(builderType, builderFields, ast);
+ if (addCleaning) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null);
+ injectField(builderType, uncleanField);
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
@@ -203,38 +260,93 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (cd != null) injectMethod(builderType, cd);
}
- for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod);
+ for (BuilderFieldData bfd : builderFields) {
+ makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain);
+ }
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions);
+ JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
if (md != null) injectMethod(builderType, md);
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ java.util.List<JavacNode> fieldNodes = new ArrayList<JavacNode>();
+ for (BuilderFieldData bfd : builderFields) {
+ fieldNodes.addAll(bfd.createdFields);
+ }
JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
if (md != null) injectMethod(builderType, md);
}
+ if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
+
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
+ recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
if (md != null) injectMethod(tdParent, md);
}
+
+ recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
}
- public JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<Name> fieldNames, JavacNode type, List<JCExpression> thrownExceptions) {
+ private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
+ JavacTreeMaker maker = type.getTreeMaker();
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements);
+ }
+ }
+
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, false))));
+ JCBlock body = maker.Block(0, statements.toList());
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(maker, CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ /*
+ * if (shouldReturnThis) {
+ methodType = cloneSelfType(field);
+ }
+
+ if (methodType == null) {
+ //WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6.
+ methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID));
+ shouldReturnThis = false;
+ }
+
+ */
+ }
+
+ private JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) {
JavacTreeMaker maker = type.getTreeMaker();
JCExpression call;
- JCStatement statement;
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ if (addCleaning) {
+ JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")));
+ JCStatement invokeClean = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName("$lombokClean")), List.<JCExpression>nil()));
+ JCIf ifUnclean = maker.If(notClean, invokeClean, null);
+ statements.append(ifUnclean);
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name);
+ }
+ }
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
- for (Name n : fieldNames) {
- args.append(maker.Ident(n));
+ for (BuilderFieldData bfd : builderFields) {
+ args.append(maker.Ident(bfd.name));
+ }
+
+ if (addCleaning) {
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true))));
}
if (staticName == null) {
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
- statement = maker.Return(call);
+ statements.append(maker.Return(call));
} else {
ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>();
for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
@@ -244,13 +356,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
call = maker.Apply(typeParams.toList(), fn, args.toList());
if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) {
- statement = maker.Exec(call);
+ statements.append(maker.Exec(call));
} else {
- statement = maker.Return(call);
+ statements.append(maker.Return(call));
}
}
- JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ JCBlock body = maker.Block(0, statements.toList());
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
@@ -270,50 +382,56 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
- public java.util.List<JavacNode> addFieldsToBuilder(JavacNode builderType, java.util.List<Name> namesOfParameters, java.util.List<JCExpression> typesOfParameters, JCTree source) {
- int len = namesOfParameters.size();
+ public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
+ int len = builderFields.size();
java.util.List<JavacNode> existing = new ArrayList<JavacNode>();
for (JavacNode child : builderType.down()) {
if (child.getKind() == Kind.FIELD) existing.add(child);
}
- java.util.List<JavacNode>out = new ArrayList<JavacNode>();
-
top:
for (int i = len - 1; i >= 0; i--) {
- Name name = namesOfParameters.get(i);
- for (JavacNode exists : existing) {
- Name n = ((JCVariableDecl) exists.get()).name;
- if (n.equals(name)) {
- out.add(exists);
- continue top;
+ BuilderFieldData bfd = builderFields.get(i);
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source));
+ } else {
+ for (JavacNode exists : existing) {
+ Name n = ((JCVariableDecl) exists.get()).name;
+ if (n.equals(bfd.name)) {
+ bfd.createdFields.add(exists);
+ continue top;
+ }
}
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
+ JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null);
+ bfd.createdFields.add(injectField(builderType, newField));
}
- JavacTreeMaker maker = builderType.getTreeMaker();
- JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
- JCVariableDecl newField = maker.VarDef(mods, name, cloneType(maker, typesOfParameters.get(i), source, builderType.getContext()), null);
- out.add(injectField(builderType, newField));
}
-
- Collections.reverse(out);
- return out;
}
+ public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain) {
+ if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
+ makeSimpleSetterMethodForBuilder(builderType, fieldNode.createdFields.get(0), source, fluent, chain);
+ } else {
+ fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, builderType, source.get(), fluent, chain);
+ }
+ }
- public JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) {
+ private void makeSimpleSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
for (JavacNode child : builderType.down()) {
if (child.getKind() != Kind.METHOD) continue;
Name existingName = ((JCMethodDecl) child.get()).name;
- if (existingName.equals(fieldName)) return null;
+ if (existingName.equals(fieldName)) return;
}
- boolean isBoolean = isBoolean(fieldNode);
- String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean);
+ String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
- JavacTreeMaker maker = builderType.getTreeMaker();
- return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ JavacTreeMaker maker = fieldNode.getTreeMaker();
+ JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ injectMethod(builderType, newMethod);
}
public JavacNode findInnerClass(JavacNode parent, String name) {
@@ -331,4 +449,59 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
return injectType(tdParent, builder);
}
+
+ /**
+ * Returns the explicitly requested singular annotation on this node (field
+ * or parameter), or null if there's no {@code @Singular} annotation on it.
+ *
+ * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation.
+ */
+ private SingularData getSingularData(JavacNode node) {
+ for (JavacNode child : node.down()) {
+ if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Singular.class, child)) {
+ Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
+ AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
+ deleteAnnotationIfNeccessary(child, Singular.class);
+ String explicitSingular = ann.getInstance().value();
+ if (explicitSingular.isEmpty()) {
+ if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
+ node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ explicitSingular = pluralName.toString();
+ } else {
+ explicitSingular = autoSingularize(node.getName());
+ if (explicitSingular == null) {
+ node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
+ explicitSingular = pluralName.toString();
+ }
+ }
+ }
+ Name singularName = node.toName(explicitSingular);
+
+ JCExpression type = null;
+ if (node.get() instanceof JCVariableDecl) {
+ type = ((JCVariableDecl) node.get()).vartype;
+ }
+
+ String name = null;
+ List<JCExpression> typeArgs = List.nil();
+ if (type instanceof JCTypeApply) {
+ typeArgs = ((JCTypeApply) type).arguments;
+ type = ((JCTypeApply) type).clazz;
+ }
+
+ name = type.toString();
+
+ String targetFqn = JavacSingularsRecipes.get().toQualified(name);
+ JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
+ }
+ }
+
+ return null;
+ }
}
diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java
index 6043d1cb..c5b309c2 100644
--- a/src/core/lombok/javac/handlers/HandleConstructor.java
+++ b/src/core/lombok/javac/handlers/HandleConstructor.java
@@ -25,13 +25,13 @@ import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.ConfigurationKeys;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
import lombok.delombok.LombokOptionsFactory;
-import lombok.experimental.Builder;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
diff --git a/src/core/lombok/javac/handlers/HandlePrintAST.java b/src/core/lombok/javac/handlers/HandlePrintAST.java
index 9a52b9d9..0826d1d1 100644
--- a/src/core/lombok/javac/handlers/HandlePrintAST.java
+++ b/src/core/lombok/javac/handlers/HandlePrintAST.java
@@ -59,7 +59,7 @@ public class HandlePrintAST extends JavacAnnotationHandler<PrintAST> {
try {
stream.close();
} catch (Exception e) {
- Lombok.sneakyThrow(e);
+ throw Lombok.sneakyThrow(e);
}
}
}
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..53e01ebb
--- /dev/null
+++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.SpiLoadUtil;
+import lombok.core.TypeLibrary;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+
+import com.sun.source.tree.Tree.Kind;
+import com.sun.tools.javac.code.BoundKind;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.JCWildcard;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+public class JavacSingularsRecipes {
+ private static final JavacSingularsRecipes INSTANCE = new JavacSingularsRecipes();
+ private final Map<String, JavacSingularizer> singularizers = new HashMap<String, JavacSingularizer>();
+ private final TypeLibrary singularizableTypes = new TypeLibrary();
+
+ private JavacSingularsRecipes() {
+ try {
+ loadAll(singularizableTypes, singularizers);
+ singularizableTypes.lock();
+ } catch (IOException e) {
+ System.err.println("Lombok's @Singularizable feature is broken due to misconfigured SPI files: " + e);
+ }
+ }
+
+ private static void loadAll(TypeLibrary library, Map<String, JavacSingularizer> map) throws IOException {
+ for (JavacSingularizer handler : SpiLoadUtil.findServices(JavacSingularizer.class, JavacSingularizer.class.getClassLoader())) {
+ for (String type : handler.getSupportedTypes()) {
+ JavacSingularizer existingSingularizer = map.get(type);
+ if (existingSingularizer != null) {
+ JavacSingularizer toKeep = existingSingularizer.getClass().getName().compareTo(handler.getClass().getName()) > 0 ? handler : existingSingularizer;
+ System.err.println("Multiple singularizers found for type " + type + "; the alphabetically first class is used: " + toKeep.getClass().getName());
+ map.put(type, toKeep);
+ } else {
+ map.put(type, handler);
+ library.addType(type);
+ }
+ }
+ }
+ }
+
+ public static JavacSingularsRecipes get() {
+ return INSTANCE;
+ }
+
+ public String toQualified(String typeReference) {
+ return singularizableTypes.toQualified(typeReference);
+ }
+
+ public JavacSingularizer getSingularizer(String fqn) {
+ return singularizers.get(fqn);
+ }
+
+ public static final class SingularData {
+ private final JavacNode annotation;
+ private final Name singularName;
+ private final Name pluralName;
+ private final List<JCExpression> typeArgs;
+ private final String targetFqn;
+ private final JavacSingularizer singularizer;
+
+ public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> typeArgs, String targetFqn, JavacSingularizer singularizer) {
+ this.annotation = annotation;
+ this.singularName = singularName;
+ this.pluralName = pluralName;
+ this.typeArgs = typeArgs;
+ this.targetFqn = targetFqn;
+ this.singularizer = singularizer;
+ }
+
+ public JavacNode getAnnotation() {
+ return annotation;
+ }
+
+ public Name getSingularName() {
+ return singularName;
+ }
+
+ public Name getPluralName() {
+ return pluralName;
+ }
+
+ public List<JCExpression> getTypeArgs() {
+ return typeArgs;
+ }
+
+ public String getTargetFqn() {
+ return targetFqn;
+ }
+
+ public JavacSingularizer getSingularizer() {
+ return singularizer;
+ }
+
+ public String getTargetSimpleType() {
+ int idx = targetFqn.lastIndexOf(".");
+ return idx == -1 ? targetFqn : targetFqn.substring(idx + 1);
+ }
+ }
+
+ public static abstract class JavacSingularizer {
+ public abstract LombokImmutableList<String> getSupportedTypes();
+
+ /** Checks if any of the to-be-generated nodes (fields, methods) already exist. If so, errors on these (singulars don't support manually writing some of it, and returns true). */
+ public boolean checkForAlreadyExistingNodesAndGenerateError(JavacNode builderType, SingularData data) {
+ for (JavacNode child : builderType.down()) {
+ switch (child.getKind()) {
+ case FIELD: {
+ JCVariableDecl field = (JCVariableDecl) child.get();
+ Name name = field.name;
+ if (name == null) break;
+ if (getGeneratedBy(field) != null) continue;
+ for (Name fieldToBeGenerated : listFieldsToBeGenerated(data, builderType)) {
+ if (!fieldToBeGenerated.equals(name)) continue;
+ child.addError("Manually adding a field that @Singular @Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use @Singular.");
+ return true;
+ }
+ break;
+ }
+ case METHOD: {
+ JCMethodDecl method = (JCMethodDecl) child.get();
+ Name name = method.name;
+ if (name == null) break;
+ if (getGeneratedBy(method) != null) continue;
+ for (Name methodToBeGenerated : listMethodsToBeGenerated(data, builderType)) {
+ if (!methodToBeGenerated.equals(name)) continue;
+ child.addError("Manually adding a method that @Singular @Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use @Singular.");
+ return true;
+ }
+ break;
+ }}
+ }
+
+ return false;
+ }
+
+ public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ return Collections.singletonList(data.pluralName);
+ }
+
+ public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ Name p = data.pluralName;
+ Name s = data.singularName;
+ if (p.equals(s)) return Collections.singletonList(p);
+ return Arrays.asList(p, s);
+ }
+
+ public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source);
+ public abstract void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain);
+ public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName);
+
+ public boolean requiresCleaning() {
+ try {
+ return !getClass().getMethod("appendCleaningCode", SingularData.class, JavacNode.class, JCTree.class, ListBuffer.class).getDeclaringClass().equals(JavacSingularizer.class);
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ public void appendCleaningCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements) {
+ }
+
+ // -- Utility methods --
+
+ /**
+ * Adds the requested number of type arguments to the provided type, copying each argument in {@code typeArgs}. If typeArgs is too long, the extra elements are ignored.
+ * If {@code typeArgs} is null or too short, {@code java.lang.Object} will be substituted for each missing type argument.
+ *
+ * @param count The number of type arguments requested.
+ * @param addExtends If {@code true}, all bounds are either '? extends X' or just '?'. If false, the reverse is applied, and '? extends Foo' is converted to Foo, '?' to Object, etc.
+ * @param node Some node in the same AST. Just used to obtain makers and contexts and such.
+ * @param type The type to add generics to.
+ * @param typeArgs the list of type args to clone.
+ * @param source The source annotation that is the root cause of this code generation.
+ */
+ protected JCExpression addTypeArgs(int count, boolean addExtends, JavacNode node, JCExpression type, List<JCExpression> typeArgs, JCTree source) {
+ JavacTreeMaker maker = node.getTreeMaker();
+ List<JCExpression> clonedAndFixedTypeArgs = createTypeArgs(count, addExtends, node, typeArgs, source);
+
+ return maker.TypeApply(type, clonedAndFixedTypeArgs);
+ }
+
+ protected List<JCExpression> createTypeArgs(int count, boolean addExtends, JavacNode node, List<JCExpression> typeArgs, JCTree source) {
+ JavacTreeMaker maker = node.getTreeMaker();
+ Context context = node.getContext();
+
+ if (count < 0) throw new IllegalArgumentException("count is negative");
+ if (count == 0) return List.nil();
+ ListBuffer<JCExpression> arguments = new ListBuffer<JCExpression>();
+
+ if (typeArgs != null) for (JCExpression orig : typeArgs) {
+ if (!addExtends) {
+ if (orig.getKind() == Kind.UNBOUNDED_WILDCARD || orig.getKind() == Kind.SUPER_WILDCARD) {
+ arguments.append(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 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..2474ce7b
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Collections;
+
+import lombok.core.GuavaTypeMap;
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+abstract class JavacGuavaSingularizer extends JavacSingularizer {
+ protected String getSimpleTargetTypeName(SingularData data) {
+ return GuavaTypeMap.getGuavaTypeName(data.getTargetFqn());
+ }
+
+ protected String getBuilderMethodName(SingularData data) {
+ String simpleTypeName = getSimpleTargetTypeName(data);
+ if ("ImmutableSortedSet".equals(simpleTypeName) || "ImmutableSortedMap".equals(simpleTypeName)) return "naturalOrder";
+ return "builder";
+ }
+
+ protected abstract boolean isMap();
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), "Builder");
+ type = addTypeArgs(isMap() ? 2 : 1, false, builderType, type, data.getTypeArgs(), source);
+
+ JCVariableDecl buildField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), data.getPluralName(), type, null);
+ return Collections.singletonList(injectField(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+ }
+
+ void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ boolean mapMode = isMap();
+
+ Name keyName = !mapMode ? data.getSingularName() : builderType.toName(data.getSingularName() + "$key");
+ Name valueName = !mapMode ? null : builderType.toName(data.getSingularName() + "$value");
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, mapMode, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), mapMode ? "put" : "add");
+ List<JCExpression> invokeAddExpr;
+ if (mapMode) {
+ invokeAddExpr = List.<JCExpression>of(maker.Ident(keyName), maker.Ident(valueName));
+ } else {
+ invokeAddExpr = List.<JCExpression>of(maker.Ident(keyName));
+ }
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, invokeAddExpr);
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name methodName = data.getSingularName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(mapMode ? "put" : "add", methodName.toString()));
+ List<JCVariableDecl> params;
+ if (mapMode) {
+ JCExpression keyType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ JCExpression valueType = cloneParamType(1, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl paramKey = maker.VarDef(maker.Modifiers(paramFlags), keyName, keyType, null);
+ JCVariableDecl paramValue = maker.VarDef(maker.Modifiers(paramFlags), valueName, valueType, null);
+ params = List.of(paramKey, paramValue);
+ } else {
+ JCExpression paramType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ params = List.of(maker.VarDef(maker.Modifiers(paramFlags), data.getSingularName(), paramType, null));
+ }
+ JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ protected void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ boolean mapMode = isMap();
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, mapMode, source));
+ JCExpression thisDotFieldDotAddAll = chainDots(builderType, "this", data.getPluralName().toString(), mapMode ? "putAll" : "addAll");
+ JCExpression invokeAddAll = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAddAll, List.<JCExpression>of(maker.Ident(data.getPluralName())));
+ statements.append(maker.Exec(invokeAddAll));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name methodName = data.getPluralName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(mapMode ? "putAll" : "addAll", methodName.toString()));
+ JCExpression paramType;
+ if (mapMode) {
+ paramType = chainDots(builderType, "java", "util", "Map");
+ } else {
+ paramType = 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");
+ List<JCExpression> invokeTypeArgs = createTypeArgs(mapMode ? 2 : 1, false, builderType, data.getTypeArgs(), source);
+ empty = maker.Apply(invokeTypeArgs, emptyMethod, jceBlank);
+ }
+
+ JCExpression invokeBuild; {
+ //this.pluralName.build();
+ invokeBuild = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "build"), jceBlank);
+ }
+
+ JCExpression isNull; {
+ //this.pluralName == null
+ isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()), maker.Literal(CTC_BOT, null));
+ }
+
+ JCExpression init = maker.Conditional(isNull, empty, invokeBuild); // this.pluralName == null ? ImmutableX.of() : this.pluralName.build()
+
+ JCStatement jcs = maker.VarDef(maker.Modifiers(0), data.getPluralName(), varType, init);
+ statements.append(jcs);
+ }
+
+ protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+
+ JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
+ JCExpression thisDotField2 = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
+ JCExpression cond = maker.Binary(CTC_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+
+ JCExpression create = maker.Apply(jceBlank, chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), getBuilderMethodName(data)), jceBlank);
+ JCStatement thenPart = maker.Exec(maker.Assign(thisDotField2, create));
+
+ return maker.If(cond, thenPart, null);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
new file mode 100644
index 00000000..6f8ff705
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Collections;
+
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularizer {
+ @Override public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ return super.listFieldsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listMethodsToBeGenerated(data, builderType);
+ }
+
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.generateFields(data, builderType, source);
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs(), source);
+
+ JCVariableDecl buildField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), data.getPluralName(), type, null);
+ return Collections.singletonList(injectField(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.generateMethods(data, builderType, source, fluent, chain);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ Name thisName = builderType.toName("this");
+
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
+ generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
+ generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+ }
+
+ void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getSingularName())));
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getSingularName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("add", name.toString()));
+ JCExpression paramType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getSingularName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "addAll");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getPluralName())));
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getPluralName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("addAll", name.toString()));
+ JCExpression paramType = chainDots(builderType, "java", "util", "Collection");
+ paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs(), source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
new file mode 100644
index 00000000..65e91fa0
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.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 = chainDotsString(builderType, data.getTargetFqn());
+ localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source);
+ JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
+ statements.append(varDefStat);
+ statements.append(switchStat);
+ }
+
+ private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ Name thisName = builderType.toName("this");
+
+ JCExpression argToUnmodifiable; {
+ // new java.util.ArrayList<Generics>(this.pluralName);
+ List<JCExpression> constructorArgs = List.nil();
+ JCExpression thisDotPluralName = maker.Select(maker.Ident(thisName), data.getPluralName());
+ constructorArgs = List.<JCExpression>of(thisDotPluralName);
+ JCExpression targetTypeExpr = chainDots(builderType, "java", "util", "ArrayList");
+ targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs(), source);
+ argToUnmodifiable = maker.NewClass(null, jceBlank, targetTypeExpr, constructorArgs, null);
+ }
+
+ JCStatement unmodifiableStat; {
+ // pluralname = Collections.unmodifiableInterfaceType(-newlist-);
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "unmodifiableList"), List.of(argToUnmodifiable));
+ unmodifiableStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+
+ return List.of(unmodifiableStat);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
new file mode 100644
index 00000000..ed91698d
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Arrays;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Map", "java.util.SortedMap", "java.util.NavigableMap");
+ }
+
+ @Override public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ String p = data.getPluralName().toString();
+ return Arrays.asList(builderType.toName(p + "$key"), builderType.toName(p + "$value"));
+ }
+
+ @Override public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listMethodsToBeGenerated(data, builderType);
+ }
+
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.generateFields(data, builderType, source);
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ JCVariableDecl buildKeyField; {
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs(), source);
+ buildKeyField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName(data.getPluralName() + "$key"), type, null);
+ }
+
+ JCVariableDecl buildValueField; {
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ List<JCExpression> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.size() > 1) tArgs = tArgs.tail;
+ else tArgs = List.nil();
+ type = addTypeArgs(1, false, builderType, type, tArgs, source);
+ buildValueField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName(data.getPluralName() + "$value"), type, null);
+ }
+
+ JavacNode valueFieldNode = injectField(builderType, buildValueField);
+ JavacNode keyFieldNode = injectField(builderType, buildKeyField);
+
+ return Arrays.asList(keyFieldNode, valueFieldNode);
+ }
+
+ @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.generateMethods(data, builderType, source, fluent, chain);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
+ }
+
+ private void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source));
+ Name keyName = builderType.toName(data.getSingularName().toString() + "Key");
+ Name valueName = builderType.toName(data.getSingularName().toString() + "Value");
+ /* this.pluralname$key.add(singularnameKey); */ {
+ JCExpression thisDotKeyFieldDotAdd = chainDots(builderType, "this", data.getPluralName() + "$key", "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotKeyFieldDotAdd, List.<JCExpression>of(maker.Ident(keyName)));
+ statements.append(maker.Exec(invokeAdd));
+ }
+ /* this.pluralname$value.add(singularnameValue); */ {
+ JCExpression thisDotValueFieldDotAdd = chainDots(builderType, "this", data.getPluralName() + "$value", "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotValueFieldDotAdd, List.<JCExpression>of(maker.Ident(valueName)));
+ statements.append(maker.Exec(invokeAdd));
+ }
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+
+ Name name = data.getSingularName();
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("put", name.toString()));
+ JCExpression paramTypeKey = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ JCExpression paramTypeValue = cloneParamType(1, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl paramKey = maker.VarDef(maker.Modifiers(paramFlags), keyName, paramTypeKey, null);
+ JCVariableDecl paramValue = maker.VarDef(maker.Modifiers(paramFlags), valueName, paramTypeValue, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(paramKey, paramValue), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ private void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> jceBlank = List.nil();
+ JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source));
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ long baseFlags = JavacHandlerUtil.addFinalIfNeeded(0, builderType.getContext());
+ Name entryName = builderType.toName("$lombokEntry");
+
+ JCExpression forEachType = chainDots(builderType, "java", "util", "Map", "Entry");
+ forEachType = addTypeArgs(2, true, builderType, forEachType, data.getTypeArgs(), source);
+ JCExpression keyArg = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(entryName), builderType.toName("getKey")), List.<JCExpression>nil());
+ JCExpression valueArg = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(entryName), builderType.toName("getValue")), List.<JCExpression>nil());
+ JCExpression addKey = maker.Apply(List.<JCExpression>nil(), chainDots(builderType, "this", data.getPluralName() + "$key", "add"), List.of(keyArg));
+ JCExpression addValue = maker.Apply(List.<JCExpression>nil(), chainDots(builderType, "this", data.getPluralName() + "$value", "add"), List.of(valueArg));
+ JCBlock forEachBody = maker.Block(0, List.<JCStatement>of(maker.Exec(addKey), maker.Exec(addValue)));
+ JCExpression entrySetInvocation = maker.Apply(jceBlank, maker.Select(maker.Ident(data.getPluralName()), builderType.toName("entrySet")), jceBlank);
+ JCStatement forEach = maker.ForeachLoop(maker.VarDef(maker.Modifiers(baseFlags), entryName, forEachType, null), entrySetInvocation, forEachBody);
+ statements.append(forEach);
+
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getPluralName();
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("putAll", name.toString()));
+ JCExpression paramType = chainDots(builderType, "java", "util", "Map");
+ paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs(), source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), jceBlank, body, null);
+ injectMethod(builderType, method);
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ if (data.getTargetFqn().equals("java.util.Map")) {
+ statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source));
+ } else {
+ statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source));
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
new file mode 100644
index 00000000..317233cb
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilSetSingularizer extends JavacJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet");
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ if (data.getTargetFqn().equals("java.util.Set")) {
+ statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source));
+ } else {
+ statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source));
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
new file mode 100644
index 00000000..c75dfbdd
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+import lombok.ConfigurationKeys;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
+ protected final JavacSingularizer guavaListSetSingularizer = new JavacGuavaSetListSingularizer();
+ protected final JavacSingularizer guavaMapSingularizer = new JavacGuavaMapSingularizer();
+
+ protected boolean useGuavaInstead(JavacNode node) {
+ return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA));
+ }
+
+ protected List<JCStatement> createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
+
+ if (emptyCollectionMethod != null) { // case 0: (empty); break;
+ JCStatement assignStat; {
+ // pluralName = java.util.Collections.emptyCollectionMethod();
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", emptyCollectionMethod), jceBlank);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase emptyCase = maker.Case(maker.Literal(CTC_INT, 0), List.of(assignStat, breakStat));
+ cases.append(emptyCase);
+ }
+
+ if (singletonCollectionMethod != null) { // case 1: (singleton); break;
+ JCStatement assignStat; {
+ // !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0));
+ // mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0));
+ JCExpression zeroLiteral = maker.Literal(CTC_INT, 0);
+ JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral));
+ List<JCExpression> args;
+ if (mapMode) {
+ JCExpression zeroLiteralClone = maker.Literal(CTC_INT, 0);
+ JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$value" : ""), "get"), List.of(zeroLiteralClone));
+ args = List.of(arg, arg2);
+ } else {
+ args = List.of(arg);
+ }
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", singletonCollectionMethod), args);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase singletonCase = maker.Case(maker.Literal(CTC_INT, 1), List.of(assignStat, breakStat));
+ cases.append(singletonCase);
+ }
+
+ { // default:
+ List<JCStatement> statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source);
+ JCCase defaultCase = maker.Case(null, statements);
+ cases.append(defaultCase);
+ }
+
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true), cases.toList());
+ JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source);
+ JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
+ return List.of(varDefStat, switchStat);
+ }
+
+ protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+
+ Name v1Name = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName();
+ Name v2Name = mapMode ? builderType.toName(data.getPluralName() + "$value") : null;
+ JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v1Name);
+ JCExpression cond = maker.Binary(CTC_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+ thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v1Name);
+ JCExpression v1Type = chainDots(builderType, "java", "util", "ArrayList");
+ v1Type = addTypeArgs(1, false, builderType, v1Type, data.getTypeArgs(), source);
+ JCExpression constructArrayList = maker.NewClass(null, jceBlank, v1Type, jceBlank, null);
+ JCStatement initV1 = maker.Exec(maker.Assign(thisDotField, constructArrayList));
+ JCStatement thenPart;
+ if (mapMode) {
+ thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v2Name);
+ JCExpression v2Type = chainDots(builderType, "java", "util", "ArrayList");
+ List<JCExpression> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.tail != null) tArgs = tArgs.tail;
+ else tArgs = List.nil();
+ v2Type = addTypeArgs(1, false, builderType, v2Type, tArgs, source);
+ constructArrayList = maker.NewClass(null, jceBlank, v2Type, jceBlank, null);
+ JCStatement initV2 = maker.Exec(maker.Assign(thisDotField, constructArrayList));
+ thenPart = maker.Block(0, List.of(initV1, initV2));
+ } else {
+ thenPart = initV1;
+ }
+ return maker.If(cond, thenPart, null);
+ }
+
+ protected List<JCStatement> createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ Name thisName = builderType.toName("this");
+
+ JCStatement createStat; {
+ // pluralName = new java.util.TargetType(initialCap);
+ List<JCExpression> constructorArgs = List.nil();
+ if (addInitialCapacityArg) {
+ Name varName = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName();
+ // this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE;
+ // lessThanCutOff = this.varName.size() < MAX_POWER_OF_2
+ JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard), maker.Literal(CTC_INT, 0x40000000));
+ JCExpression integerMaxValue = 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/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java
index f64e36a1..059bb004 100644
--- a/src/delombok/lombok/delombok/Delombok.java
+++ b/src/delombok/lombok/delombok/Delombok.java
@@ -509,7 +509,7 @@ public class Delombok {
DelombokResult result = new DelombokResult(catcher.getComments(unit), unit, force || options.isChanged(unit), fps);
if (verbose) feedback.printf("File: %s [%s]\n", unit.sourcefile.getName(), result.isChanged() ? "delomboked" : "unchanged");
Writer rawWriter;
- if (presetWriter != null) rawWriter = presetWriter;
+ if (presetWriter != null) rawWriter = createUnicodeEscapeWriter(presetWriter);
else if (output == null) rawWriter = createStandardOutWriter();
else rawWriter = createFileWriter(output, baseMap.get(unit), unit.sourcefile.toUri());
BufferedWriter writer = new BufferedWriter(rawWriter);
@@ -605,6 +605,10 @@ public class Delombok {
return createUnicodeEscapeWriter(System.out);
}
+ private Writer createUnicodeEscapeWriter(Writer writer) {
+ return new UnicodeEscapeWriter(writer, charset);
+ }
+
private Writer createUnicodeEscapeWriter(OutputStream out) {
return new UnicodeEscapeWriter(new OutputStreamWriter(out, charset), charset);
}
diff --git a/src/delombok/lombok/delombok/DelombokApp.java b/src/delombok/lombok/delombok/DelombokApp.java
index 276bd7de..aa753fc8 100644
--- a/src/delombok/lombok/delombok/DelombokApp.java
+++ b/src/delombok/lombok/delombok/DelombokApp.java
@@ -88,7 +88,7 @@ public class DelombokApp extends LombokApp {
// Since we only read from it, not closing it should not be a problem.
@SuppressWarnings({"resource", "all"}) final JarFile toolsJarFile = new JarFile(toolsJar);
- ClassLoader loader = new ClassLoader() {
+ ClassLoader loader = new ClassLoader(DelombokApp.class.getClassLoader()) {
private Class<?> loadStreamAsClass(String name, boolean resolve, InputStream in) throws ClassNotFoundException {
try {
try {
@@ -107,16 +107,24 @@ public class DelombokApp extends LombokApp {
} finally {
in.close();
}
- } catch (IOException e2) {
+ } catch (Exception e2) {
throw new ClassNotFoundException(name, e2);
}
}
@Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- String rawName = name.replace(".", "/") + ".class";
+ String rawName, altName; {
+ String binName = name.replace(".", "/");
+ rawName = binName + ".class";
+ altName = binName + ".SCL.lombok";
+ }
JarEntry entry = toolsJarFile.getJarEntry(rawName);
if (entry == null) {
- if (name.startsWith("lombok.")) return loadStreamAsClass(name, resolve, super.getResourceAsStream(rawName));
+ if (name.startsWith("lombok.")) {
+ InputStream res = getParent().getResourceAsStream(rawName);
+ if (res == null) res = getParent().getResourceAsStream(altName);
+ return loadStreamAsClass(name, resolve, res);
+ }
return super.loadClass(name, resolve);
}
diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
index 9dd7e40e..72a8ff59 100644
--- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
+++ b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
@@ -104,7 +104,6 @@ import com.sun.tools.javac.tree.JCTree.LetExpr;
import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.tree.TreeScanner;
-import com.sun.tools.javac.util.Convert;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Position;
@@ -209,7 +208,6 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
private void consumeComments(int until, JCTree tree) throws IOException {
boolean prevNewLine = onNewLine;
- boolean found = false;
CommentInfo head = comments.head;
while (comments.nonEmpty() && head.pos < until) {
printComment(head);
@@ -355,7 +353,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
}
needsSpace = false;
- out.write(Convert.escapeUnicode(s.toString()));
+ out.write(s.toString());
onNewLine = false;
aligned = false;
@@ -1413,16 +1411,51 @@ public class PrettyCommentsPrinter extends JCTree.Visitor {
else if (CTC_FLOAT.equals(typeTag)) print(tree.value + "F");
else if (CTC_DOUBLE.equals(typeTag)) print(tree.value.toString());
else if (CTC_CHAR.equals(typeTag)) {
- print("\'" + Convert.quote(String.valueOf((char)((Number)tree.value).intValue())) + "\'");
+ print("\'" + quoteChar((char)((Number)tree.value).intValue()) + "\'");
}
else if (CTC_BOOLEAN.equals(typeTag)) print(((Number)tree.value).intValue() == 1 ? "true" : "false");
else if (CTC_BOT.equals(typeTag)) print("null");
- else print("\"" + Convert.quote(tree.value.toString()) + "\"");
+ else print("\"" + quoteChars(tree.value.toString()) + "\"");
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
+ public static String quoteChars(String s) {
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < s.length(); i++) {
+ buf.append(quoteChar(s.charAt(i)));
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Escapes a character if it has an escape sequence or is non-printable
+ * ASCII. Leaves non-ASCII characters alone.
+ */
+ public static String quoteChar(char ch) {
+ switch (ch) {
+ case '\b':
+ return "\\b";
+ case '\f':
+ return "\\f";
+ case '\n':
+ return "\\n";
+ case '\r':
+ return "\\r";
+ case '\t':
+ return "\\t";
+ case '\'':
+ return "\\'";
+ case '\"':
+ return "\\\"";
+ case '\\':
+ return "\\\\";
+ default:
+ return ch < 32 ? String.format("\\%03o", (int) ch) : String.valueOf(ch);
+ }
+ }
+
public void visitTypeIdent(JCPrimitiveTypeTree tree) {
TypeTag typetag = typeTag(tree);
try {
diff --git a/src/delombok/lombok/delombok/UnicodeEscapeWriter.java b/src/delombok/lombok/delombok/UnicodeEscapeWriter.java
index 95b55078..9c2ef190 100644
--- a/src/delombok/lombok/delombok/UnicodeEscapeWriter.java
+++ b/src/delombok/lombok/delombok/UnicodeEscapeWriter.java
@@ -63,6 +63,6 @@ public class UnicodeEscapeWriter extends Writer {
}
protected void writeUnicodeEscape(char c) throws IOException {
- writer.write("\\u" + Integer.toHexString(c));
+ writer.write(String.format("\\u%04x", (int) c));
}
} \ No newline at end of file
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java
new file mode 100644
index 00000000..0d21c212
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java
@@ -0,0 +1,106 @@
+package lombok.eclipse.agent;
+
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import lombok.patcher.ClassRootFinder;
+import lombok.patcher.Hook;
+import lombok.patcher.MethodTarget;
+import lombok.patcher.ScriptManager;
+import lombok.patcher.StackRequest;
+import lombok.patcher.scripts.ScriptBuilder;
+
+public class EclipseLoaderPatcher {
+ public static boolean overrideLoadDecide(ClassLoader original, String name, boolean resolve) {
+ return name.startsWith("lombok.");
+ }
+
+ public static Class<?> overrideLoadResult(ClassLoader original, String name, boolean resolve) throws ClassNotFoundException {
+ try {
+ Field shadowLoaderField = original.getClass().getField("lombok$shadowLoader");
+ ClassLoader shadowLoader = (ClassLoader) shadowLoaderField.get(original);
+ if (shadowLoader == null) {
+ String jarLoc = (String) original.getClass().getField("lombok$location").get(null);
+ JarFile jf = new JarFile(jarLoc);
+ InputStream in = null;
+ try {
+ ZipEntry entry = jf.getEntry("lombok/launch/ShadowClassLoader.class");
+ in = jf.getInputStream(entry);
+ byte[] bytes = new byte[65536];
+ int len = 0;
+ while (true) {
+ int r = in.read(bytes, len, bytes.length - len);
+ if (r == -1) break;
+ len += r;
+ if (len == bytes.length) throw new IllegalStateException("lombok.launch.ShadowClassLoader too large.");
+ }
+ in.close();
+ Class<?> shadowClassLoaderClass; {
+ Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
+ defineClassMethod.setAccessible(true);
+ shadowClassLoaderClass = (Class<?>) defineClassMethod.invoke(original, "lombok.launch.ShadowClassLoader", bytes, 0, len);
+ }
+ Constructor<?> constructor = shadowClassLoaderClass.getDeclaredConstructor(ClassLoader.class, String.class, String.class, String[].class);
+ constructor.setAccessible(true);
+ shadowLoader = (ClassLoader) constructor.newInstance(original, "lombok", jarLoc, new String[] {"lombok."});
+ shadowLoaderField.set(original, shadowLoader);
+ } finally {
+ if (in != null) in.close();
+ jf.close();
+ }
+ }
+
+ if (resolve) {
+ Method m = shadowLoader.getClass().getDeclaredMethod("loadClass", String.class, boolean.class);
+ m.setAccessible(true);
+ return (Class<?>) m.invoke(shadowLoader, name, true);
+ } else {
+ return shadowLoader.loadClass(name);
+ }
+ } catch (Exception ex) {
+ Throwable t = ex;
+ if (t instanceof InvocationTargetException) t = t.getCause();
+ if (t instanceof RuntimeException) throw (RuntimeException) t;
+ if (t instanceof Error) throw (Error) t;
+ throw new RuntimeException(t);
+ }
+ }
+
+ private static final String SELF_NAME = "lombok.eclipse.agent.EclipseLoaderPatcher";
+
+ public static void patchEquinoxLoaders(ScriptManager sm, Class<?> launchingContext) {
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader", "loadClass",
+ "java.lang.Class", "java.lang.String", "boolean"))
+ .target(new MethodTarget("org.eclipse.osgi.framework.adapter.core.AbstractClassLoader", "loadClass",
+ "java.lang.Class", "java.lang.String", "boolean"))
+ .target(new MethodTarget("org.eclipse.osgi.internal.loader.ModuleClassLoader", "loadClass",
+ "java.lang.Class", "java.lang.String", "boolean"))
+ .decisionMethod(new Hook(SELF_NAME, "overrideLoadDecide", "boolean", "java.lang.ClassLoader", "java.lang.String", "boolean"))
+ .valueMethod(new Hook(SELF_NAME, "overrideLoadResult", "java.lang.Class", "java.lang.ClassLoader", "java.lang.String", "boolean"))
+ .transplant()
+ .request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
+
+ sm.addScript(ScriptBuilder.addField().setPublic()
+ .fieldType("Ljava/lang/ClassLoader;")
+ .fieldName("lombok$shadowLoader")
+ .targetClass("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader")
+ .targetClass("org.eclipse.osgi.framework.adapter.core.AbstractClassLoader")
+ .targetClass("org.eclipse.osgi.internal.loader.ModuleClassLoader")
+ .build());
+
+ sm.addScript(ScriptBuilder.addField().setPublic().setStatic().setFinal()
+ .fieldType("Ljava/lang/String;")
+ .fieldName("lombok$location")
+ .targetClass("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader")
+ .targetClass("org.eclipse.osgi.framework.adapter.core.AbstractClassLoader")
+ .targetClass("org.eclipse.osgi.internal.loader.ModuleClassLoader")
+ .value(ClassRootFinder.findClassRootOfClass(launchingContext))
+ .build());
+ }
+}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
index e14d1367..efc7ce94 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,13 +28,13 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import lombok.core.Agent;
+import lombok.core.AgentLauncher;
+
import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
import lombok.patcher.ScriptManager;
import lombok.patcher.StackRequest;
import lombok.patcher.TargetMatcher;
-import lombok.patcher.equinox.EquinoxClassLoader;
import lombok.patcher.scripts.ScriptBuilder;
/**
@@ -44,13 +44,12 @@ import lombok.patcher.scripts.ScriptBuilder;
* classes in this package for more information about which classes are transformed and how they are
* transformed.
*/
-public class EclipsePatcher extends Agent {
+public class EclipsePatcher implements AgentLauncher.AgentLaunchable {
// At some point I'd like the agent to be capable of auto-detecting if its on eclipse or on ecj. This class is a sure sign we're not in ecj but in eclipse. -ReinierZ
@SuppressWarnings("unused")
private static final String ECLIPSE_SIGNATURE_CLASS = "org/eclipse/core/runtime/adaptor/EclipseStarter";
- @Override
- public void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected) throws Exception {
+ @Override public void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Exception {
String[] args = agentArgs == null ? new String[0] : agentArgs.split(":");
boolean forceEcj = false;
boolean forceEclipse = false;
@@ -69,18 +68,14 @@ public class EclipsePatcher extends Agent {
else if (forceEclipse) ecj = false;
else ecj = injected;
- registerPatchScripts(instrumentation, injected, ecj);
+ registerPatchScripts(instrumentation, injected, ecj, launchingContext);
}
- private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly) {
+ private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly, Class<?> launchingContext) {
ScriptManager sm = new ScriptManager();
sm.registerTransformer(instrumentation);
if (!ecjOnly) {
- EquinoxClassLoader.addPrefix("lombok.");
- EquinoxClassLoader.registerScripts(sm);
- }
-
- if (!ecjOnly) {
+ EclipseLoaderPatcher.patchEquinoxLoaders(sm, launchingContext);
patchCatchReparse(sm);
patchIdentifierEndReparse(sm);
patchRetrieveEllipsisStartPosition(sm);
@@ -114,7 +109,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.compiler.SourceElementNotifier", "notifySourceElementRequestor", "void", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration", "org.eclipse.jdt.internal.compiler.ast.ImportReference"))
.methodToWrap(new Hook("org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt", "get", "int", "java.lang.Object"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "getSourceEndFixed", "int", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getSourceEndFixed", "int", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.requestExtra(StackRequest.PARAM1)
.transplant().build());
@@ -127,7 +122,7 @@ public class EclipsePatcher extends Agent {
"org.eclipse.jdt.core.dom.MethodDeclaration"
))
.methodToWrap(new Hook("org.eclipse.jface.text.IDocument", "get", "java.lang.String", "int", "int"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "getRealMethodDeclarationSource", "java.lang.String", "java.lang.String", "java.lang.Object", "org.eclipse.jdt.core.dom.MethodDeclaration"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getRealMethodDeclarationSource", "java.lang.String", "java.lang.String", "java.lang.Object", "org.eclipse.jdt.core.dom.MethodDeclaration"))
.requestExtra(StackRequest.THIS, StackRequest.PARAM4)
.transplant().build());
@@ -136,20 +131,20 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMemberDeclarations"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodComments"))
.methodToReplace(new Hook("org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil", "getMethodDeclarationNode", "org.eclipse.jdt.core.dom.MethodDeclaration", "org.eclipse.jdt.core.IMethod", "org.eclipse.jdt.core.dom.CompilationUnit"))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchFixes", "getRealMethodDeclarationNode", "org.eclipse.jdt.core.dom.MethodDeclaration", "org.eclipse.jdt.core.IMethod", "org.eclipse.jdt.core.dom.CompilationUnit"))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getRealMethodDeclarationNode", "org.eclipse.jdt.core.dom.MethodDeclaration", "org.eclipse.jdt.core.IMethod", "org.eclipse.jdt.core.dom.CompilationUnit"))
.transplant().build());
/* Do not add @Override's for generated methods */
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ListRewrite", "insertFirst"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isListRewriteOnGeneratedNode", "boolean", "org.eclipse.jdt.core.dom.rewrite.ListRewrite"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isListRewriteOnGeneratedNode", "boolean", "org.eclipse.jdt.core.dom.rewrite.ListRewrite"))
.request(StackRequest.THIS)
.transplant().build());
/* Do not add comments for generated methods */
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodComment"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
.request(StackRequest.PARAM2)
.transplant().build());
}
@@ -162,7 +157,7 @@ public class EclipsePatcher extends Agent {
*/
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.core.internal.runtime.Product", "getProperty", "java.lang.String", "java.lang.String"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "addLombokNotesToEclipseAboutDialog", "java.lang.String", "java.lang.String", "java.lang.String"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "addLombokNotesToEclipseAboutDialog", "java.lang.String", "java.lang.String", "java.lang.String"))
.request(StackRequest.RETURN_VALUE, StackRequest.PARAM1)
.transplant().build());
}
@@ -175,8 +170,8 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.internal.ui.search.OccurrencesFinder", "addUsage"))
.target(new MethodTarget("org.eclipse.jdt.internal.ui.search.OccurrencesFinder", "addWrite"))
.target(new MethodTarget("org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingReconciler$PositionCollector", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
- .valueMethod(new Hook("lombok.eclipse.agent.PatchFixes", "returnFalse", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
+ .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnFalse", "boolean", "java.lang.Object"))
.request(StackRequest.PARAM1)
.build());
}
@@ -201,9 +196,9 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.QualifiedName"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName"))
// if a generated node has children we can just ignore them as well;
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
.request(StackRequest.PARAM1)
- .valueMethod(new Hook("lombok.eclipse.agent.PatchFixes", "returnFalse", "boolean", "java.lang.Object"))
+ .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnFalse", "boolean", "java.lang.Object"))
.build());
}
@@ -211,7 +206,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.replaceMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer$ListRewriter", "rewriteList"))
.methodToReplace(new Hook("org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent", "getChildren", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]"))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchFixes", "listRewriteHandleGeneratedMethods", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent"))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "listRewriteHandleGeneratedMethods", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent"))
.build());
}
@@ -223,37 +218,37 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.CompilationUnit"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.CompilationUnit", "types", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.AnnotationTypeDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.AnnotationTypeDeclaration", "bodyDeclarations", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.AnonymousClassDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.AnonymousClassDeclaration", "bodyDeclarations", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.TypeDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.TypeDeclaration", "bodyDeclarations", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.EnumDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.EnumDeclaration", "bodyDeclarations", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.EnumDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.EnumDeclaration", "enumConstants", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
}
@@ -261,7 +256,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.replaceMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer", "visit"))
.methodToReplace(new Hook("org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "getTokenEndOffset", "int", "int", "int"))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchFixes", "getTokenEndOffsetFixed", "int", "org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "int", "int", "java.lang.Object"))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getTokenEndOffsetFixed", "int", "org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "int", "int", "java.lang.Object"))
.requestExtra(StackRequest.PARAM1)
.transplant()
.build());
@@ -273,7 +268,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder", "writeClassFileContents"))
.target(new MethodTarget("org.eclipse.jdt.internal.core.builder.AbstractImageBuilder", "writeClassFileContents"))
.methodToWrap(new Hook("org.eclipse.jdt.internal.compiler.ClassFile", "getBytes", "byte[]"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "runPostCompiler", "byte[]", "byte[]", "java.lang.String"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "byte[]", "byte[]", "java.lang.String"))
.requestExtra(StackRequest.PARAM3)
.build());
}
@@ -282,24 +277,22 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.compiler.tool.EclipseCompilerImpl", "outputClassFiles"))
.methodToWrap(new Hook("javax.tools.JavaFileObject", "openOutputStream", "java.io.OutputStream"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "runPostCompiler", "java.io.OutputStream", "java.io.OutputStream"))
- .transplant()
- .build());
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "java.io.OutputStream", "java.io.OutputStream"))
+ .transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.compiler.util.Util", "writeToDisk"))
.methodToWrap(new Hook("java.io.BufferedOutputStream", "<init>", "void", "java.io.OutputStream", "int"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "runPostCompiler", "java.io.BufferedOutputStream", "java.io.BufferedOutputStream", "java.lang.String", "java.lang.String"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "java.io.BufferedOutputStream", "java.io.BufferedOutputStream", "java.lang.String", "java.lang.String"))
.requestExtra(StackRequest.PARAM2, StackRequest.PARAM3)
- .transplant()
- .build());
+ .transplant().build());
}
private static void patchHideGeneratedNodes(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder", "findByNode"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder", "findByBinding"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedSimpleNames", "org.eclipse.jdt.core.dom.SimpleName[]",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedSimpleNames", "org.eclipse.jdt.core.dom.SimpleName[]",
"org.eclipse.jdt.core.dom.SimpleName[]"))
.request(StackRequest.RETURN_VALUE).build());
@@ -319,22 +312,22 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ASTRewrite", "replace"))
.target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ASTRewrite", "remove"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "skipRewritingGeneratedNodes", "boolean",
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "skipRewritingGeneratedNodes", "boolean",
"org.eclipse.jdt.core.dom.ASTNode"))
.transplant().request(StackRequest.PARAM1).build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameTypeProcessor", "addConstructorRenames"))
.methodToWrap(new Hook("org.eclipse.jdt.core.IType", "getMethods", "org.eclipse.jdt.core.IMethod[]"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedMethods", "org.eclipse.jdt.core.IMethod[]",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedMethods", "org.eclipse.jdt.core.IMethod[]",
"org.eclipse.jdt.core.IMethod[]"))
.transplant().build());
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameAnalyzeUtil$ProblemNodeFinder$NameNodeVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
- .valueMethod(new Hook("lombok.eclipse.agent.PatchFixes", "returnTrue", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
+ .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnTrue", "boolean", "java.lang.Object"))
.request(StackRequest.PARAM1)
.transplant().build());
}
@@ -342,21 +335,21 @@ public class EclipsePatcher extends Agent {
private static void patchCatchReparse(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveStartingCatchPosition"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "fixRetrieveStartingCatchPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveStartingCatchPosition", "int", "int", "int"))
.transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM1).build());
}
private static void patchIdentifierEndReparse(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveIdentifierEndPosition"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "fixRetrieveIdentifierEndPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveIdentifierEndPosition", "int", "int", "int"))
.transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
}
private static void patchRetrieveEllipsisStartPosition(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveEllipsisStartPosition"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "fixRetrieveEllipsisStartPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveEllipsisStartPosition", "int", "int", "int"))
.transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
}
@@ -364,7 +357,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition"))
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "int"))
.transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
}
@@ -396,14 +389,14 @@ public class EclipsePatcher extends Agent {
return Collections.singleton("org.eclipse.jdt.core.dom.ASTConverter");
}
}).request(StackRequest.PARAM1, StackRequest.RETURN_VALUE)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlag", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void",
"org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
.request(StackRequest.PARAM2, StackRequest.RETURN_VALUE)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlag", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void",
"org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.transplant().build());
@@ -421,7 +414,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationStatement", "org.eclipse.jdt.core.dom.VariableDeclarationStatement", "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration"))
/* Targets above are only patched because the resulting dom nodes should be marked if generated. */
.request(StackRequest.PARAM1, StackRequest.RETURN_VALUE)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlag", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void",
"org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.transplant().build());
@@ -443,7 +436,7 @@ public class EclipsePatcher extends Agent {
}
}).methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM1)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -451,7 +444,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM2)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -460,7 +453,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM4)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -468,7 +461,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM4)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -476,7 +469,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM3)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -484,7 +477,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM3)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
}
@@ -495,7 +488,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget(PARSER_SIG, "parse", "void",
"org.eclipse.jdt.internal.compiler.ast.MethodDeclaration",
"org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
.transplant()
.request(StackRequest.PARAM1).build());
@@ -503,7 +496,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget(PARSER_SIG, "parse", "void",
"org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration",
"org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "boolean"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
.transplant()
.request(StackRequest.PARAM1).build());
@@ -512,7 +505,7 @@ public class EclipsePatcher extends Agent {
"org.eclipse.jdt.internal.compiler.ast.Initializer",
"org.eclipse.jdt.internal.compiler.ast.TypeDeclaration",
"org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
.transplant()
.request(StackRequest.PARAM1).build());
}
@@ -528,12 +521,12 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(PARSER_SIG, "getMethodBodies", "void", CUD_SIG))
- .wrapMethod(new Hook("lombok.eclipse.TransformEclipseAST", "transform", "void", PARSER_SIG, CUD_SIG))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform", "void", PARSER_SIG, CUD_SIG))
.request(StackRequest.THIS, StackRequest.PARAM1).build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(PARSER_SIG, "endParse", CUD_SIG, "int"))
- .wrapMethod(new Hook("lombok.eclipse.TransformEclipseAST", "transform_swapped", "void", CUD_SIG, PARSER_SIG))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform_swapped", "void", CUD_SIG, PARSER_SIG))
.request(StackRequest.THIS, StackRequest.RETURN_VALUE).build());
}
@@ -549,7 +542,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget(CLASSSCOPE_SIG, "buildFieldsAndMethods", "void"))
.request(StackRequest.THIS)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchDelegatePortal", "handleDelegateForType", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "handleDelegateForType", "boolean", "java.lang.Object"))
.build());
}
@@ -579,24 +572,24 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(PARSER_SIG, "consumeExitVariableWithInitialization", "void"))
.request(StackRequest.THIS)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchValEclipsePortal", "copyInitializationOfLocalDeclaration", "void", "java.lang.Object"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "copyInitializationOfLocalDeclaration", "void", "java.lang.Object"))
.build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(PARSER_SIG, "consumeEnhancedForStatementHeader", "void"))
.request(StackRequest.THIS)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchValEclipsePortal", "copyInitializationOfForEachIterable", "void", "java.lang.Object"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "copyInitializationOfForEachIterable", "void", "java.lang.Object"))
.build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", VARIABLEDECLARATIONSTATEMENT_SIG, LOCALDECLARATION_SIG))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchValEclipsePortal", "addFinalAndValAnnotationToVariableDeclarationStatement",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "addFinalAndValAnnotationToVariableDeclarationStatement",
"void", "java.lang.Object", "java.lang.Object", "java.lang.Object"))
.request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", SINGLEVARIABLEDECLARATION_SIG, LOCALDECLARATION_SIG))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchValEclipsePortal", "addFinalAndValAnnotationToSingleVariableDeclaration",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "addFinalAndValAnnotationToSingleVariableDeclaration",
"void", "java.lang.Object", "java.lang.Object", "java.lang.Object"))
.request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
}
@@ -611,26 +604,26 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
.request(StackRequest.THIS, StackRequest.PARAM1)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG))
.build());
sm.addScript(ScriptBuilder.replaceMethodCall()
.target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
.methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
.requestExtra(StackRequest.THIS)
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Val", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG))
.build());
sm.addScript(ScriptBuilder.replaceMethodCall()
.target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
.methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Val", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG))
.build());
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
.request(StackRequest.THIS, StackRequest.PARAM1)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG))
.build());
}
@@ -641,7 +634,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(SOURCE_TYPE_CONVERTER_SIG, "convertAnnotations", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "convertAnnotations", ANNOTATION_SIG + "[]", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "convertAnnotations", ANNOTATION_SIG + "[]", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG))
.request(StackRequest.PARAM1, StackRequest.RETURN_VALUE).build());
}
@@ -659,7 +652,7 @@ public class EclipsePatcher extends Agent {
}
private static void patchExtensionMethod(ScriptManager sm, boolean ecj) {
- final String PATCH_EXTENSIONMETHOD = "lombok.eclipse.agent.PatchExtensionMethod";
+ final String PATCH_EXTENSIONMETHOD = "lombok.launch.PatchFixesHider$ExtensionMethod";
final String PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL = "lombok.eclipse.agent.PatchExtensionMethodCompletionProposalPortal";
final String MESSAGE_SEND_SIG = "org.eclipse.jdt.internal.compiler.ast.MessageSend";
final String TYPE_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
index 8eec27fb..ca0933fb 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
@@ -224,10 +224,14 @@ public class PatchExtensionMethod {
if (methodCall.arguments != null) arguments.addAll(Arrays.asList(methodCall.arguments));
List<TypeBinding> argumentTypes = new ArrayList<TypeBinding>();
for (Expression argument : arguments) {
- argumentTypes.add(argument.resolvedType);
+ if (argument.resolvedType != null) argumentTypes.add(argument.resolvedType);
+ // TODO: Instead of just skipping nulls entirely, there is probably a 'unresolved type' placeholder. THAT is what we ought to be adding here!
}
+ Expression[] originalArgs = methodCall.arguments;
+ methodCall.arguments = arguments.toArray(new Expression[0]);
MethodBinding fixedBinding = scope.getMethod(extensionMethod.declaringClass, methodCall.selector, argumentTypes.toArray(new TypeBinding[0]), methodCall);
if (fixedBinding instanceof ProblemMethodBinding) {
+ methodCall.arguments = originalArgs;
if (fixedBinding.declaringClass != null) {
scope.problemReporter().invalidMethod(methodCall, fixedBinding);
}
@@ -246,7 +250,6 @@ public class PatchExtensionMethod {
arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds
}
}
- methodCall.arguments = arguments.toArray(new Expression[0]);
methodCall.receiver = createNameRef(extensionMethod.declaringClass, methodCall);
methodCall.actualReceiverType = extensionMethod.declaringClass;
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
deleted file mode 100644
index d1c668a0..00000000
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2010-2013 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.agent;
-
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Stack;
-
-import lombok.core.DiagnosticsReceiver;
-import lombok.core.PostCompiler;
-import lombok.core.Version;
-import lombok.eclipse.EclipseAugments;
-
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.jdt.core.IAnnotatable;
-import org.eclipse.jdt.core.IAnnotation;
-import org.eclipse.jdt.core.IMethod;
-import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.core.dom.MethodDeclaration;
-import org.eclipse.jdt.core.dom.SimpleName;
-import org.eclipse.jdt.internal.compiler.ast.Annotation;
-import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent;
-import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent;
-import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner;
-import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
-
-public class PatchFixes {
- public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
- if ("aboutText".equals(key)) {
- return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. http://projectlombok.org/";
- }
-
- return origReturnValue;
- }
-
- public static boolean isGenerated(org.eclipse.jdt.core.dom.ASTNode node) {
- boolean result = false;
- try {
- result = ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue();
- if (!result && node.getParent() != null && node.getParent() instanceof org.eclipse.jdt.core.dom.QualifiedName)
- result = isGenerated(node.getParent());
- } catch (Exception e) {
- // better to assume it isn't generated
- }
- return result;
- }
-
- public static boolean isListRewriteOnGeneratedNode(org.eclipse.jdt.core.dom.rewrite.ListRewrite rewrite) {
- return isGenerated(rewrite.getParent());
- }
-
- public static boolean returnFalse(java.lang.Object object) {
- return false;
- }
-
- public static boolean returnTrue(java.lang.Object object) {
- return true;
- }
-
- @java.lang.SuppressWarnings({"unchecked", "rawtypes"}) public static java.util.List removeGeneratedNodes(java.util.List list) {
- try {
- java.util.List realNodes = new java.util.ArrayList(list.size());
- for (java.lang.Object node : list) {
- if(!isGenerated(((org.eclipse.jdt.core.dom.ASTNode)node))) {
- realNodes.add(node);
- }
- }
- return realNodes;
- } catch (Exception e) {
- }
- return list;
- }
-
- public static java.lang.String getRealMethodDeclarationSource(java.lang.String original, Object processor, org.eclipse.jdt.core.dom.MethodDeclaration declaration) throws Exception {
- if (!isGenerated(declaration)) return original;
-
- List<org.eclipse.jdt.core.dom.Annotation> annotations = new ArrayList<org.eclipse.jdt.core.dom.Annotation>();
- for (Object modifier : declaration.modifiers()) {
- if (modifier instanceof org.eclipse.jdt.core.dom.Annotation) {
- org.eclipse.jdt.core.dom.Annotation annotation = (org.eclipse.jdt.core.dom.Annotation)modifier;
- String qualifiedAnnotationName = annotation.resolveTypeBinding().getQualifiedName();
- if (!"java.lang.Override".equals(qualifiedAnnotationName) && !"java.lang.SuppressWarnings".equals(qualifiedAnnotationName)) annotations.add(annotation);
- }
- }
-
- StringBuilder signature = new StringBuilder();
- addAnnotations(annotations, signature);
-
- if ((Boolean)processor.getClass().getDeclaredField("fPublic").get(processor)) signature.append("public ");
- if ((Boolean)processor.getClass().getDeclaredField("fAbstract").get(processor)) signature.append("abstract ");
-
- signature
- .append(declaration.getReturnType2().toString())
- .append(" ").append(declaration.getName().getFullyQualifiedName())
- .append("(");
-
- boolean first = true;
- for (Object parameter : declaration.parameters()) {
- if (!first) signature.append(", ");
- first = false;
- // We should also add the annotations of the parameters
- signature.append(parameter);
- }
-
- signature.append(");");
- return signature.toString();
- }
-
- // part of getRealMethodDeclarationSource(...)
- public static void addAnnotations(List<org.eclipse.jdt.core.dom.Annotation> annotations, StringBuilder signature) {
- /*
- * We SHOULD be able to handle the following cases:
- * @Override
- * @Override()
- * @SuppressWarnings("all")
- * @SuppressWarnings({"all", "unused"})
- * @SuppressWarnings(value = "all")
- * @SuppressWarnings(value = {"all", "unused"})
- * @EqualsAndHashCode(callSuper=true, of="id")
- *
- * Currently, we only seem to correctly support:
- * @Override
- * @Override() N.B. We lose the parentheses here, since there are no values. No big deal.
- * @SuppressWarnings("all")
- */
- for (org.eclipse.jdt.core.dom.Annotation annotation : annotations) {
- List<String> values = new ArrayList<String>();
- if (annotation.isSingleMemberAnnotation()) {
- org.eclipse.jdt.core.dom.SingleMemberAnnotation smAnn = (org.eclipse.jdt.core.dom.SingleMemberAnnotation) annotation;
- values.add(smAnn.getValue().toString());
- } else if (annotation.isNormalAnnotation()) {
- org.eclipse.jdt.core.dom.NormalAnnotation normalAnn = (org.eclipse.jdt.core.dom.NormalAnnotation) annotation;
- for (Object value : normalAnn.values()) values.add(value.toString());
- }
-
- signature.append("@").append(annotation.resolveTypeBinding().getQualifiedName());
- if (!values.isEmpty()) {
- signature.append("(");
- boolean first = true;
- for (String string : values) {
- if (!first) signature.append(", ");
- first = false;
- signature.append('"').append(string).append('"');
- }
- signature.append(")");
- }
- signature.append(" ");
- }
- }
-
- public static org.eclipse.jdt.core.dom.MethodDeclaration getRealMethodDeclarationNode(org.eclipse.jdt.core.IMethod sourceMethod, org.eclipse.jdt.core.dom.CompilationUnit cuUnit) throws JavaModelException {
- MethodDeclaration methodDeclarationNode = ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, cuUnit);
- if (isGenerated(methodDeclarationNode)) {
- IType declaringType = sourceMethod.getDeclaringType();
- Stack<IType> typeStack = new Stack<IType>();
- while (declaringType != null) {
- typeStack.push(declaringType);
- declaringType = declaringType.getDeclaringType();
- }
-
- IType rootType = typeStack.pop();
- org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = findTypeDeclaration(rootType, cuUnit.types());
- while (!typeStack.isEmpty() && typeDeclaration != null) {
- typeDeclaration = findTypeDeclaration(typeStack.pop(), typeDeclaration.bodyDeclarations());
- }
-
- if (typeStack.isEmpty() && typeDeclaration != null) {
- String methodName = sourceMethod.getElementName();
- for (Object declaration : typeDeclaration.bodyDeclarations()) {
- if (declaration instanceof org.eclipse.jdt.core.dom.MethodDeclaration) {
- org.eclipse.jdt.core.dom.MethodDeclaration methodDeclaration = (org.eclipse.jdt.core.dom.MethodDeclaration) declaration;
- if (methodDeclaration.getName().toString().equals(methodName)) {
- return methodDeclaration;
- }
- }
- }
- }
- }
- return methodDeclarationNode;
- }
-
- // part of getRealMethodDeclarationNode
- public static org.eclipse.jdt.core.dom.AbstractTypeDeclaration findTypeDeclaration(IType searchType, List<?> nodes) {
- for (Object object : nodes) {
- if (object instanceof org.eclipse.jdt.core.dom.AbstractTypeDeclaration) {
- org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = (org.eclipse.jdt.core.dom.AbstractTypeDeclaration) object;
- if (typeDeclaration.getName().toString().equals(searchType.getElementName()))
- return typeDeclaration;
- }
- }
- return null;
- }
-
- public static int getSourceEndFixed(int sourceEnd, org.eclipse.jdt.internal.compiler.ast.ASTNode node) throws Exception {
- if (sourceEnd == -1) {
- org.eclipse.jdt.internal.compiler.ast.ASTNode object = (org.eclipse.jdt.internal.compiler.ast.ASTNode)node.getClass().getField("$generatedBy").get(node);
- if (object != null) {
- return object.sourceEnd;
- }
- }
- return sourceEnd;
- }
-
- public static int fixRetrieveStartingCatchPosition(int original, int start) {
- return original == -1 ? start : original;
- }
-
- public static int fixRetrieveIdentifierEndPosition(int original, int end) {
- return original == -1 ? end : original;
- }
-
- public static int fixRetrieveEllipsisStartPosition(int original, int end) {
- return original == -1 ? end : original;
- }
-
- public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) {
-// return original;
- return original == -1 ? end : original; // Need to fix: see issue 325.
- }
-
- public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24
-
- public static boolean checkBit24(Object node) throws Exception {
- int bits = (Integer)(node.getClass().getField("bits").get(node));
- return (bits & ALREADY_PROCESSED_FLAG) != 0;
- }
-
- public static boolean skipRewritingGeneratedNodes(org.eclipse.jdt.core.dom.ASTNode node) throws Exception {
- return ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue();
- }
-
- public static void setIsGeneratedFlag(org.eclipse.jdt.core.dom.ASTNode domNode,
- org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode) throws Exception {
- if (internalNode == null || domNode == null) return;
- boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(internalNode) != null;
- if (isGenerated) domNode.getClass().getField("$isGenerated").set(domNode, true);
- }
-
- public static void setIsGeneratedFlagForName(org.eclipse.jdt.core.dom.Name name, Object internalNode) throws Exception {
- if (internalNode instanceof org.eclipse.jdt.internal.compiler.ast.ASTNode) {
- if (EclipseAugments.ASTNode_generatedBy.get((org.eclipse.jdt.internal.compiler.ast.ASTNode) internalNode) != null) {
- name.getClass().getField("$isGenerated").set(name, true);
- }
- }
- }
-
- public static RewriteEvent[] listRewriteHandleGeneratedMethods(RewriteEvent parent) {
- RewriteEvent[] children = parent.getChildren();
- List<RewriteEvent> newChildren = new ArrayList<RewriteEvent>();
- List<RewriteEvent> modifiedChildren = new ArrayList<RewriteEvent>();
- for (int i=0; i<children.length; i++) {
- RewriteEvent child = children[i];
- boolean isGenerated = isGenerated( (org.eclipse.jdt.core.dom.ASTNode)child.getOriginalValue() );
- if (isGenerated) {
- if ((child.getChangeKind() == RewriteEvent.REPLACED || child.getChangeKind() == RewriteEvent.REMOVED)
- && child.getOriginalValue() instanceof org.eclipse.jdt.core.dom.MethodDeclaration
- && child.getNewValue() != null
- ) {
- modifiedChildren.add(new NodeRewriteEvent(null, child.getNewValue()));
- }
- } else {
- newChildren.add(child);
- }
- }
- // Since Eclipse doesn't honor the "insert at specified location" for already existing members,
- // we'll just add them last
- newChildren.addAll(modifiedChildren);
- return newChildren.toArray(new RewriteEvent[newChildren.size()]);
- }
-
- public static int getTokenEndOffsetFixed(TokenScanner scanner, int token, int startOffset, Object domNode) throws CoreException {
- boolean isGenerated = false;
- try {
- isGenerated = (Boolean) domNode.getClass().getField("$isGenerated").get(domNode);
- } catch (Exception e) {
- // If this fails, better to break some refactor scripts than to crash eclipse.
- }
- if (isGenerated) return -1;
- return scanner.getTokenEndOffset(token, startOffset);
- }
-
- public static IMethod[] removeGeneratedMethods(IMethod[] methods) throws Exception {
- List<IMethod> result = new ArrayList<IMethod>();
- for (IMethod m : methods) {
- if (m.getNameRange().getLength() > 0 && !m.getNameRange().equals(m.getSourceRange())) result.add(m);
- }
- return result.size() == methods.length ? methods : result.toArray(new IMethod[result.size()]);
- }
-
- public static SimpleName[] removeGeneratedSimpleNames(SimpleName[] in) throws Exception {
- Field f = SimpleName.class.getField("$isGenerated");
-
- int count = 0;
- for (int i = 0; i < in.length; i++) {
- if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) count++;
- }
- if (count == in.length) return in;
- SimpleName[] newSimpleNames = new SimpleName[count];
- count = 0;
- for (int i = 0; i < in.length; i++) {
- if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) newSimpleNames[count++] = in[i];
- }
- return newSimpleNames;
- }
-
- public static byte[] runPostCompiler(byte[] bytes, String fileName) {
- byte[] transformed = PostCompiler.applyTransformations(bytes, fileName, DiagnosticsReceiver.CONSOLE);
- return transformed == null ? bytes : transformed;
- }
-
- public static OutputStream runPostCompiler(OutputStream out) throws IOException {
- return PostCompiler.wrapOutputStream(out, "TEST", DiagnosticsReceiver.CONSOLE);
- }
-
- public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
- String fileName = path + "/" + name;
- return new BufferedOutputStream(PostCompiler.wrapOutputStream(out, fileName, DiagnosticsReceiver.CONSOLE));
- }
-
- public static Annotation[] convertAnnotations(Annotation[] out, IAnnotatable annotatable) {
- IAnnotation[] in;
-
- try {
- in = annotatable.getAnnotations();
- } catch (Exception e) {
- return out;
- }
-
- if (out == null) return null;
- int toWrite = 0;
-
- for (int idx = 0; idx < out.length; idx++) {
- String oName = new String(out[idx].type.getLastToken());
- boolean found = false;
- for (IAnnotation i : in) {
- String name = i.getElementName();
- int li = name.lastIndexOf('.');
- if (li > -1) name = name.substring(li + 1);
- if (name.equals(oName)) {
- found = true;
- break;
- }
- }
- if (!found) out[idx] = null;
- else toWrite++;
- }
-
- Annotation[] replace = out;
- if (toWrite < out.length) {
- replace = new Annotation[toWrite];
- int idx = 0;
- for (int i = 0; i < out.length; i++) {
- if (out[i] == null) continue;
- replace[idx++] = out[i];
- }
- }
-
- return replace;
- }
-}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
new file mode 100644
index 00000000..6685b6bb
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
@@ -0,0 +1,53 @@
+/*
+ * 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.agent;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import lombok.core.DiagnosticsReceiver;
+import lombok.core.PostCompiler;
+import lombok.core.Version;
+
+public class PatchFixesShadowLoaded {
+ public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
+ if ("aboutText".equals(key)) {
+ return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. http://projectlombok.org/";
+ }
+ return origReturnValue;
+ }
+
+ public static byte[] runPostCompiler(byte[] bytes, String fileName) {
+ byte[] transformed = PostCompiler.applyTransformations(bytes, fileName, DiagnosticsReceiver.CONSOLE);
+ return transformed == null ? bytes : transformed;
+ }
+
+ public static OutputStream runPostCompiler(OutputStream out) throws IOException {
+ return PostCompiler.wrapOutputStream(out, "TEST", DiagnosticsReceiver.CONSOLE);
+ }
+
+ public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
+ String fileName = path + "/" + name;
+ return new BufferedOutputStream(PostCompiler.wrapOutputStream(out, fileName, DiagnosticsReceiver.CONSOLE));
+ }
+}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
index e734dceb..30574ea6 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
@@ -128,7 +128,14 @@ public class PatchVal {
if (!isVal(local.type, scope)) return false;
- if (new Throwable().getStackTrace()[2].getClassName().contains("ForStatement")) return false;
+ StackTraceElement[] st = new Throwable().getStackTrace();
+ for (int i = 0; i < st.length - 2 && i < 10; i++) {
+ if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) {
+ if (st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") &&
+ st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement")) return false;
+ break;
+ }
+ }
Expression init = local.initialization;
if (init == null && Reflection.initCopyField != null) {
diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
new file mode 100644
index 00000000..2472ca3c
--- /dev/null
+++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2010-2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * 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.launch;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import lombok.eclipse.EclipseAugments;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IAnnotatable;
+import org.eclipse.jdt.core.IAnnotation;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+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.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
+import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
+import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent;
+import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent;
+import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
+
+/** These contain a mix of the following:
+ * <ul>
+ * <li> 'dependency free' method wrappers that cross the shadowloader barrier.
+ * <li> methods that directly patch, <em>but</em>, these should ALWAYS be transplanted.
+ * </ul>
+ *
+ * <strong>This class lives on the outside of the shadowloader barrier, and as a consequence, cannot access any other lombok code except other
+ * code in the {@code lombok.launch} package!</strong>.
+ * <p>
+ * This class is package private with lots of public inner static classes. This hides all of them from IDE autocomplete dialogs and such but at the JVM
+ * level the inner static class are just plain public, which is important, because calls to the contents of these inner static classes are injected into
+ * various eclipse classes verbatim, and if they weren't public, the verifier wouldn't accept it.
+ */
+final class PatchFixesHider {
+
+ /** These utility methods are only used 'internally', but because of transplant methods, the class (and its methods) still have to be public! */
+ public static final class Util {
+ private static ClassLoader shadowLoader;
+
+ public static Class<?> shadowLoadClass(String name) {
+ try {
+ if (shadowLoader == null) {
+ try {
+ Class.forName("lombok.core.LombokNode");
+ // If we get here, then lombok is already available.
+ shadowLoader = Util.class.getClassLoader();
+ } catch (ClassNotFoundException e) {
+ // If we get here, it isn't, and we should use the shadowloader.
+ shadowLoader = Main.createShadowClassLoader();
+ }
+ }
+
+ return Class.forName(name, true, shadowLoader);
+ } catch (ClassNotFoundException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ public static Method findMethod(Class<?> type, String name, Class<?>... parameterTypes) {
+ try {
+ return type.getDeclaredMethod(name, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ public static Object invokeMethod(Method method, Object... args) {
+ try {
+ return method.invoke(null, args);
+ } catch (IllegalAccessException e) {
+ throw sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw sneakyThrow(e.getCause());
+ }
+ }
+
+ private static RuntimeException sneakyThrow(Throwable t) {
+ if (t == null) throw new NullPointerException("t");
+ Util.<RuntimeException>sneakyThrow0(t);
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
+ throw (T)t;
+ }
+ }
+
+ /** Contains patch fixes that are dependent on lombok internals. */
+ public static final class LombokDeps {
+ public static final Method ADD_LOMBOK_NOTES;
+ public static final Method POST_COMPILER_BYTES_STRING;
+ public static final Method POST_COMPILER_OUTPUTSTREAM;
+ public static final Method POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchFixesShadowLoaded");
+ ADD_LOMBOK_NOTES = Util.findMethod(shadowed, "addLombokNotesToEclipseAboutDialog", String.class, String.class);
+ POST_COMPILER_BYTES_STRING = Util.findMethod(shadowed, "runPostCompiler", byte[].class, String.class);
+ POST_COMPILER_OUTPUTSTREAM = Util.findMethod(shadowed, "runPostCompiler", OutputStream.class);
+ POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING = Util.findMethod(shadowed, "runPostCompiler", BufferedOutputStream.class, String.class, String.class);
+ }
+
+ public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
+ return (String) Util.invokeMethod(LombokDeps.ADD_LOMBOK_NOTES, origReturnValue, key);
+ }
+
+ public static byte[] runPostCompiler(byte[] bytes, String fileName) {
+ return (byte[]) Util.invokeMethod(LombokDeps.POST_COMPILER_BYTES_STRING, bytes, fileName);
+ }
+
+ public static OutputStream runPostCompiler(OutputStream out) throws IOException {
+ return (OutputStream) Util.invokeMethod(LombokDeps.POST_COMPILER_OUTPUTSTREAM, out);
+ }
+
+ public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
+ return (BufferedOutputStream) Util.invokeMethod(LombokDeps.POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING, out, path, name);
+ }
+ }
+
+ public static final class Transform {
+ private static final Method TRANSFORM;
+ private static final Method TRANSFORM_SWAPPED;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.TransformEclipseAST");
+ TRANSFORM = Util.findMethod(shadowed, "transform", Parser.class, CompilationUnitDeclaration.class);
+ TRANSFORM_SWAPPED = Util.findMethod(shadowed, "transform_swapped", CompilationUnitDeclaration.class, Parser.class);
+ }
+
+ public static void transform(Parser parser, CompilationUnitDeclaration ast) throws IOException {
+ Util.invokeMethod(TRANSFORM, parser, ast);
+ }
+
+ public static void transform_swapped(CompilationUnitDeclaration ast, Parser parser) throws IOException {
+ Util.invokeMethod(TRANSFORM_SWAPPED, ast, parser);
+ }
+ }
+
+ /** Contains patch code to support {@code @Delegate} */
+ public static final class Delegate {
+ private static final Method HANDLE_DELEGATE_FOR_TYPE;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchDelegatePortal");
+ HANDLE_DELEGATE_FOR_TYPE = Util.findMethod(shadowed, "handleDelegateForType", Object.class);
+ }
+
+ public static boolean handleDelegateForType(Object classScope) {
+ return (Boolean) Util.invokeMethod(HANDLE_DELEGATE_FOR_TYPE, classScope);
+ }
+ }
+
+ /** Contains patch code to support {@code val} (eclipse specific) */
+ public static final class ValPortal {
+ private static final Method COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE;
+ private static final Method COPY_INITIALIZATION_OF_LOCAL_DECLARATION;
+ private static final Method ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT;
+ private static final Method ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchValEclipsePortal");
+ COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE = Util.findMethod(shadowed, "copyInitializationOfForEachIterable", Object.class);
+ COPY_INITIALIZATION_OF_LOCAL_DECLARATION = Util.findMethod(shadowed, "copyInitializationOfLocalDeclaration", Object.class);
+ ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT = Util.findMethod(shadowed, "addFinalAndValAnnotationToVariableDeclarationStatement", Object.class, Object.class, Object.class);
+ ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION = Util.findMethod(shadowed, "addFinalAndValAnnotationToSingleVariableDeclaration", Object.class, Object.class, Object.class);
+ }
+
+ public static void copyInitializationOfForEachIterable(Object parser) {
+ Util.invokeMethod(COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE, parser);
+ }
+
+ public static void copyInitializationOfLocalDeclaration(Object parser) {
+ Util.invokeMethod(COPY_INITIALIZATION_OF_LOCAL_DECLARATION, parser);
+ }
+
+ public static void addFinalAndValAnnotationToVariableDeclarationStatement(Object converter, Object out, Object in) {
+ Util.invokeMethod(ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT, converter, out, in);
+ }
+
+ public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, Object out, Object in) {
+ Util.invokeMethod(ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION, converter, out, in);
+ }
+ }
+
+ /** Contains patch code to support {@code val} (eclipse and ecj) */
+ public static final class Val {
+ private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED;
+ private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2;
+ private static final Method HANDLE_VAL_FOR_LOCAL_DECLARATION;
+ private static final Method HANDLE_VAL_FOR_FOR_EACH;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchVal");
+ SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled", Expression.class, BlockScope.class);
+ SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2 = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled2", Expression.class, BlockScope.class, LocalDeclaration.class);
+ HANDLE_VAL_FOR_LOCAL_DECLARATION = Util.findMethod(shadowed, "handleValForLocalDeclaration", LocalDeclaration.class, BlockScope.class);
+ HANDLE_VAL_FOR_FOR_EACH = Util.findMethod(shadowed, "handleValForForEach", ForeachStatement.class, BlockScope.class);
+ }
+
+ public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) {
+ return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED, expr, scope);
+ }
+
+ public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) {
+ return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2, expr, scope, decl);
+ }
+
+ public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) {
+ return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_LOCAL_DECLARATION, local, scope);
+ }
+
+ public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) {
+ return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_FOR_EACH, forEach, scope);
+ }
+ }
+
+ /** Contains patch code to support {@code @ExtensionMethod} */
+ public static final class ExtensionMethod {
+ private static final Method RESOLVE_TYPE;
+ private static final Method ERROR_NO_METHOD_FOR;
+ private static final Method INVALID_METHOD;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchExtensionMethod");
+ RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", TypeBinding.class, MessageSend.class, BlockScope.class);
+ ERROR_NO_METHOD_FOR = Util.findMethod(shadowed, "errorNoMethodFor", ProblemReporter.class, MessageSend.class, TypeBinding.class, TypeBinding[].class);
+ INVALID_METHOD = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class);
+ }
+
+ public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) {
+ return (TypeBinding) Util.invokeMethod(RESOLVE_TYPE, resolvedType, methodCall, scope);
+ }
+
+ public static void errorNoMethodFor(ProblemReporter problemReporter, MessageSend messageSend, TypeBinding recType, TypeBinding[] params) {
+ Util.invokeMethod(ERROR_NO_METHOD_FOR, problemReporter, messageSend, recType, params);
+ }
+
+ public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
+ Util.invokeMethod(INVALID_METHOD, problemReporter, messageSend, method);
+ }
+ }
+
+ /**
+ * Contains a mix of methods: ecj only, ecj+eclipse, and eclipse only. As a consequence, _EVERY_ method from here used for ecj MUST be
+ * transplanted, as ecj itself cannot load this class (signatures refer to things that don't exist in ecj-only mode).
+ * <p>
+ * Because of usage of transplant(), a bunch of these contain direct code and don't try to cross the shadowloader barrier.
+ */
+ public static final class PatchFixes {
+ public static boolean isGenerated(org.eclipse.jdt.core.dom.ASTNode node) {
+ boolean result = false;
+ try {
+ result = ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue();
+ if (!result && node.getParent() != null && node.getParent() instanceof org.eclipse.jdt.core.dom.QualifiedName) {
+ result = isGenerated(node.getParent());
+ }
+ } catch (Exception e) {
+ // better to assume it isn't generated
+ }
+ return result;
+ }
+
+ public static boolean isListRewriteOnGeneratedNode(org.eclipse.jdt.core.dom.rewrite.ListRewrite rewrite) {
+ return isGenerated(rewrite.getParent());
+ }
+
+ public static boolean returnFalse(java.lang.Object object) {
+ return false;
+ }
+
+ public static boolean returnTrue(java.lang.Object object) {
+ return true;
+ }
+
+ @java.lang.SuppressWarnings({"unchecked", "rawtypes"}) public static java.util.List removeGeneratedNodes(java.util.List list) {
+ try {
+ java.util.List realNodes = new java.util.ArrayList(list.size());
+ for (java.lang.Object node : list) {
+ if(!isGenerated(((org.eclipse.jdt.core.dom.ASTNode)node))) {
+ realNodes.add(node);
+ }
+ }
+ return realNodes;
+ } catch (Exception e) {
+ }
+ return list;
+ }
+
+ public static java.lang.String getRealMethodDeclarationSource(java.lang.String original, Object processor, org.eclipse.jdt.core.dom.MethodDeclaration declaration) throws Exception {
+ if (!isGenerated(declaration)) return original;
+
+ List<org.eclipse.jdt.core.dom.Annotation> annotations = new ArrayList<org.eclipse.jdt.core.dom.Annotation>();
+ for (Object modifier : declaration.modifiers()) {
+ if (modifier instanceof org.eclipse.jdt.core.dom.Annotation) {
+ org.eclipse.jdt.core.dom.Annotation annotation = (org.eclipse.jdt.core.dom.Annotation)modifier;
+ String qualifiedAnnotationName = annotation.resolveTypeBinding().getQualifiedName();
+ if (!"java.lang.Override".equals(qualifiedAnnotationName) && !"java.lang.SuppressWarnings".equals(qualifiedAnnotationName)) annotations.add(annotation);
+ }
+ }
+
+ StringBuilder signature = new StringBuilder();
+ addAnnotations(annotations, signature);
+
+ if ((Boolean)processor.getClass().getDeclaredField("fPublic").get(processor)) signature.append("public ");
+ if ((Boolean)processor.getClass().getDeclaredField("fAbstract").get(processor)) signature.append("abstract ");
+
+ signature
+ .append(declaration.getReturnType2().toString())
+ .append(" ").append(declaration.getName().getFullyQualifiedName())
+ .append("(");
+
+ boolean first = true;
+ for (Object parameter : declaration.parameters()) {
+ if (!first) signature.append(", ");
+ first = false;
+ // We should also add the annotations of the parameters
+ signature.append(parameter);
+ }
+
+ signature.append(");");
+ return signature.toString();
+ }
+
+ // part of getRealMethodDeclarationSource(...)
+ public static void addAnnotations(List<org.eclipse.jdt.core.dom.Annotation> annotations, StringBuilder signature) {
+ /*
+ * We SHOULD be able to handle the following cases:
+ * @Override
+ * @Override()
+ * @SuppressWarnings("all")
+ * @SuppressWarnings({"all", "unused"})
+ * @SuppressWarnings(value = "all")
+ * @SuppressWarnings(value = {"all", "unused"})
+ * @EqualsAndHashCode(callSuper=true, of="id")
+ *
+ * Currently, we only seem to correctly support:
+ * @Override
+ * @Override() N.B. We lose the parentheses here, since there are no values. No big deal.
+ * @SuppressWarnings("all")
+ */
+ for (org.eclipse.jdt.core.dom.Annotation annotation : annotations) {
+ List<String> values = new ArrayList<String>();
+ if (annotation.isSingleMemberAnnotation()) {
+ org.eclipse.jdt.core.dom.SingleMemberAnnotation smAnn = (org.eclipse.jdt.core.dom.SingleMemberAnnotation) annotation;
+ values.add(smAnn.getValue().toString());
+ } else if (annotation.isNormalAnnotation()) {
+ org.eclipse.jdt.core.dom.NormalAnnotation normalAnn = (org.eclipse.jdt.core.dom.NormalAnnotation) annotation;
+ for (Object value : normalAnn.values()) values.add(value.toString());
+ }
+
+ signature.append("@").append(annotation.resolveTypeBinding().getQualifiedName());
+ if (!values.isEmpty()) {
+ signature.append("(");
+ boolean first = true;
+ for (String string : values) {
+ if (!first) signature.append(", ");
+ first = false;
+ signature.append('"').append(string).append('"');
+ }
+ signature.append(")");
+ }
+ signature.append(" ");
+ }
+ }
+
+ public static org.eclipse.jdt.core.dom.MethodDeclaration getRealMethodDeclarationNode(org.eclipse.jdt.core.IMethod sourceMethod, org.eclipse.jdt.core.dom.CompilationUnit cuUnit) throws JavaModelException {
+ MethodDeclaration methodDeclarationNode = ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, cuUnit);
+ if (isGenerated(methodDeclarationNode)) {
+ IType declaringType = sourceMethod.getDeclaringType();
+ Stack<IType> typeStack = new Stack<IType>();
+ while (declaringType != null) {
+ typeStack.push(declaringType);
+ declaringType = declaringType.getDeclaringType();
+ }
+
+ IType rootType = typeStack.pop();
+ org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = findTypeDeclaration(rootType, cuUnit.types());
+ while (!typeStack.isEmpty() && typeDeclaration != null) {
+ typeDeclaration = findTypeDeclaration(typeStack.pop(), typeDeclaration.bodyDeclarations());
+ }
+
+ if (typeStack.isEmpty() && typeDeclaration != null) {
+ String methodName = sourceMethod.getElementName();
+ for (Object declaration : typeDeclaration.bodyDeclarations()) {
+ if (declaration instanceof org.eclipse.jdt.core.dom.MethodDeclaration) {
+ org.eclipse.jdt.core.dom.MethodDeclaration methodDeclaration = (org.eclipse.jdt.core.dom.MethodDeclaration) declaration;
+ if (methodDeclaration.getName().toString().equals(methodName)) {
+ return methodDeclaration;
+ }
+ }
+ }
+ }
+ }
+ return methodDeclarationNode;
+ }
+
+ // part of getRealMethodDeclarationNode
+ public static org.eclipse.jdt.core.dom.AbstractTypeDeclaration findTypeDeclaration(IType searchType, List<?> nodes) {
+ for (Object object : nodes) {
+ if (object instanceof org.eclipse.jdt.core.dom.AbstractTypeDeclaration) {
+ org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = (org.eclipse.jdt.core.dom.AbstractTypeDeclaration) object;
+ if (typeDeclaration.getName().toString().equals(searchType.getElementName()))
+ return typeDeclaration;
+ }
+ }
+ return null;
+ }
+
+ public static int getSourceEndFixed(int sourceEnd, org.eclipse.jdt.internal.compiler.ast.ASTNode node) throws Exception {
+ if (sourceEnd == -1) {
+ org.eclipse.jdt.internal.compiler.ast.ASTNode object = (org.eclipse.jdt.internal.compiler.ast.ASTNode)node.getClass().getField("$generatedBy").get(node);
+ if (object != null) {
+ return object.sourceEnd;
+ }
+ }
+ return sourceEnd;
+ }
+
+ public static int fixRetrieveStartingCatchPosition(int original, int start) {
+ return original == -1 ? start : original;
+ }
+
+ public static int fixRetrieveIdentifierEndPosition(int original, int end) {
+ return original == -1 ? end : original;
+ }
+
+ public static int fixRetrieveEllipsisStartPosition(int original, int end) {
+ return original == -1 ? end : original;
+ }
+
+ public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) {
+ return original == -1 ? end : original; // Need to fix: see issue 325.
+ }
+
+ public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24
+
+ public static boolean checkBit24(Object node) throws Exception {
+ int bits = (Integer)(node.getClass().getField("bits").get(node));
+ return (bits & ALREADY_PROCESSED_FLAG) != 0;
+ }
+
+ public static boolean skipRewritingGeneratedNodes(org.eclipse.jdt.core.dom.ASTNode node) throws Exception {
+ return ((Boolean) node.getClass().getField("$isGenerated").get(node)).booleanValue();
+ }
+
+ public static void setIsGeneratedFlag(org.eclipse.jdt.core.dom.ASTNode domNode,
+ org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode) throws Exception {
+
+ if (internalNode == null || domNode == null) return;
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(internalNode) != null;
+ if (isGenerated) domNode.getClass().getField("$isGenerated").set(domNode, true);
+ }
+
+ public static void setIsGeneratedFlagForName(org.eclipse.jdt.core.dom.Name name, Object internalNode) throws Exception {
+ if (internalNode instanceof org.eclipse.jdt.internal.compiler.ast.ASTNode) {
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get((org.eclipse.jdt.internal.compiler.ast.ASTNode) internalNode) != null;
+ if (isGenerated) name.getClass().getField("$isGenerated").set(name, true);
+ }
+ }
+
+ public static RewriteEvent[] listRewriteHandleGeneratedMethods(RewriteEvent parent) {
+ RewriteEvent[] children = parent.getChildren();
+ List<RewriteEvent> newChildren = new ArrayList<RewriteEvent>();
+ List<RewriteEvent> modifiedChildren = new ArrayList<RewriteEvent>();
+ for (int i = 0; i < children.length; i++) {
+ RewriteEvent child = children[i];
+ boolean isGenerated = isGenerated((org.eclipse.jdt.core.dom.ASTNode) child.getOriginalValue());
+ if (isGenerated) {
+ boolean isReplacedOrRemoved = child.getChangeKind() == RewriteEvent.REPLACED || child.getChangeKind() == RewriteEvent.REMOVED;
+ boolean convertingFromMethod = child.getOriginalValue() instanceof org.eclipse.jdt.core.dom.MethodDeclaration;
+ if (isReplacedOrRemoved && convertingFromMethod && child.getNewValue() != null) {
+ modifiedChildren.add(new NodeRewriteEvent(null, child.getNewValue()));
+ }
+ } else {
+ newChildren.add(child);
+ }
+ }
+ // Since Eclipse doesn't honor the "insert at specified location" for already existing members,
+ // we'll just add them last
+ newChildren.addAll(modifiedChildren);
+ return newChildren.toArray(new RewriteEvent[newChildren.size()]);
+ }
+
+ public static int getTokenEndOffsetFixed(TokenScanner scanner, int token, int startOffset, Object domNode) throws CoreException {
+ boolean isGenerated = false;
+ try {
+ isGenerated = (Boolean) domNode.getClass().getField("$isGenerated").get(domNode);
+ } catch (Exception e) {
+ // If this fails, better to break some refactor scripts than to crash eclipse.
+ }
+ if (isGenerated) return -1;
+ return scanner.getTokenEndOffset(token, startOffset);
+ }
+
+ public static IMethod[] removeGeneratedMethods(IMethod[] methods) throws Exception {
+ List<IMethod> result = new ArrayList<IMethod>();
+ for (IMethod m : methods) {
+ if (m.getNameRange().getLength() > 0 && !m.getNameRange().equals(m.getSourceRange())) result.add(m);
+ }
+ return result.size() == methods.length ? methods : result.toArray(new IMethod[result.size()]);
+ }
+
+ public static SimpleName[] removeGeneratedSimpleNames(SimpleName[] in) throws Exception {
+ Field f = SimpleName.class.getField("$isGenerated");
+
+ int count = 0;
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) count++;
+ }
+ if (count == in.length) return in;
+ SimpleName[] newSimpleNames = new SimpleName[count];
+ count = 0;
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) newSimpleNames[count++] = in[i];
+ }
+ return newSimpleNames;
+ }
+
+ public static Annotation[] convertAnnotations(Annotation[] out, IAnnotatable annotatable) {
+ IAnnotation[] in;
+
+ try {
+ in = annotatable.getAnnotations();
+ } catch (Exception e) {
+ return out;
+ }
+
+ if (out == null) return null;
+ int toWrite = 0;
+
+ for (int idx = 0; idx < out.length; idx++) {
+ String oName = new String(out[idx].type.getLastToken());
+ boolean found = false;
+ for (IAnnotation i : in) {
+ String name = i.getElementName();
+ int li = name.lastIndexOf('.');
+ if (li > -1) name = name.substring(li + 1);
+ if (name.equals(oName)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) out[idx] = null;
+ else toWrite++;
+ }
+
+ Annotation[] replace = out;
+ if (toWrite < out.length) {
+ replace = new Annotation[toWrite];
+ int idx = 0;
+ for (int i = 0; i < out.length; i++) {
+ if (out[i] == null) continue;
+ replace[idx++] = out[i];
+ }
+ }
+
+ return replace;
+ }
+ }
+}
diff --git a/src/installer/lombok/installer/InstallerGUI.java b/src/installer/lombok/installer/InstallerGUI.java
index c33aed74..6b8a58ab 100644
--- a/src/installer/lombok/installer/InstallerGUI.java
+++ b/src/installer/lombok/installer/InstallerGUI.java
@@ -821,9 +821,8 @@ public class InstallerGUI {
private static final String HOW_I_WORK_EXPLANATION =
"<html><h2>Eclipse</h2><ol>" +
"<li>First, I copy myself (lombok.jar) to your Eclipse install directory.</li>" +
- "<li>Then, I edit the <i>eclipse.ini</i> file to add the following two entries:<br>" +
- "<pre>-Xbootclasspath/a:lombok.jar<br>" +
- "-javaagent:lombok.jar</pre></li></ol>" +
+ "<li>Then, I edit the <i>eclipse.ini</i> file to add the following entry:<br>" +
+ "<pre>-javaagent:lombok.jar</pre></li></ol>" +
"On Mac OS X, eclipse.ini is hidden in<br>" +
"<code>Eclipse.app/Contents/MacOS</code> so that's where I place the jar files.</html>";
diff --git a/src/installer/lombok/installer/eclipse/EclipseLocation.java b/src/installer/lombok/installer/eclipse/EclipseLocation.java
index e347cd98..0fe60c05 100644
--- a/src/installer/lombok/installer/eclipse/EclipseLocation.java
+++ b/src/installer/lombok/installer/eclipse/EclipseLocation.java
@@ -337,8 +337,6 @@ public class EclipseLocation extends IdeLocation {
newContents.append(String.format(
"-javaagent:%s", escapePath(fullPathToLombok + "lombok.jar"))).append(OS_NEWLINE);
- newContents.append(String.format(
- "-Xbootclasspath/a:%s", escapePath(fullPathToLombok + "lombok.jar"))).append(OS_NEWLINE);
FileOutputStream fos = new FileOutputStream(eclipseIniPath);
try {
@@ -360,8 +358,7 @@ public class EclipseLocation extends IdeLocation {
}
return "If you start " + getTypeName() + " with a custom -vm parameter, you'll need to add:<br>" +
- "<code>-vmargs -Xbootclasspath/a:lombok.jar -javaagent:lombok.jar</code><br>" +
- "as parameter as well.";
+ "<code>-vmargs -javaagent:lombok.jar</code><br>as parameter as well.";
}
@Override public URL getIdeIcon() {
diff --git a/src/launch/lombok/launch/Agent.java b/src/launch/lombok/launch/Agent.java
new file mode 100644
index 00000000..7989e51f
--- /dev/null
+++ b/src/launch/lombok/launch/Agent.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 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.launch;
+
+import java.lang.instrument.Instrumentation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+final class Agent {
+ public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Throwable {
+ runLauncher(agentArgs, instrumentation, true);
+ }
+
+ public static void premain(String agentArgs, Instrumentation instrumentation) throws Throwable {
+ runLauncher(agentArgs, instrumentation, false);
+ }
+
+ private static void runLauncher(String agentArgs, Instrumentation instrumentation, boolean injected) throws Throwable {
+ ClassLoader cl = Main.createShadowClassLoader();
+ try {
+ Class<?> c = cl.loadClass("lombok.core.AgentLauncher");
+ Method m = c.getDeclaredMethod("runAgents", String.class, Instrumentation.class, boolean.class, Class.class);
+ m.invoke(null, agentArgs, instrumentation, injected, Agent.class);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+}
diff --git a/src/launch/lombok/launch/AnnotationProcessor.java b/src/launch/lombok/launch/AnnotationProcessor.java
new file mode 100644
index 00000000..35c26b7c
--- /dev/null
+++ b/src/launch/lombok/launch/AnnotationProcessor.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 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.launch;
+
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Completion;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+
+class AnnotationProcessorHider {
+ public static class AnnotationProcessor extends AbstractProcessor {
+ private final AbstractProcessor instance = createWrappedInstance();
+
+ @Override public Set<String> getSupportedOptions() {
+ return instance.getSupportedOptions();
+ }
+
+ @Override public Set<String> getSupportedAnnotationTypes() {
+ return instance.getSupportedAnnotationTypes();
+ }
+
+ @Override public SourceVersion getSupportedSourceVersion() {
+ return instance.getSupportedSourceVersion();
+ }
+
+ @Override public void init(ProcessingEnvironment processingEnv) {
+ instance.init(processingEnv);
+ super.init(processingEnv);
+ }
+
+ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return instance.process(annotations, roundEnv);
+ }
+
+ @Override public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
+ return instance.getCompletions(element, annotation, member, userText);
+ }
+
+ private static AbstractProcessor createWrappedInstance() {
+ ClassLoader cl = Main.createShadowClassLoader();
+ try {
+ Class<?> mc = cl.loadClass("lombok.core.AnnotationProcessor");
+ return (AbstractProcessor) mc.newInstance();
+ } catch (Throwable t) {
+ if (t instanceof Error) throw (Error) t;
+ if (t instanceof RuntimeException) throw (RuntimeException) t;
+ throw new RuntimeException(t);
+ }
+ }
+ }
+}
diff --git a/src/launch/lombok/launch/Main.java b/src/launch/lombok/launch/Main.java
new file mode 100644
index 00000000..63d97d48
--- /dev/null
+++ b/src/launch/lombok/launch/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 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.launch;
+
+import java.lang.reflect.InvocationTargetException;
+
+class Main {
+ static ClassLoader createShadowClassLoader() {
+ return new ShadowClassLoader(Main.class.getClassLoader(), "lombok");
+ }
+
+ public static void main(String[] args) throws Throwable {
+ ClassLoader cl = createShadowClassLoader();
+ Class<?> mc = cl.loadClass("lombok.core.Main");
+ try {
+ mc.getMethod("main", String[].class).invoke(null, new Object[] {args});
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+}
diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java
new file mode 100644
index 00000000..b883bd71
--- /dev/null
+++ b/src/launch/lombok/launch/ShadowClassLoader.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 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.launch;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import java.util.WeakHashMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * The shadow classloader serves to completely hide almost all classes in a given jar file by using a different file ending.
+ *
+ * The shadow classloader also serves to link in a project as it is being developed (a 'bin' dir from an IDE for example).
+ * <p>
+ * Classes loaded by the shadowloader use ".SCL.<em>sclSuffix</em>" in addition to ".class". In other words, most of the class files in a given jar end in this suffix, which
+ * serves to hide them from any tool that isn't aware of the suffix (such as IDEs generating auto-complete dialogs, and javac's classpath in general). Only shadowloader can actually
+ * load these classes.
+ * <p>
+ * The shadowloader will pick up an alternate (priority) classpath, using normal class files, from the system property "<code>shadow.override.<em>sclSuffix</em></code>".
+ * This shadow classpath looks just like a normal java classpath; the path separator is applied (semi-colon on windows, colon elsewhere), and entries can consist of directories,
+ * jar files, or directories ending in "/*" to pick up all jars inside it.
+ * <p>
+ * Load order is as follows if at least one override is present:
+ * <li>First, if the resource is found in one of the paths stated in the shadow classpath, find that.
+ * <li>Next, ask the <code>parent</code> loader, which is passed during construction of the ShadowClassLoader.
+ * <li>Notably, this jar's contents are always skipped! (The idea of the shadow classpath is that this jar only functions as a launcher, not as a source of your actual application).
+ * </ul>
+ *
+ * If no overrides are present, the load order is as follows:
+ * <li>First, if the resource is found in our own jar (trying ".SCL.<em>sclSuffix</em>" first for any resource request ending in ".class"), return that.
+ * <li>Next, ask the <code>parent</code> loader.
+ * </ul>
+ *
+ * Use ShadowClassLoader to accomplish the following things:<ul>
+ * <li>Avoid contaminating the namespace of any project using an SCL-based jar. Autocompleters in IDEs will NOT suggest anything other than actual public API.
+ * <li>Like jarjar, allows folding in dependencies such as ASM without foisting these dependencies on projects that use this jar. shadowloader obviates the need for jarjar.
+ * <li>Allows an agent (which MUST be in jar form) to still load everything except this loader infrastructure from class files generated by the IDE, which should
+ * considerably help debugging, as you can now rely on the IDE's built-in auto-recompile features instead of having to run a full build everytime, and it should help
+ * with hot code replace and the like (this is what the {@code shadow.override} feature is for).
+ * </ul>
+ *
+ * Implementation note: {@code lombok.eclipse.agent.EclipseLoaderPatcher} <em>relies</em> on this class having no dependencies on any other class except the JVM boot class, notably
+ * including any other classes in this package, <strong>including</strong> inner classes. So, don't write closures, anonymous inner class literals,
+ * enums, or anything else that could cause the compilation of this file to produce more than 1 class file. In general, actually passing load control to this loader is a bit tricky
+ * so ensure that this class has zero dependencies on anything except java core classes.
+ */
+class ShadowClassLoader extends ClassLoader {
+ private static final String SELF_NAME = "lombok/launch/ShadowClassLoader.class";
+ private final String SELF_BASE;
+ private final File SELF_BASE_FILE;
+ private final int SELF_BASE_LENGTH;
+
+ private final List<File> override = new ArrayList<File>();
+ private final String sclSuffix;
+ private final List<String> parentExclusion = new ArrayList<String>();
+
+ /**
+ * Calls the {@link ShadowClassLoader(ClassLoader, String, String, String[]) constructor with no exclusions and the source of this class as base.
+ */
+ ShadowClassLoader(ClassLoader source, String sclSuffix) {
+ this(source, sclSuffix, null);
+ }
+
+ /**
+ * @param source The 'parent' classloader.
+ * @param sclSuffix The suffix of the shadowed class files in our own jar. For example, if this is {@code lombok}, then the class files in your jar should be {@code foo/Bar.SCL.lombok} and not {@code foo/Bar.class}.
+ * @param selfBase The (preferably absolute) path to our own jar. This jar will be searched for class/SCL.sclSuffix files.
+ * @param parentExclusion For example {@code "lombok."}; upon invocation of loadClass of this loader, the parent loader ({@code source}) will NOT be invoked if the class to be loaded begins with anything in the parent exclusion list. No exclusion is applied for getResource(s).
+ */
+ ShadowClassLoader(ClassLoader source, String sclSuffix, String selfBase, String... parentExclusion) {
+ super(source);
+ this.sclSuffix = sclSuffix;
+ if (parentExclusion != null) for (String pe : parentExclusion) {
+ pe = pe.replace(".", "/");
+ if (!pe.endsWith("/")) pe = pe + "/";
+ this.parentExclusion.add(pe);
+ }
+
+ if (selfBase != null) {
+ SELF_BASE = selfBase;
+ SELF_BASE_LENGTH = selfBase.length();
+ } else {
+ String sclClassUrl = ShadowClassLoader.class.getResource("ShadowClassLoader.class").toString();
+ if (!sclClassUrl.endsWith(SELF_NAME)) throw new InternalError("ShadowLoader can't find itself.");
+ SELF_BASE_LENGTH = sclClassUrl.length() - SELF_NAME.length();
+ SELF_BASE = sclClassUrl.substring(0, SELF_BASE_LENGTH);
+ }
+
+ if (SELF_BASE.startsWith("jar:file:") && SELF_BASE.endsWith("!/")) SELF_BASE_FILE = new File(SELF_BASE.substring(9, SELF_BASE.length() - 2));
+ else if (SELF_BASE.startsWith("file:")) SELF_BASE_FILE = new File(SELF_BASE.substring(5));
+ else SELF_BASE_FILE = new File(SELF_BASE);
+ String scl = System.getProperty("shadow.override." + sclSuffix);
+ if (scl != null && !scl.isEmpty()) {
+ for (String part : scl.split("\\s*" + (File.pathSeparatorChar == ';' ? ";" : ":") + "\\s*")) {
+ if (part.endsWith("/*") || part.endsWith(File.separator + "*")) {
+ addOverrideJarDir(part.substring(0, part.length() - 2));
+ } else {
+ addOverrideClasspathEntry(part);
+ }
+ }
+ }
+ }
+
+ private static final String EMPTY_MARKER = new String("--EMPTY JAR--");
+ private Map<String, Object> jarContentsCacheTrackers = new HashMap<String, Object>();
+ private static WeakHashMap<Object, String> trackerCache = new WeakHashMap<Object, String>();
+ private static WeakHashMap<Object, List<String>> jarContentsCache = new WeakHashMap<Object, List<String>>();
+
+ /**
+ * This cache ensures that any given jar file is only opened once in order to determine the full contents of it.
+ * We use 'trackers' to make sure that the bulk of the memory taken up by this cache (the list of strings representing the content of a jar file)
+ * gets garbage collected if all ShadowClassLoaders that ever tried to request a listing of this jar file, are garbage collected.
+ */
+ private List<String> getOrMakeJarListing(String absolutePathToJar) {
+ List<String> list = retrieveFromCache(absolutePathToJar);
+ synchronized (list) {
+ if (list.isEmpty()) {
+ try {
+ JarFile jf = new JarFile(absolutePathToJar);
+ try {
+ Enumeration<JarEntry> entries = jf.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry = entries.nextElement();
+ if (!jarEntry.isDirectory()) list.add(jarEntry.getName());
+ }
+ } finally {
+ jf.close();
+ }
+ } catch (Exception ignore) {}
+ if (list.isEmpty()) list.add(EMPTY_MARKER);
+ }
+ }
+
+ if (list.size() == 1 && list.get(0) == EMPTY_MARKER) return Collections.emptyList();
+ return list;
+ }
+
+ private List<String> retrieveFromCache(String absolutePathToJar) {
+ synchronized (trackerCache) {
+ Object tracker = jarContentsCacheTrackers.get(absolutePathToJar);
+ if (tracker != null) return jarContentsCache.get(tracker);
+
+ for (Map.Entry<Object, String> entry : trackerCache.entrySet()) {
+ if (entry.getValue().equals(absolutePathToJar)) {
+ tracker = entry.getKey();
+ break;
+ }
+ }
+ List<String> result = null;
+ if (tracker != null) result = jarContentsCache.get(tracker);
+ if (result != null) return result;
+
+ tracker = new Object();
+ List<String> list = new ArrayList<String>();
+ jarContentsCache.put(tracker, list);
+ trackerCache.put(tracker, absolutePathToJar);
+ jarContentsCacheTrackers.put(absolutePathToJar, tracker);
+ return list;
+ }
+ }
+
+ /**
+ * Looks up {@code altName} in {@code location}, and if that isn't found, looks up {@code name}; {@code altName} can be null in which case it is skipped.
+ */
+ private URL getResourceFromLocation(String name, String altName, File location) {
+ if (location.isDirectory()) {
+ try {
+ if (altName != null) {
+ File f = new File(location, altName);
+ if (f.isFile() && f.canRead()) return f.toURI().toURL();
+ }
+
+ File f = new File(location, name);
+ if (f.isFile() && f.canRead()) return f.toURI().toURL();
+ return null;
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+ if (!location.isFile() || !location.canRead()) return null;
+
+ File absoluteFile; {
+ try {
+ absoluteFile = location.getCanonicalFile();
+ } catch (Exception e) {
+ absoluteFile = location.getAbsoluteFile();
+ }
+ }
+ List<String> jarContents = getOrMakeJarListing(absoluteFile.getAbsolutePath());
+
+ String absoluteUri = absoluteFile.toURI().toString();
+
+ try {
+ if (jarContents.contains(altName)) {
+ return new URI("jar:" + absoluteUri + "!/" + altName).toURL();
+ }
+ } catch (Exception e) {}
+
+ try {
+ if (jarContents.contains(name)) {
+ return new URI("jar:" + absoluteUri + "!/" + name).toURL();
+ }
+ } catch(Exception e) {}
+
+ return null;
+ }
+
+ /**
+ * Checks if the stated item is located inside the same classpath root as the jar that hosts ShadowClassLoader.class. {@code item} and {@code name} refer to the same thing.
+ */
+ private boolean inOwnBase(URL item, String name) {
+ if (item == null) return false;
+ String itemString = item.toString();
+ return (itemString.length() == SELF_BASE_LENGTH + name.length()) && SELF_BASE.regionMatches(0, itemString, 0, SELF_BASE_LENGTH);
+ }
+
+ @Override public Enumeration<URL> getResources(String name) throws IOException {
+ String altName = null;
+ if (name.endsWith(".class")) altName = name.substring(0, name.length() - 6) + ".SCL." + sclSuffix;
+
+ // Vector? Yes, we need one:
+ // * We can NOT make inner classes here (this class is loaded with special voodoo magic in eclipse, as a one off, it's not a full loader.
+ // * We need to return an enumeration.
+ // * We can't make one on the fly.
+ // * ArrayList can't make these.
+ Vector<URL> vector = new Vector<URL>();
+
+ for (File ce : override) {
+ URL url = getResourceFromLocation(name, altName, ce);
+ if (url != null) vector.add(url);
+ }
+
+ if (override.isEmpty()) {
+ URL fromSelf = getResourceFromLocation(name, altName, SELF_BASE_FILE);
+ if (fromSelf != null) vector.add(fromSelf);
+ }
+
+ Enumeration<URL> sec = super.getResources(name);
+ while (sec.hasMoreElements()) {
+ URL item = sec.nextElement();
+ if (!inOwnBase(item, name)) vector.add(item);
+ }
+
+ if (altName != null) {
+ Enumeration<URL> tern = super.getResources(altName);
+ while (tern.hasMoreElements()) {
+ URL item = tern.nextElement();
+ if (!inOwnBase(item, altName)) vector.add(item);
+ }
+ }
+
+ return vector.elements();
+ }
+
+ @Override public URL getResource(String name) {
+ return getResource_(name, false);
+ }
+
+ private URL getResource_(String name, boolean noSuper) {
+ String altName = null;
+ if (name.endsWith(".class")) altName = name.substring(0, name.length() - 6) + ".SCL." + sclSuffix;
+ for (File ce : override) {
+ URL url = getResourceFromLocation(name, altName, ce);
+ if (url != null) return url;
+ }
+
+ if (!override.isEmpty()) {
+ if (noSuper) return null;
+ if (altName != null) {
+ try {
+ URL res = getResourceSkippingSelf(altName);
+ if (res != null) return res;
+ } catch (IOException ignore) {}
+ }
+
+ try {
+ return getResourceSkippingSelf(name);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ URL url = getResourceFromLocation(name, altName, SELF_BASE_FILE);
+ if (url != null) return url;
+
+ if (altName != null) {
+ URL res = super.getResource(altName);
+ if (res != null && (!noSuper || inOwnBase(res, altName))) return res;
+ }
+
+ URL res = super.getResource(name);
+ if (res != null && (!noSuper || inOwnBase(res, name))) return res;
+ return null;
+ }
+
+ private boolean exclusionListMatch(String name) {
+ for (String pe : parentExclusion) {
+ if (name.startsWith(pe)) return true;
+ }
+ return false;
+ }
+
+ private URL getResourceSkippingSelf(String name) throws IOException {
+ URL candidate = super.getResource(name);
+ if (candidate == null) return null;
+ if (!inOwnBase(candidate, name)) return candidate;
+
+ Enumeration<URL> en = super.getResources(name);
+ while (en.hasMoreElements()) {
+ candidate = en.nextElement();
+ if (!inOwnBase(candidate, name)) return candidate;
+ }
+
+ return null;
+ }
+
+ @Override public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ {
+ Class<?> alreadyLoaded = findLoadedClass(name);
+ if (alreadyLoaded != null) return alreadyLoaded;
+ }
+
+ String fileNameOfClass = name.replace(".", "/") + ".class";
+ URL res = getResource_(fileNameOfClass, true);
+ if (res == null) {
+ if (!exclusionListMatch(fileNameOfClass)) return super.loadClass(name, resolve);
+ throw new ClassNotFoundException(name);
+ }
+
+ byte[] b;
+ int p = 0;
+ try {
+ InputStream in = res.openStream();
+
+ try {
+ b = new byte[65536];
+ while (true) {
+ int r = in.read(b, p, b.length - p);
+ if (r == -1) break;
+ p += r;
+ if (p == b.length) {
+ byte[] nb = new byte[b.length * 2];
+ System.arraycopy(b, 0, nb, 0, p);
+ b = nb;
+ }
+ }
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ throw new ClassNotFoundException("I/O exception reading class " + name, e);
+ }
+
+ Class<?> c = defineClass(name, b, 0, p);
+ if (resolve) resolveClass(c);
+ return c;
+ }
+
+ public void addOverrideJarDir(String dir) {
+ File f = new File(dir);
+ for (File j : f.listFiles()) {
+ if (j.getName().toLowerCase().endsWith(".jar") && j.canRead() && j.isFile()) override.add(j);
+ }
+ }
+
+ public void addOverrideClasspathEntry(String entry) {
+ override.add(new File(entry));
+ }
+}
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;