aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.xml18
-rw-r--r--buildScripts/ivy-repo/org.projectlombok-lombok.patcher-0.30.xml14
-rw-r--r--buildScripts/ivy.xml6
-rw-r--r--doc/changelog.markdown4
-rw-r--r--doc/utils-maven-pom.xml4
-rw-r--r--src/core/lombok/bytecode/AsmUtil.java2
-rw-r--r--src/core/lombok/bytecode/PreventNullAnalysisRemover.java4
-rw-r--r--src/core/lombok/bytecode/SneakyThrowsRemover.java4
-rw-r--r--src/core/lombok/core/AST.java3
-rw-r--r--src/core/lombok/core/AnnotationProcessor.java6
-rw-r--r--src/core/lombok/core/AnnotationValues.java3
-rw-r--r--src/core/lombok/core/Main.java8
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java3
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java3
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java3
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java9
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java47
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java1
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSuperBuilder.java271
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java2
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java4
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java2
-rw-r--r--src/core/lombok/experimental/SuperBuilder.java23
-rw-r--r--src/core/lombok/javac/CompilerMessageSuppressor.java9
-rw-r--r--src/core/lombok/javac/Javac6BasedLombokOptions.java5
-rw-r--r--src/core/lombok/javac/JavacAST.java52
-rw-r--r--src/core/lombok/javac/JavacResolution.java11
-rw-r--r--src/core/lombok/javac/apt/LombokFileObjects.java7
-rw-r--r--src/core/lombok/javac/apt/LombokProcessor.java68
-rw-r--r--src/core/lombok/javac/apt/Processor.java8
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java26
-rw-r--r--src/core/lombok/javac/handlers/HandleSuperBuilder.java293
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java14
-rw-r--r--src/delombok/lombok/delombok/Delombok.java185
-rw-r--r--src/delombok/lombok/delombok/DelombokApp.java3
-rw-r--r--src/delombok/lombok/delombok/PrettyPrinter.java17
-rw-r--r--src/delombok/lombok/delombok/ant/DelombokTask.java42
-rw-r--r--src/delombok/lombok/delombok/ant/DelombokTaskImpl.java4
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java8
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java6
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java23
-rw-r--r--src/utils/lombok/core/FieldAugment.java5
-rw-r--r--src/utils/lombok/javac/CommentCatcher.java5
-rw-r--r--src/utils/lombok/javac/Javac.java20
-rw-r--r--src/utils/lombok/javac/JavacTreeMaker.java21
-rw-r--r--src/utils/lombok/javac/PackageName.java4
-rw-r--r--src/utils/lombok/javac/java6/CommentCollectingParserFactory.java5
-rw-r--r--src/utils/lombok/javac/java7/CommentCollectingParserFactory.java5
-rw-r--r--src/utils/lombok/javac/java8/CommentCollectingParserFactory.java5
-rw-r--r--src/utils/lombok/javac/java9/CommentCollectingParserFactory.java5
-rw-r--r--src/utils/lombok/permit/Permit.java100
-rw-r--r--src/utils/lombok/permit/package-info.java8
-rw-r--r--test/core/src/lombok/CompilerMessageMatcher.java3
-rw-r--r--test/manual/about.txt1
-rw-r--r--test/manual/moduleBasedMultiProject/.gitignore1
-rw-r--r--test/manual/moduleBasedMultiProject/projA/module-info.java4
-rw-r--r--test/manual/moduleBasedMultiProject/projA/pkgA/ClassA.java5
-rw-r--r--test/manual/moduleBasedMultiProject/projB/module-info.java5
-rw-r--r--test/manual/moduleBasedMultiProject/projB/pkgB/ClassB.java10
-rwxr-xr-xtest/manual/moduleBasedMultiProject/runTests10
-rw-r--r--test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java4
-rw-r--r--test/transform/resource/after-delombok/BuilderWithToBuilder.java77
-rw-r--r--test/transform/resource/after-delombok/EqualsAndHashCodeExplicitInclude.java22
-rw-r--r--test/transform/resource/after-delombok/NoArgsConstructorForce.java1
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderAbstractToBuilder.java168
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java198
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderCustomized.java102
-rw-r--r--test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java162
-rw-r--r--test/transform/resource/after-delombok/ToStringExplicitInclude.java8
-rw-r--r--test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java7
-rw-r--r--test/transform/resource/after-ecj/BuilderWithToBuilder.java66
-rw-r--r--test/transform/resource/after-ecj/EqualsAndHashCodeExplicitInclude.java23
-rw-r--r--test/transform/resource/after-ecj/NoArgsConstructorForce.java1
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderAbstractToBuilder.java131
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java164
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderCustomized.java86
-rw-r--r--test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java134
-rw-r--r--test/transform/resource/after-ecj/ToStringExplicitInclude.java9
-rw-r--r--test/transform/resource/before/BuilderWithToBuilder.java15
-rw-r--r--test/transform/resource/before/EqualsAndHashCodeExplicitInclude.java4
-rw-r--r--test/transform/resource/before/NoArgsConstructorForce.java1
-rw-r--r--test/transform/resource/before/SuperBuilderAbstractToBuilder.java20
-rw-r--r--test/transform/resource/before/SuperBuilderBasicToBuilder.java32
-rw-r--r--test/transform/resource/before/SuperBuilderCustomized.java33
-rw-r--r--test/transform/resource/before/SuperBuilderWithGenericsAndToBuilder.java18
-rw-r--r--test/transform/resource/before/ToStringExplicitInclude.java4
-rw-r--r--website/templates/features/GetterLazy.html3
-rw-r--r--website/templates/features/constructor.html8
-rw-r--r--website/templates/features/delombok.html4
-rw-r--r--website/templates/features/experimental/SuperBuilder.html13
-rw-r--r--website/templates/setup/gwt.html2
-rw-r--r--website/usageExamples/experimental/UtilityClassExample_post.jpage2
-rw-r--r--website/usageExamples/experimental/UtilityClassExample_pre.jpage2
93 files changed, 2491 insertions, 480 deletions
diff --git a/build.xml b/build.xml
index 8fbce893..4ab74f21 100644
--- a/build.xml
+++ b/build.xml
@@ -25,6 +25,8 @@ This buildfile is part of projectlombok.org. It is the main entry point that con
the common tasks and can be called on to run the main aspects of all the sub-scripts.
</description>
+ <property name="pattern.jdk9Plus" value="^(9|[1-9][0-9])(\..*)?$" />
+ <property name="pattern.jdkUpto8" value="^(1\.)?[2-8](\..*)?$" />
<property name="build.compiler" value="javac1.6" />
<property name="ivy.retrieve.pattern" value="lib/[conf]/[organisation]-[artifact].[ext]" />
<available file="lib/ivyplusplus.jar" property="ivyplusplus.available" />
@@ -163,7 +165,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr
<target name="-ensureJdk9">
<condition property="java.version.insufficient">
- <matches string="${java.version}" pattern="^1\.[2-8](\..*)?" />
+ <matches string="${ant.java.version}" pattern="${pattern.jdkUpto8}" />
</condition>
<fail if="java.version.insufficient">To compile lombok, you need JDK9 or higher; lombok requires this version because it's rather difficult to produce lombok builds that are compatible on JDK9 without at least building with JDK9. Sorry about that.</fail>
</target>
@@ -552,11 +554,11 @@ ${sourceWarning}</echo>
</target>
<target name="test-ecj" depends="dist, contrib, setupJavaOracle8TestEnvironment" unless="tests.skip">
- <condition property="ecj.loc" value="lib/ecj9/*" else="lib/ecj8/*">
- <equals arg1="${ant.java.version}" arg2="9" />
+ <condition property="ecj.loc" value="lib/ecj9/org.eclipse.jdt-ecj.jar" else="lib/ecj8/org.eclipse.jdt.core.compiler-ecj.jar">
+ <matches string="${ant.java.version}" pattern="${pattern.jdk9Plus}" />
</condition>
- <java classname="org.eclipse.jdt.internal.compiler.batch.Main" fork="true" failonerror="true">
- <classpath path="${ecj.loc}" />
+ <echo>Testing ECJ using ECJ: ${ecj.loc}</echo>
+ <java jar="${ecj.loc}" fork="true" failonerror="true">
<jvmarg value="-javaagent:dist/lombok.jar=ecj" />
<arg value="-source" />
<arg value="1.6" />
@@ -646,7 +648,7 @@ ${sourceWarning}</echo>
<get src="https://projectlombok.org/ivyrepo/langtools/jdk8-javac-sources.zip" dest="lib/oracleJDK8Environment/javac8-sources.zip" verbose="true" usetimestamp="true" />
<propertyfile file="testenvironment.properties">
<entry key="test.location.javac" value="lib/oracleJDK8Environment/javac8.jar" />
- <entry key="test.location.ecj" value="lib/ecj8/org.eclipse.custom-ecj.jar" />
+ <entry key="test.location.ecj" value="lib/ecj8/org.eclipse.jdt.core.compiler-ecj.jar" />
<entry key="test.location.bootclasspath" value="lib/oracleJDK8Environment/rt.jar" />
<entry key="test.location.name" value="OracleJDK8" />
<entry key="test.javaversion" value="8" />
@@ -688,13 +690,13 @@ You can also create your own by writing a 'testenvironment.properties' file. The
<condition property="test9.run">
<and>
<not><isset property="tests.skip" /></not>
- <equals arg1="${ant.java.version}" arg2="9" />
+ <matches string="${ant.java.version}" pattern="${pattern.jdk9Plus}" />
</and>
</condition>
<condition property="test8.run">
<and>
<not><isset property="tests.skip" /></not>
- <not><equals arg1="${ant.java.version}" arg2="9" /></not>
+ <matches string="${ant.java.version}" pattern="${pattern.jdkUpto8}" />
</and>
</condition>
</target>
diff --git a/buildScripts/ivy-repo/org.projectlombok-lombok.patcher-0.30.xml b/buildScripts/ivy-repo/org.projectlombok-lombok.patcher-0.30.xml
new file mode 100644
index 00000000..9a87a8bf
--- /dev/null
+++ b/buildScripts/ivy-repo/org.projectlombok-lombok.patcher-0.30.xml
@@ -0,0 +1,14 @@
+<ivy-module version="2.0">
+ <info organisation="org.projectlombok" module="lombok.patcher" revision="0.30" publication="20180910222000">
+ <license name="MIT License" url="https://www.opensource.org/licenses/mit-license.php" />
+ <ivyauthor name="rzwitserloot" url="https://github.com/rzwitserloot" />
+ <ivyauthor name="rspilker" url="https://github.com/rspilker" />
+ <description homepage="https://projectlombok.org/" />
+ </info>
+ <configurations>
+ <conf name="default" />
+ </configurations>
+ <publications>
+ <artifact conf="default" url="https://projectlombok.org/downloads/lombok.patcher-0.30.jar" />
+ </publications>
+</ivy-module>
diff --git a/buildScripts/ivy.xml b/buildScripts/ivy.xml
index 15c03ed1..221ec6e9 100644
--- a/buildScripts/ivy.xml
+++ b/buildScripts/ivy.xml
@@ -18,7 +18,7 @@
<conf name="supporters" />
</configurations>
<dependencies>
- <dependency org="org.projectlombok" name="lombok.patcher" rev="0.28" conf="buildBase->default; runtime->default" />
+ <dependency org="org.projectlombok" name="lombok.patcher" rev="0.30" conf="buildBase->default; runtime->default" />
<dependency org="zwitserloot.com" name="cmdreader" rev="1.2" conf="buildBase->runtime; runtime" />
<dependency org="junit" name="junit" rev="4.8.2" conf="test->default; contrib->sources" />
@@ -44,8 +44,8 @@
<dependency org="net.java.openjdk.custom" name="javac7" rev="1.7.0" conf="javac7->runtime; contrib->sources" />
<dependency org="org.eclipse.custom" name="ecj" rev="4.3.1" conf="ecj7->default; contrib->sources" />
<dependency org="org.eclipse.jdt.core.compiler" name="ecj" rev="4.6.1" conf="ecj8->default; contrib->sources" />
- <dependency org="org.eclipse.tycho" name="org.eclipse.jdt.core" rev="3.13.50.v20171007-0855" conf="ecj9->default; eclipseBuild->default" />
- <dependency org="org.eclipse.tycho" name="org.eclipse.jdt.compiler.apt" rev="1.3.50.v20170920-0950" conf="ecj9->default; eclipseBuild->default" />
+ <dependency org="org.eclipse.jdt" name="ecj" rev="3.15.0" conf="ecj9->default; eclipseBuild->default" />
+ <dependency org="org.eclipse.jdt" name="org.eclipse.jdt.compiler.apt" rev="1.3.300" conf="ecj9->default; eclipseBuild->default" />
<dependency org="netbeans.org" name="boot" rev="6.8beta" conf="netbeansBuild->build" />
<dependency org="netbeans.org" name="openide.modules" rev="6.8beta" conf="netbeansBuild->build" />
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index 220e0dc0..aad47a85 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -5,7 +5,11 @@ Lombok Changelog
* PLATFORM: Support for Eclipse Photon. [Issue #1831](https://github.com/rzwitserloot/lombok/issues/1831)
* FEATURE: The `@FieldNameConstants` feature has been completely redesigned. [Issue #1774](https://github.com/rzwitserloot/lombok/issues/1774) [FieldNameConstants documentation](https://projectlombok.org/features/experimental/FieldNameConstants)
* FEATURE: Lombok's `@NonNull` annotation can now be used on types (annotation on types has been introduced in JDK 8). `@Builder`'s `@Singular` annotation now properly deals with annotations on the generics type on the collection: `@Singular List<@NonNull String> names;` now does the right thing.
+* FEATURE: You can now mix `@SuperBuilder` and `toBuilder`, and `toBuilder` no longer throws `NullPointerException` if an `@Singular`-marked collection field is `null`. [Issue #1324](https://github.com/rzwitserloot/lombok/issues/1324)
+* FEATURE: delombok now supports module paths via the `--module-path` option, and will automatically add lombok itself to the module path. This should make it possible to delombok your modularized projects. [Issue #1848](https://github.com/rzwitserloot/lombok/issues/1848)
+* FEATURE: You can pass `@args.txt` to `delombok` to read args from the text file; useful if you have really long classpaths you need to pass to delombok. [Issue #1795](https://github.com/rzwitserloot/lombok/issues/1795)
* BREAKING CHANGE: Lombok will now always copy specific annotations around (from field to getter, from field to builder 'setter', etcetera): A specific curated list of known annotations where that is the right thing to do (generally, `@NonNull` style annotations from various libraries), as well as any annotations you explicitly list in the `lombok.copyableAnnotations` config key in your `lombok.config` file. Also, lombok is more consistent about copying these annotations. (Previous behaviour: Lombok used to copy any annotation whose simple name was `NonNull`, `Nullable`, or `CheckForNull`). [Issue #1570](https://github.com/rzwitserloot/lombok/issues/1570) and [Issue #1634](https://github.com/rzwitserloot/lombok/issues/1634)
+* BUGFIX: `@NoArgsConstructor(force=true)` would try to initialize already initialized final fields in Eclipse. [Issue #1829](https://github.com/rzwitserloot/lombok/issues/1829)
* BUGFIX: When using lombok to compile modularized (`module-info.java`-style) code, if the module name has dots in it, it wouldn't work. [Issue #1808](https://github.com/rzwitserloot/lombok/issues/1808)
* BUGFIX: Errors about lombok not reading a module providing `org.mapstruct.ap.spi` when trying to use lombok in jigsaw-mode on JDK 11. [Issue #1806](https://github.com/rzwitserloot/lombok/issues/1806)
* BUGFIX: Fix NetBeans compile on save. [Issue #1770](https://github.com/rzwitserloot/lombok/issues/1770)
diff --git a/doc/utils-maven-pom.xml b/doc/utils-maven-pom.xml
index b3b73c1c..e9c17d34 100644
--- a/doc/utils-maven-pom.xml
+++ b/doc/utils-maven-pom.xml
@@ -21,8 +21,8 @@
<url>http://github.com/rzwitserloot/lombok</url>
</scm>
<issueManagement>
- <system>Google Code</system>
- <url>http://code.google.com/p/projectlombok/issues</url>
+ <system>GitHub Issues</system>
+ <url>https://github.com/rzwitserloot/lombok/issues</url>
</issueManagement>
<developers>
<developer>
diff --git a/src/core/lombok/bytecode/AsmUtil.java b/src/core/lombok/bytecode/AsmUtil.java
index e3d33efa..42bf9700 100644
--- a/src/core/lombok/bytecode/AsmUtil.java
+++ b/src/core/lombok/bytecode/AsmUtil.java
@@ -37,7 +37,7 @@ class AsmUtil {
ClassReader reader = new ClassReader(byteCode);
ClassWriter writer = new FixedClassWriter(reader, 0);
- ClassVisitor visitor = new ClassVisitor(Opcodes.ASM6, writer) {
+ ClassVisitor visitor = new ClassVisitor(Opcodes.ASM7, writer) {
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new JSRInlinerAdapter(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc, signature, exceptions);
}
diff --git a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java
index c06f2d7c..20572e22 100644
--- a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java
+++ b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java
@@ -50,7 +50,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation {
class PreventNullAnalysisVisitor extends MethodVisitor {
PreventNullAnalysisVisitor(MethodVisitor mv) {
- super(Opcodes.ASM6, mv);
+ super(Opcodes.ASM7, mv);
}
@Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
@@ -68,7 +68,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation {
}
}
- reader.accept(new ClassVisitor(Opcodes.ASM6, writer) {
+ reader.accept(new ClassVisitor(Opcodes.ASM7, writer) {
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new PreventNullAnalysisVisitor(super.visitMethod(access, name, desc, signature, exceptions));
}
diff --git a/src/core/lombok/bytecode/SneakyThrowsRemover.java b/src/core/lombok/bytecode/SneakyThrowsRemover.java
index a2ee59ea..037cb109 100644
--- a/src/core/lombok/bytecode/SneakyThrowsRemover.java
+++ b/src/core/lombok/bytecode/SneakyThrowsRemover.java
@@ -52,7 +52,7 @@ public class SneakyThrowsRemover implements PostCompilerTransformation {
class SneakyThrowsRemoverVisitor extends MethodVisitor {
SneakyThrowsRemoverVisitor(MethodVisitor mv) {
- super(Opcodes.ASM6, mv);
+ super(Opcodes.ASM7, mv);
}
private boolean methodInsnQueued = false;
@@ -177,7 +177,7 @@ public class SneakyThrowsRemover implements PostCompilerTransformation {
}
}
- reader.accept(new ClassVisitor(Opcodes.ASM5, writer) {
+ reader.accept(new ClassVisitor(Opcodes.ASM7, writer) {
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new SneakyThrowsRemoverVisitor(super.visitMethod(access, name, desc, signature, exceptions));
}
diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java
index fe7a4330..afbba1e8 100644
--- a/src/core/lombok/core/AST.java
+++ b/src/core/lombok/core/AST.java
@@ -39,6 +39,7 @@ import java.util.concurrent.ConcurrentMap;
import lombok.core.configuration.ConfigurationKey;
import lombok.core.debug.HistogramTracker;
+import lombok.permit.Permit;
/**
* Lombok wraps the AST produced by a target platform into its own AST system, mostly because both Eclipse and javac
@@ -252,7 +253,7 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
}
if (shouldDrill(c, t, f.getName())) {
- f.setAccessible(true);
+ Permit.setAccessible(f);
fields.add(new FieldAccess(f, dim));
}
}
diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java
index 293bfef6..363952a4 100644
--- a/src/core/lombok/core/AnnotationProcessor.java
+++ b/src/core/lombok/core/AnnotationProcessor.java
@@ -46,6 +46,7 @@ import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import lombok.patcher.ClassRootFinder;
+import lombok.permit.Permit;
@SupportedAnnotationTypes("*")
public class AnnotationProcessor extends AbstractProcessor {
@@ -82,8 +83,7 @@ public class AnnotationProcessor extends AbstractProcessor {
for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) {
try {
- Field field = procEnvClass.getDeclaredField("delegate");
- field.setAccessible(true);
+ Field field = Permit.getField(procEnvClass, "delegate");
Object delegate = field.get(procEnv);
return tryRecursivelyObtainJavacProcessingEnvironment((ProcessingEnvironment) delegate);
@@ -136,7 +136,7 @@ public class AnnotationProcessor extends AbstractProcessor {
ClassLoader environmentClassLoader = procEnv.getClass().getClassLoader();
if (environmentClassLoader != null && environmentClassLoader.getClass().getCanonicalName().equals("org.codehaus.plexus.compiler.javac.IsolatedClassLoader")) {
if (!ClassLoader_lombokAlreadyAddedTo.getAndSet(environmentClassLoader, true)) {
- Method m = environmentClassLoader.getClass().getDeclaredMethod("addURL", URL.class);
+ Method m = Permit.getMethod(environmentClassLoader.getClass(), "addURL", URL.class);
URL selfUrl = new File(ClassRootFinder.findClassRootOfClass(AnnotationProcessor.class)).toURI().toURL();
m.invoke(environmentClassLoader, selfUrl);
}
diff --git a/src/core/lombok/core/AnnotationValues.java b/src/core/lombok/core/AnnotationValues.java
index a24330fa..eec5abd8 100644
--- a/src/core/lombok/core/AnnotationValues.java
+++ b/src/core/lombok/core/AnnotationValues.java
@@ -33,6 +33,7 @@ import java.util.List;
import java.util.Map;
import lombok.core.AST.Kind;
+import lombok.permit.Permit;
/**
* Represents a single annotation in a source file and can be used to query the parameters present on it.
@@ -210,7 +211,7 @@ public class AnnotationValues<A extends Annotation> {
public <T> T getDefaultIf(String methodName, Class<T> type, T defaultValue) {
try {
- return type.cast(type.getMethod(methodName).getDefaultValue());
+ return type.cast(Permit.getMethod(type, methodName).getDefaultValue());
} catch (Exception e) {
return defaultValue;
}
diff --git a/src/core/lombok/core/Main.java b/src/core/lombok/core/Main.java
index 6952ab78..47afaefd 100644
--- a/src/core/lombok/core/Main.java
+++ b/src/core/lombok/core/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 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,7 +41,9 @@ public class Main {
Thread.currentThread().setContextClassLoader(Main.class.getClassLoader());
int err = new Main(SpiLoadUtil.readAllFromIterator(
SpiLoadUtil.findServices(LombokApp.class)), Arrays.asList(args)).go();
- System.exit(err);
+ if (err != 0) {
+ System.exit(err);
+ }
}
@ProviderFor(LombokApp.class)
@@ -143,7 +145,7 @@ public class Main {
out.println("------------------------------");
}
out.println("projectlombok.org " + Version.getFullVersion());
- out.println("Copyright (C) 2009-2015 The Project Lombok Authors.");
+ out.println("Copyright (C) 2009-2018 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/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
index 296b70b4..ac91447d 100644
--- a/src/core/lombok/core/handlers/HandlerUtil.java
+++ b/src/core/lombok/core/handlers/HandlerUtil.java
@@ -85,6 +85,7 @@ public class HandlerUtil {
"org.jetbrains.annotations.NotNull",
"android.support.annotation.NonNull",
"org.eclipse.jdt.annotation.NonNull",
+ "org.springframework.lang.NonNull"
}));
BASE_COPYABLE_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
"lombok.NonNull",
@@ -93,6 +94,7 @@ public class HandlerUtil {
"org.jetbrains.annotations.NotNull",
"android.support.annotation.NonNull",
"org.eclipse.jdt.annotation.NonNull",
+ "org.springframework.lang.NonNull",
"javax.annotation.Nullable",
"javax.annotation.CheckForNull",
"edu.umd.cs.findbugs.annotations.UnknownNullness",
@@ -100,6 +102,7 @@ public class HandlerUtil {
"org.jetbrains.annotations.Nullable",
"android.support.annotation.Nullable",
"org.eclipse.jdt.annotation.Nullable",
+ "org.springframework.lang.Nullable"
}));
}
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java
index 7cd2e400..1ba26338 100644
--- a/src/core/lombok/eclipse/EclipseAST.java
+++ b/src/core/lombok/eclipse/EclipseAST.java
@@ -34,6 +34,7 @@ import lombok.Lombok;
import lombok.core.AST;
import lombok.core.LombokImmutableList;
import lombok.eclipse.handlers.EclipseHandlerUtil;
+import lombok.permit.Permit;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;
@@ -497,7 +498,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
Throwable problem_ = null;
Method m = null;
try {
- m = EclipseAstProblemView.class.getMethod("addProblemToCompilationResult", char[].class, Class.forName(COMPILATIONRESULT_TYPE), boolean.class, String.class, int.class, int.class);
+ m = Permit.getMethod(EclipseAstProblemView.class, "addProblemToCompilationResult", char[].class, Class.forName(COMPILATIONRESULT_TYPE), boolean.class, String.class, int.class, int.class);
} catch (Throwable t) {
// That's problematic, but as long as no local classes are used we don't actually need it.
// Better fail on local classes than crash altogether.
diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java
index 323fc171..e5edba64 100644
--- a/src/core/lombok/eclipse/TransformEclipseAST.java
+++ b/src/core/lombok/eclipse/TransformEclipseAST.java
@@ -30,6 +30,7 @@ import lombok.core.LombokConfiguration;
import lombok.core.debug.DebugSnapshotStore;
import lombok.core.debug.HistogramTracker;
import lombok.patcher.Symbols;
+import lombok.permit.Permit;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
@@ -90,7 +91,7 @@ public class TransformEclipseAST {
disableLombok = true;
}
try {
- f = CompilationUnitDeclaration.class.getDeclaredField("$lombokAST");
+ f = Permit.getField(CompilationUnitDeclaration.class, "$lombokAST");
} catch (Throwable t) {
//I guess we're in an ecj environment; we'll just not cache stuff then.
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 5d582aad..c69b3d0f 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -56,6 +56,7 @@ import lombok.eclipse.EclipseAST;
import lombok.eclipse.EclipseNode;
import lombok.experimental.Accessors;
import lombok.experimental.Tolerate;
+import lombok.permit.Permit;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -337,9 +338,7 @@ public class EclipseHandlerUtil {
private static Field getField(Class<?> c, String fName) {
try {
- Field f = c.getDeclaredField(fName);
- f.setAccessible(true);
- return f;
+ return Permit.getField(c, fName);
} catch (Exception e) {
return null;
}
@@ -1931,12 +1930,12 @@ public class EclipseHandlerUtil {
Constructor<IntLiteral> intLiteralConstructor_ = null;
Method intLiteralFactoryMethod_ = null;
try {
- intLiteralConstructor_ = IntLiteral.class.getConstructor(parameterTypes);
+ intLiteralConstructor_ = Permit.getConstructor(IntLiteral.class, parameterTypes);
} catch (Throwable ignore) {
// probably eclipse 3.7++
}
try {
- intLiteralFactoryMethod_ = IntLiteral.class.getMethod("buildIntLiteral", parameterTypes);
+ intLiteralFactoryMethod_ = Permit.getMethod(IntLiteral.class, "buildIntLiteral", parameterTypes);
} catch (Throwable ignore) {
// probably eclipse versions before 3.7
}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 86c1da42..0ce1436d 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -25,6 +25,7 @@ import static lombok.eclipse.Eclipse.*;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -46,6 +47,7 @@ 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.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;
@@ -105,7 +107,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return ((Boolean) expr).booleanValue();
}
- private static class BuilderFieldData {
+ static class BuilderFieldData {
Annotation[] annotations;
TypeReference type;
char[] rawName;
@@ -443,6 +445,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
cleanDecl.declarationSourceEnd = -1;
cleanDecl.modifiers = ClassFileConstants.AccPrivate;
cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
+ cleanDecl.traverse(new SetGeneratedByVisitor(ast), (MethodScope) null);
injectFieldAndMarkGenerated(builderType, cleanDecl);
}
@@ -502,15 +505,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
}
- private static final char[] EMPTY_LIST = "emptyList".toCharArray();
+ private static final char[] BUILDER_TEMP_VAR = {'b', 'u', 'i', 'l', 'd', 'e', 'r'};
private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source) {
- // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b);
-
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;
- MethodDeclaration out = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.selector = methodName.toCharArray();
out.modifiers = ClassFileConstants.AccPublic;
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
@@ -519,6 +519,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
Expression receiver = invoke;
+ List<Statement> statements = null;
for (BuilderFieldData bfd : builderFields) {
char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray();
MessageSend ms = new MessageSend();
@@ -552,22 +553,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
tgt[i] = obtainExpr;
}
}
+
+ ms.selector = setterName;
if (bfd.singularData == null) {
ms.arguments = tgt;
+ ms.receiver = receiver;
+ receiver = ms;
} else {
- Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
- MessageSend emptyList = new MessageSend();
- emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray());
- emptyList.selector = EMPTY_LIST;
- emptyList.typeArguments = bfd.singularData.getSingularizer().createTypeArgs(bfd.singularData.getTypeArgs().size(), false, type, bfd.singularData.getTypeArgs());
- ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])};
+ ms.arguments = new Expression[] {tgt[1]};
+ ms.receiver = new SingleNameReference(BUILDER_TEMP_VAR, p);
+ EqualExpression isNotNull = new EqualExpression(tgt[0], new NullLiteral(pS, pE), OperatorIds.NOT_EQUAL);
+ if (statements == null) statements = new ArrayList<Statement>();
+ statements.add(new IfStatement(isNotNull, ms, pS, pE));
}
- ms.receiver = receiver;
- ms.selector = setterName;
- receiver = ms;
}
- out.statements = new Statement[] {new ReturnStatement(receiver, pS, pE)};
+ if (statements != null) {
+ out.statements = new Statement[statements.size() + 2];
+ for (int i = 0; i < statements.size(); i++) out.statements[i + 1] = statements.get(i);
+ LocalDeclaration b = new LocalDeclaration(BUILDER_TEMP_VAR, pS, pE);
+ out.statements[0] = b;
+ b.modifiers |= Modifier.FINAL;
+ b.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+ b.type.sourceStart = pS; b.type.sourceEnd = pE;
+ b.initialization = receiver;
+ out.statements[out.statements.length - 1] = new ReturnStatement(new SingleNameReference(BUILDER_TEMP_VAR, p), pS, pE);
+ } else {
+ out.statements = new Statement[] {new ReturnStatement(receiver, pS, pE)};
+ }
out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
@@ -627,7 +640,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters);
args.add(new ConditionalExpression(
- new SingleNameReference(bfd.nameOfSetFlag, 0L),
+ new SingleNameReference(bfd.nameOfSetFlag, 0L),
new SingleNameReference(bfd.name, 0L),
inv));
} else {
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java
index cb07115a..82859e4b 100644
--- a/src/core/lombok/eclipse/handlers/HandleConstructor.java
+++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java
@@ -484,6 +484,7 @@ public class HandleConstructor {
for (EclipseNode node : type.down()) {
if (node.getKind() != Kind.FIELD) continue top;
FieldDeclaration fd = (FieldDeclaration) node.get();
+ if (fd.initialization != null) continue top;
if ((fd.modifiers & ClassFileConstants.AccFinal) == 0) continue top;
if ((fd.modifiers & ClassFileConstants.AccStatic) != 0) continue top;
for (EclipseNode ftp : fieldsToParam) if (node == ftp) continue top;
diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
index 3070f1dc..9a3275c2 100644
--- a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -39,7 +39,9 @@ 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.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
@@ -48,6 +50,8 @@ 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.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.QualifiedNameReference;
@@ -56,6 +60,7 @@ 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.SuperReference;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
@@ -87,6 +92,7 @@ import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
+import lombok.eclipse.handlers.HandleBuilder.BuilderFieldData;
import lombok.experimental.NonFinal;
import lombok.experimental.SuperBuilder;
@@ -98,41 +104,36 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
private static final char[] DEFAULT_PREFIX = "$default$".toCharArray();
private static final char[] SET_PREFIX = "$set".toCharArray();
private static final char[] SELF_METHOD_NAME = "self".toCharArray();
+ private static final String TO_BUILDER_METHOD_NAME_STRING = "toBuilder";
+ private static final char[] TO_BUILDER_METHOD_NAME = TO_BUILDER_METHOD_NAME_STRING.toCharArray();
+ private static final char[] FILL_VALUES_METHOD_NAME = "$fillValuesFrom".toCharArray();
+ private static final char[] FILL_VALUES_STATIC_METHOD_NAME = "$fillValuesFromInstanceIntoBuilder".toCharArray();
+ private static final char[] EMPTY_LIST = "emptyList".toCharArray();
+ private static final char[] INSTANCE_VARIABLE_NAME = "instance".toCharArray();
+ private static final String BUILDER_VARIABLE_NAME_STRING = "b";
+ private static final char[] BUILDER_VARIABLE_NAME = BUILDER_VARIABLE_NAME_STRING.toCharArray();
private static final AbstractMethodDeclaration[] EMPTY_METHODS = {};
- private static class BuilderFieldData {
- Annotation[] annotations;
- TypeReference type;
- char[] rawName;
- char[] name;
- char[] nameOfDefaultProvider;
- char[] nameOfSetFlag;
- SingularData singularData;
- ObtainVia obtainVia;
- EclipseNode obtainViaNode;
- EclipseNode originalFieldNode;
-
- List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
- }
-
@Override
public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) {
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
- SuperBuilder builderInstance = annotation.getInstance();
+ SuperBuilder superbuilderAnnotation = annotation.getInstance();
- String builderMethodName = builderInstance.builderMethodName();
- String buildMethodName = builderInstance.buildMethodName();
+ String builderMethodName = superbuilderAnnotation.builderMethodName();
+ String buildMethodName = superbuilderAnnotation.buildMethodName();
if (builderMethodName == null) builderMethodName = "builder";
if (buildMethodName == null) buildMethodName = "build";
if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
-
+
+ boolean toBuilder = superbuilderAnnotation.toBuilder();
+
EclipseNode tdParent = annotationNode.up();
java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
@@ -258,14 +259,28 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
superclassBuilderClass != null);
- // Create the abstract builder class.
+ // Create the abstract builder class, or reuse an existing one.
EclipseNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass,
typeParams, ast, classGenericName, builderGenericName);
} else {
- annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
- return;
+ TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get();
+ if ((builderTypeDeclaration.modifiers & (ClassFileConstants.AccStatic | ClassFileConstants.AccAbstract)) == 0) {
+ annotationNode.addError("Existing Builder must be an abstract static inner class.");
+ return;
+ }
+ 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;
+ }
+ }
}
// Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary.
@@ -297,7 +312,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
injectFieldAndMarkGenerated(builderType, cleanDecl);
}
-
+
+ if (toBuilder) {
+ // Generate $fillValuesFrom() method in the abstract builder.
+ injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClass != null, builderGenericName, classGenericName, builderClassName, typeParams));
+ // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
+ injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields, ast));
+ }
+
// Generate abstract self() and build() methods in the abstract builder.
injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName));
injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClass != null, classGenericName, ast));
@@ -324,23 +346,44 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
- if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) {
+ boolean isAbstract = (td.modifiers & ClassFileConstants.AccAbstract) != 0;
+ if (isAbstract) {
// Only non-abstract classes get the Builder implementation.
return;
}
- // Create the builder implementation class.
+ // Create the builder implementation class, or reuse an existing one.
EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName);
if (builderImplType == null) {
builderImplType = generateBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast);
} else {
- annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
- return;
+ TypeDeclaration builderImplTypeDeclaration = (TypeDeclaration) builderImplType.get();
+ if ((builderImplTypeDeclaration.modifiers & ClassFileConstants.AccAbstract) != 0 ||
+ (builderImplTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) {
+ annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class.");
+ return;
+ }
+ sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderImplType, annotationNode);
}
-
+
+ if (toBuilder) {
+ // Add the toBuilder() method to the annotated class.
+ switch (methodExists(TO_BUILDER_METHOD_NAME_STRING, tdParent, 0)) {
+ case EXISTS_BY_USER:
+ annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+ break;
+ case NOT_EXISTS:
+ injectMethod(tdParent, generateToBuilderMethod(builderClassName, builderImplClassName, tdParent, typeParams, ast));
+ default:
+ // Should not happen.
+ }
+ }
+
// Create the self() and build() methods in the BuilderImpl.
injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams, p));
- injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast));
+ if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) {
+ injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast));
+ }
// Add the builder() method to the annotated class.
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
@@ -441,7 +484,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
constructor.selector = typeDeclaration.name;
if (callBuilderBasedSuperConstructor) {
constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
- constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)};
+ constructor.constructorCall.arguments = new Expression[] {new SingleNameReference(BUILDER_VARIABLE_NAME, p)};
} else {
constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper);
}
@@ -455,7 +498,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
- constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, builderType, Modifier.FINAL)};
+ constructor.arguments = new Argument[] {new Argument(BUILDER_VARIABLE_NAME, p, builderType, Modifier.FINAL)};
List<Statement> statements = new ArrayList<Statement>();
@@ -468,10 +511,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
Expression assignmentExpr;
if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) {
- fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, "b");
+ fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, BUILDER_VARIABLE_NAME_STRING);
assignmentExpr = new SingleNameReference(fieldNode.name, p);
} else {
- char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName};
+ char[][] variableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldName};
long[] positions = new long[] {p, p};
assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e);
}
@@ -479,7 +522,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
// In case of @Builder.Default, set the value to the default if it was NOT set in the builder.
if (fieldNode.nameOfSetFlag != null) {
- char[][] setVariableInBuilder = new char[][] {"b".toCharArray(), fieldNode.nameOfSetFlag};
+ char[][] setVariableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldNode.nameOfSetFlag};
long[] positions = new long[] {p, p};
QualifiedNameReference setVariableInBuilderRef = new QualifiedNameReference(setVariableInBuilder, positions, s, e);
@@ -533,6 +576,166 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
return out;
}
+ /**
+ * Generates a <code>toBuilder()</code> method in the annotated class that looks like this:
+ * <pre>
+ * public ParentBuilder&lt;?, ?&gt; toBuilder() {
+ * return new <i>Foobar</i>BuilderImpl().$fillValuesFrom(this);
+ * }
+ * </pre>
+ */
+ private MethodDeclaration generateToBuilderMethod(String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ out.selector = TO_BUILDER_METHOD_NAME;
+ out.modifiers = ClassFileConstants.AccPublic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+
+ TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) };
+ out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
+
+ AllocationExpression newClass = new AllocationExpression();
+ newClass.type = namePlusTypeParamsToTypeReference(builderImplClassName.toCharArray(), typeParams, p);
+ MessageSend invokeFillMethod = new MessageSend();
+ invokeFillMethod.receiver = newClass;
+ invokeFillMethod.selector = FILL_VALUES_METHOD_NAME;
+ invokeFillMethod.arguments = new Expression[] {new ThisReference(0, 0)};
+ out.statements = new Statement[] {new ReturnStatement(invokeFillMethod, pS, pE)};
+
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
+ return out;
+ }
+
+ /**
+ * Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks
+ * like this:
+ * <pre>
+ * protected B $fillValuesFrom(final C instance) {
+ * super.$fillValuesFrom(instance);
+ * FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
+ * return self();
+ * }
+ * </pre>
+ */
+ private MethodDeclaration generateFillValuesMethod(EclipseNode tdParent, boolean inherited, String builderGenericName, String classGenericName, String builderClassName, TypeParameter[] typeParams) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+ out.selector = FILL_VALUES_METHOD_NAME;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.modifiers = ClassFileConstants.AccProtected;
+ if (inherited) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())};
+ out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0);
+
+ TypeReference builderType = new SingleTypeReference(classGenericName.toCharArray(), 0);
+ out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, builderType, Modifier.FINAL)};
+
+ List<Statement> body = new ArrayList<Statement>();
+
+ if (inherited) {
+ // Call super.
+ MessageSend callToSuper = new MessageSend();
+ callToSuper.receiver = new SuperReference(0, 0);
+ callToSuper.selector = FILL_VALUES_METHOD_NAME;
+ callToSuper.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0)};
+ body.add(callToSuper);
+ }
+
+ // Call the builder implemention's helper method that actually fills the values from the instance.
+ MessageSend callStaticFillValuesMethod = new MessageSend();
+ callStaticFillValuesMethod.receiver = new SingleNameReference(builderClassName.toCharArray(), 0);
+ callStaticFillValuesMethod.selector = FILL_VALUES_STATIC_METHOD_NAME;
+ callStaticFillValuesMethod.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0), new ThisReference(0, 0)};
+ body.add(callStaticFillValuesMethod);
+
+ // Return self().
+ MessageSend returnCall = new MessageSend();
+ returnCall.receiver = ThisReference.implicitThis();
+ returnCall.selector = SELF_METHOD_NAME;
+ body.add(new ReturnStatement(returnCall, 0, 0));
+
+ out.statements = body.isEmpty() ? null : body.toArray(new Statement[body.size()]);
+
+ return out;
+ }
+
+ /**
+ * Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in
+ * the builder implementation class that copies all fields from the instance
+ * to the builder. It looks like this:
+ *
+ * <pre>
+ * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder&lt;?, ?&gt; b) {
+ * b.field(instance.field);
+ * }
+ * </pre>
+ */
+ private MethodDeclaration generateStaticFillValuesMethod(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, java.util.List<BuilderFieldData> builderFields, ASTNode source) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+ out.selector = FILL_VALUES_STATIC_METHOD_NAME;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic;
+ out.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
+
+ TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
+ TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, 0);
+ Argument builderArgument = new Argument(BUILDER_VARIABLE_NAME, 0, builderType, Modifier.FINAL);
+ TypeReference parentArgument = createTypeReferenceWithTypeParameters(tdParent.getName(), typeParams);
+ out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, parentArgument, Modifier.FINAL), builderArgument};
+
+ // Add type params if there are any.
+ if (typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source);
+
+ List<Statement> body = new ArrayList<Statement>();
+
+ // Call the builder's setter methods to fill the values from the instance.
+ for (BuilderFieldData bfd : builderFields) {
+ MessageSend exec = createSetterCallWithInstanceValue(bfd, tdParent, source);
+ body.add(exec);
+ }
+
+ out.statements = body.isEmpty() ? null : body.toArray(new Statement[body.size()]);
+
+ return out;
+ }
+
+ private MessageSend createSetterCallWithInstanceValue(BuilderFieldData bfd, EclipseNode type, ASTNode source) {
+ char[] setterName = bfd.name;
+ MessageSend ms = new MessageSend();
+ Expression[] tgt = new Expression[bfd.singularData == null ? 1 : 2];
+
+ if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+ char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray();
+ for (int i = 0; i < tgt.length; i++) {
+ FieldReference fr = new FieldReference(fieldName, 0);
+ fr.receiver = new SingleNameReference(INSTANCE_VARIABLE_NAME, 0);
+ tgt[i] = fr;
+ }
+ } else {
+ String obtainName = bfd.obtainVia.method();
+ boolean obtainIsStatic = bfd.obtainVia.isStatic();
+ for (int i = 0; i < tgt.length; i++) {
+ MessageSend obtainExpr = new MessageSend();
+ obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new SingleNameReference(INSTANCE_VARIABLE_NAME, 0);
+ obtainExpr.selector = obtainName.toCharArray();
+ if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0)};
+ tgt[i] = obtainExpr;
+ }
+ }
+ if (bfd.singularData == null) {
+ ms.arguments = tgt;
+ } else {
+ Expression ifNull = new EqualExpression(tgt[0], new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+ MessageSend emptyList = new MessageSend();
+ emptyList.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Collections".toCharArray());
+ emptyList.selector = EMPTY_LIST;
+ ms.arguments = new Expression[] {new ConditionalExpression(ifNull, emptyList, tgt[1])};
+ }
+ ms.receiver = new SingleNameReference(BUILDER_VARIABLE_NAME, 0);
+ ms.selector = setterName;
+ return ms;
+ }
+
private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) {
MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
out.selector = SELF_METHOD_NAME;
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
index 17fc5e09..4b094a9f 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
@@ -94,6 +94,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
buildField.modifiers = ClassFileConstants.AccPrivate;
buildField.declarationSourceEnd = -1;
buildField.type = type;
+
data.setGeneratedByRecursive(buildField);
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
@@ -117,6 +118,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
md.returnType = returnType;
md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+ data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
index c7315790..11314bd3 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
@@ -85,6 +85,7 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
buildField.modifiers = ClassFileConstants.AccPrivate;
buildField.declarationSourceEnd = -1;
buildField.type = type;
+
data.setGeneratedByRecursive(buildField);
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
@@ -117,6 +118,8 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement};
md.returnType = returnType;
md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
@@ -149,7 +152,6 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
data.setGeneratedByRecursive(md);
-
HandleNonNull.INSTANCE.fix(injectMethod(builderType, md));
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
index 174cd5fc..55f6cadd 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
@@ -127,6 +127,7 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
buildValueField.declarationSourceEnd = -1;
buildValueField.type = type;
}
+
data.setGeneratedByRecursive(buildKeyField);
data.setGeneratedByRecursive(buildValueField);
EclipseNode keyFieldNode = injectFieldAndMarkGenerated(builderType, buildKeyField);
@@ -174,6 +175,7 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
md.returnType = returnType;
md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+ data.setGeneratedByRecursive(md);
injectMethod(builderType, md);
}
diff --git a/src/core/lombok/experimental/SuperBuilder.java b/src/core/lombok/experimental/SuperBuilder.java
index 26127a5f..be6ea304 100644
--- a/src/core/lombok/experimental/SuperBuilder.java
+++ b/src/core/lombok/experimental/SuperBuilder.java
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2018 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
@@ -43,7 +43,7 @@ import lombok.Singular;
* The builder also has a <code>build()</code> method which returns a completed instance of the original type.
* <p>
* Complete documentation is found at <a href="https://projectlombok.org/features/experimental/SuperBuilder">the project lombok features page for &#64;SuperBuilder</a>.
- *
+ *
* @see Singular
*/
@Target(TYPE)
@@ -51,16 +51,15 @@ import lombok.Singular;
public @interface SuperBuilder {
/** @return Name of the method that creates a new builder instance. Default: {@code builder}. */
String builderMethodName() default "builder";
-
+
/** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
String buildMethodName() default "build";
-
- // toBuilder also requires a two-stage system where each class gets its own toBuilder but calls on a second method (and also calls parentclass's method)
- // to fill the builder, as this class does not know what fields to pass on to the builder. Let's consider this, but only for milestone 2.
- /*
- * If true, generate an instance method to obtain a builder that is initialized with the values of this instance.
- *
+
+ /**
+ * If <code>true</code>, generate an instance method to obtain a builder that is initialized with the values of this instance.
+ * In this case, all superclasses must also have <code>toBuilder=true</code>.
+ *
* @return Whether to generate a {@code toBuilder()} method.
*/
-// boolean toBuilder() default false;
+ boolean toBuilder() default false;
}
diff --git a/src/core/lombok/javac/CompilerMessageSuppressor.java b/src/core/lombok/javac/CompilerMessageSuppressor.java
index 391ec64a..02dc6c26 100644
--- a/src/core/lombok/javac/CompilerMessageSuppressor.java
+++ b/src/core/lombok/javac/CompilerMessageSuppressor.java
@@ -39,6 +39,8 @@ import com.sun.tools.javac.util.JCDiagnostic;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
+import lombok.permit.Permit;
+
/**
* During resolution, the resolver will emit resolution errors, but without appropriate file names and line numbers. If these resolution errors stick around
* then they will be generated AGAIN, this time with proper names and line numbers, at the end. Therefore, we want to suppress the logger.
@@ -89,11 +91,8 @@ public final class CompilerMessageSuppressor {
static Field getDeclaredField(Class<?> c, String fieldName) {
try {
- Field field = c.getDeclaredField(fieldName);
- field.setAccessible(true);
- return field;
- }
- catch (Throwable t) {
+ return Permit.getField(c, fieldName);
+ } catch (Throwable t) {
return null;
}
}
diff --git a/src/core/lombok/javac/Javac6BasedLombokOptions.java b/src/core/lombok/javac/Javac6BasedLombokOptions.java
index cefb89ff..fc73181c 100644
--- a/src/core/lombok/javac/Javac6BasedLombokOptions.java
+++ b/src/core/lombok/javac/Javac6BasedLombokOptions.java
@@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import lombok.Lombok;
+import lombok.permit.Permit;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Options;
@@ -36,8 +37,8 @@ public class Javac6BasedLombokOptions extends LombokOptions {
static {
try {
Class<?> optionNameClass = Class.forName("com.sun.tools.javac.main.OptionName");
- optionName_valueOf = optionNameClass.getMethod("valueOf", String.class);
- options_put = Class.forName("com.sun.tools.javac.util.Options").getMethod("put", optionNameClass, String.class);
+ optionName_valueOf = Permit.getMethod(optionNameClass, "valueOf", String.class);
+ options_put = Permit.getMethod(Class.forName("com.sun.tools.javac.util.Options"), "put", optionNameClass, String.class);
} catch (Exception e) {
throw new IllegalArgumentException("Can't initialize Javac6-based lombok options due to reflection issue.", e);
}
diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java
index 091612cc..9a5305a6 100644
--- a/src/core/lombok/javac/JavacAST.java
+++ b/src/core/lombok/javac/JavacAST.java
@@ -37,6 +37,7 @@ import javax.tools.JavaFileObject;
import com.sun.tools.javac.util.JCDiagnostic;
import lombok.core.AST;
+import lombok.permit.Permit;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.code.Symtab;
@@ -243,7 +244,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
private static List<JCTree> getResourcesForTryNode(JCTry tryNode) {
if (!JCTRY_RESOURCES_FIELD_INITIALIZED) {
try {
- JCTRY_RESOURCES_FIELD = JCTry.class.getField("resources");
+ JCTRY_RESOURCES_FIELD = Permit.getField(JCTry.class, "resources");
} catch (NoSuchFieldException ignore) {
// Java 1.6 or lower won't have this at all.
} catch (Exception ignore) {
@@ -343,7 +344,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return m;
}
try {
- m = c.getMethod("getBody");
+ m = Permit.getMethod(c, "getBody");
} catch (NoSuchMethodException e) {
throw Javac.sneakyThrow(e);
}
@@ -501,12 +502,11 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
static ErrorLog create(Messager messager, Log log) {
- Field errorCount = null;
- try {
- Field f = messager.getClass().getDeclaredField("errorCount");
- f.setAccessible(true);
- errorCount = f;
- } catch (Throwable t) {}
+ Field errorCount; try {
+ errorCount = Permit.getField(messager.getClass(), "errorCount");
+ } catch (Throwable t) {
+ errorCount = null;
+ }
boolean hasMultipleErrors = false;
for (Field field : log.getClass().getFields()) {
if (field.getName().equals("multipleErrors")) {
@@ -515,14 +515,13 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
}
if (hasMultipleErrors) return new JdkBefore9(log, messager, errorCount);
-
- Field warningCount = null;
- try {
- Field f = messager.getClass().getDeclaredField("warningCount");
- f.setAccessible(true);
- warningCount = f;
- } catch (Throwable t) {}
-
+
+ Field warningCount; try {
+ warningCount = Permit.getField(messager.getClass(), "warningCount");
+ } catch (Throwable t) {
+ warningCount = null;
+ }
+
return new Jdk9Plus(log, messager, errorCount, warningCount);
}
}
@@ -577,21 +576,18 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
Class<?> noteCls = Class.forName(jcd + "$Note");
Class<?> lc = log.getClass();
- this.errorMethod = lc.getMethod("error", df, DiagnosticPosition.class, errorCls);
- this.warningMethod = lc.getMethod("warning", DiagnosticPosition.class, warningCls);
- this.mandatoryWarningMethod = lc.getMethod("mandatoryWarning", DiagnosticPosition.class, warningCls);
- this.noteMethod = lc.getMethod("note", DiagnosticPosition.class, noteCls);
+ this.errorMethod = Permit.getMethod(lc, "error", df, DiagnosticPosition.class, errorCls);
+ this.warningMethod = Permit.getMethod(lc, "warning", DiagnosticPosition.class, warningCls);
+ this.mandatoryWarningMethod = Permit.getMethod(lc, "mandatoryWarning", DiagnosticPosition.class, warningCls);
+ this.noteMethod = Permit.getMethod(lc, "note", DiagnosticPosition.class, noteCls);
- Field diagsField = lc.getSuperclass().getDeclaredField("diags");
- diagsField.setAccessible(true);
- this.diags = (JCDiagnostic.Factory)diagsField.get(log);
+ Field diagsField = Permit.getField(lc.getSuperclass(), "diags");
+ this.diags = (JCDiagnostic.Factory) diagsField.get(log);
Class<?> dc = this.diags.getClass();
- this.errorKey = dc.getMethod("errorKey", String.class, Object[].class);
- this.warningKey = dc.getDeclaredMethod("warningKey", String.class, Object[].class);
- this.warningKey.setAccessible(true);
- this.noteKey = dc.getDeclaredMethod("noteKey", String.class, Object[].class);
- this.noteKey.setAccessible(true);
+ this.errorKey = Permit.getMethod(dc, "errorKey", String.class, Object[].class);
+ this.warningKey = Permit.getMethod(dc, "warningKey", String.class, Object[].class);
+ this.noteKey = Permit.getMethod(dc, "noteKey", String.class, Object[].class);
} catch (Throwable t) {
//t.printStackTrace();
}
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
index 8cc239e1..abbf6726 100644
--- a/src/core/lombok/javac/JavacResolution.java
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -35,6 +35,7 @@ import javax.tools.JavaFileObject;
import lombok.Lombok;
import lombok.core.debug.AssertionLogger;
+import lombok.permit.Permit;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
@@ -162,14 +163,10 @@ public class JavacResolution {
private static Field getMemberEnterDotEnv() {
if (memberEnterDotEnv != null) return memberEnterDotEnv;
try {
- Field f = MemberEnter.class.getDeclaredField("env");
- f.setAccessible(true);
- memberEnterDotEnv = f;
+ return memberEnterDotEnv = Permit.getField(MemberEnter.class, "env");
} catch (NoSuchFieldException e) {
return null;
}
-
- return memberEnterDotEnv;
}
@SuppressWarnings("unchecked")
@@ -252,10 +249,10 @@ public class JavacResolution {
static {
Method upperBound = null;
try {
- upperBound = Types.class.getMethod("upperBound", Type.class);
+ upperBound = Permit.getMethod(Types.class, "upperBound", Type.class);
} catch (Throwable ignore) {}
if (upperBound == null) try {
- upperBound = Types.class.getMethod("wildUpperBound", Type.class);
+ upperBound = Permit.getMethod(Types.class, "wildUpperBound", Type.class);
} catch (Throwable ignore) {}
UPPER_BOUND = upperBound;
diff --git a/src/core/lombok/javac/apt/LombokFileObjects.java b/src/core/lombok/javac/apt/LombokFileObjects.java
index aba10540..28d3c9fa 100644
--- a/src/core/lombok/javac/apt/LombokFileObjects.java
+++ b/src/core/lombok/javac/apt/LombokFileObjects.java
@@ -39,6 +39,7 @@ import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import lombok.core.DiagnosticsReceiver;
+import lombok.permit.Permit;
import com.sun.tools.javac.file.BaseFileManager;
@@ -87,16 +88,14 @@ final class LombokFileObjects {
}
static Method getDecoderMethod(String className) {
- Method m = null;
try {
- m = Class.forName(className).getDeclaredMethod("getDecoder", boolean.class);
- m.setAccessible(true);
+ return Permit.getMethod(Class.forName(className), "getDecoder", boolean.class);
} catch (NoSuchMethodException e) {
// Intentional fallthrough - getDecoder(boolean) is not always present.
} catch (ClassNotFoundException e) {
// Intentional fallthrough - getDecoder(boolean) is not always present.
}
- return m;
+ return null;
}
private LombokFileObjects() {}
diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java
index 247d0560..a3d1dfcf 100644
--- a/src/core/lombok/javac/apt/LombokProcessor.java
+++ b/src/core/lombok/javac/apt/LombokProcessor.java
@@ -52,6 +52,7 @@ import javax.tools.JavaFileObject;
import lombok.Lombok;
import lombok.core.DiagnosticsReceiver;
import lombok.javac.JavacTransformer;
+import lombok.permit.Permit;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
@@ -112,10 +113,7 @@ public class LombokProcessor extends AbstractProcessor {
private static final Field getFieldAccessor(String typeName, String fieldName) {
try {
- Class<?> c = Class.forName(typeName);
- Field f = c.getDeclaredField(fieldName);
- f.setAccessible(true);
- return f;
+ return Permit.getField(Class.forName(typeName), fieldName);
} catch (ClassNotFoundException e) {
return null;
} catch (NoSuchFieldException e) {
@@ -156,11 +154,9 @@ public class LombokProcessor extends AbstractProcessor {
Context context = javacProcessingEnv.getContext();
disablePartialReparseInNetBeansEditor(context);
try {
- Method keyMethod = Context.class.getDeclaredMethod("key", Class.class);
- keyMethod.setAccessible(true);
+ Method keyMethod = Permit.getMethod(Context.class, "key", Class.class);
Object key = keyMethod.invoke(context, JavaFileManager.class);
- Field htField = Context.class.getDeclaredField("ht");
- htField.setAccessible(true);
+ Field htField = Permit.getField(Context.class, "ht");
@SuppressWarnings("unchecked")
Map<Object,Object> ht = (Map<Object,Object>) htField.get(context);
final JavaFileManager originalFiler = (JavaFileManager) ht.get(key);
@@ -170,8 +166,7 @@ public class LombokProcessor extends AbstractProcessor {
JavaFileManager newFilerManager = new InterceptingJavaFileManager(originalFiler, receiver);
ht.put(key, newFilerManager);
- Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager");
- filerFileManagerField.setAccessible(true);
+ Field filerFileManagerField = Permit.getField(JavacFiler.class, "fileManager");
filerFileManagerField.set(javacFiler, newFilerManager);
if (lombok.javac.Javac.getJavaCompilerVersion() > 8
@@ -186,20 +181,17 @@ public class LombokProcessor extends AbstractProcessor {
private void replaceFileManagerJdk9(Context context, JavaFileManager newFiler) {
try {
- JavaCompiler compiler = (JavaCompiler) JavaCompiler.class.getDeclaredMethod("instance", Context.class).invoke(null, context);
+ JavaCompiler compiler = (JavaCompiler) Permit.getMethod(JavaCompiler.class, "instance", Context.class).invoke(null, context);
try {
- Field fileManagerField = JavaCompiler.class.getDeclaredField("fileManager");
- fileManagerField.setAccessible(true);
+ Field fileManagerField = Permit.getField(JavaCompiler.class, "fileManager");
fileManagerField.set(compiler, newFiler);
}
catch (Exception e) {}
try {
- Field writerField = JavaCompiler.class.getDeclaredField("writer");
- writerField.setAccessible(true);
+ Field writerField = Permit.getField(JavaCompiler.class, "writer");
ClassWriter writer = (ClassWriter) writerField.get(compiler);
- Field fileManagerField = ClassWriter.class.getDeclaredField("fileManager");
- fileManagerField.setAccessible(true);
+ Field fileManagerField = Permit.getField(ClassWriter.class, "fileManager");
fileManagerField.set(writer, newFiler);
}
catch (Exception e) {}
@@ -210,8 +202,7 @@ public class LombokProcessor extends AbstractProcessor {
private void forceMultipleRoundsInNetBeansEditor() {
try {
- Field f = JavacProcessingEnvironment.class.getDeclaredField("isBackgroundCompilation");
- f.setAccessible(true);
+ Field f = Permit.getField(JavacProcessingEnvironment.class, "isBackgroundCompilation");
f.set(javacProcessingEnv, true);
} catch (NoSuchFieldException e) {
// only NetBeans has it
@@ -223,14 +214,12 @@ public class LombokProcessor extends AbstractProcessor {
private void disablePartialReparseInNetBeansEditor(Context context) {
try {
Class<?> cancelServiceClass = Class.forName("com.sun.tools.javac.util.CancelService");
- Method cancelServiceInstace = cancelServiceClass.getDeclaredMethod("instance", Context.class);
+ Method cancelServiceInstace = Permit.getMethod(cancelServiceClass, "instance", Context.class);
Object cancelService = cancelServiceInstace.invoke(null, context);
if (cancelService == null) return;
- Field parserField = cancelService.getClass().getDeclaredField("parser");
- parserField.setAccessible(true);
+ Field parserField = Permit.getField(cancelService.getClass(), "parser");
Object parser = parserField.get(cancelService);
- Field supportsReparseField = parser.getClass().getDeclaredField("supportsReparse");
- supportsReparseField.setAccessible(true);
+ Field supportsReparseField = Permit.getField(parser.getClass(), "supportsReparse");
supportsReparseField.set(parser, false);
} catch (ClassNotFoundException e) {
// only NetBeans has it
@@ -284,8 +273,7 @@ public class LombokProcessor extends AbstractProcessor {
private void stopJavacProcessingEnvironmentFromClosingOurClassloader() {
try {
- Field f = JavacProcessingEnvironment.class.getDeclaredField("processorClassLoader");
- f.setAccessible(true);
+ Field f = Permit.getField(JavacProcessingEnvironment.class, "processorClassLoader");
ClassLoader unwrapped = (ClassLoader) f.get(javacProcessingEnv);
if (unwrapped == null) return;
ClassLoader wrapped = wrapClassLoader(unwrapped);
@@ -377,7 +365,7 @@ public class LombokProcessor extends AbstractProcessor {
} catch (Exception e) {
e.printStackTrace();
processingEnv.getMessager().printMessage(Kind.WARNING,
- "Can't force a new processing round. Lombok won't work.");
+ "Can't force a new processing round. Lombok won't work.");
}
}
}
@@ -414,16 +402,14 @@ public class LombokProcessor extends AbstractProcessor {
@Override public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
-
+
/**
* This class casts the given processing environment to a JavacProcessingEnvironment. In case of
* gradle incremental compilation, the delegate ProcessingEnvironment of the gradle wrapper is returned.
*/
public JavacProcessingEnvironment getJavacProcessingEnvironment(Object procEnv) {
- if (procEnv instanceof JavacProcessingEnvironment) {
- return (JavacProcessingEnvironment) procEnv;
- }
-
+ if (procEnv instanceof JavacProcessingEnvironment) return (JavacProcessingEnvironment) procEnv;
+
// try to find a "delegate" field in the object, and use this to try to obtain a JavacProcessingEnvironment
for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) {
try {
@@ -432,9 +418,9 @@ public class LombokProcessor extends AbstractProcessor {
// delegate field was not found, try on superclass
}
}
-
+
processingEnv.getMessager().printMessage(Kind.WARNING,
- "Can't get the delegate of the gradle IncrementalProcessingEnvironment. Lombok won't work.");
+ "Can't get the delegate of the gradle IncrementalProcessingEnvironment. Lombok won't work.");
return null;
}
@@ -444,10 +430,8 @@ public class LombokProcessor extends AbstractProcessor {
* (directly or through a delegate field again)
*/
public JavacFiler getJavacFiler(Object filer) {
- if (filer instanceof JavacFiler) {
- return (JavacFiler) filer;
- }
-
+ if (filer instanceof JavacFiler) return (JavacFiler) filer;
+
// try to find a "delegate" field in the object, and use this to check for a JavacFiler
for (Class<?> filerClass = filer.getClass(); filerClass != null; filerClass = filerClass.getSuperclass()) {
try {
@@ -456,15 +440,13 @@ public class LombokProcessor extends AbstractProcessor {
// delegate field was not found, try on superclass
}
}
-
+
processingEnv.getMessager().printMessage(Kind.WARNING,
- "Can't get a JavacFiler from " + filer.getClass().getName() + ". Lombok won't work.");
+ "Can't get a JavacFiler from " + filer.getClass().getName() + ". Lombok won't work.");
return null;
}
private Object tryGetDelegateField(Class<?> delegateClass, Object instance) throws Exception {
- Field field = delegateClass.getDeclaredField("delegate");
- field.setAccessible(true);
- return field.get(instance);
+ return Permit.getField(delegateClass, "delegate").get(instance);
}
}
diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java
index 7a187148..f15bf74c 100644
--- a/src/core/lombok/javac/apt/Processor.java
+++ b/src/core/lombok/javac/apt/Processor.java
@@ -53,6 +53,8 @@ import com.sun.tools.javac.processing.JavacFiler;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.util.Options;
+import lombok.permit.Permit;
+
/**
* This processor should not be used. It used to be THE processor. This class is only there to warn people that something went wrong, and for the
* lombok developers to see if what the reason for those failures is.
@@ -101,8 +103,7 @@ public class Processor extends AbstractProcessor {
try {
JavacProcessingEnvironment environment = (JavacProcessingEnvironment) procEnv;
Options instance = Options.instance(environment.getContext());
- Field field = Options.class.getDeclaredField("values");
- field.setAccessible(true);
+ Field field = Permit.getField(Options.class, "values");
@SuppressWarnings("unchecked") Map<String, String> values = (Map<String, String>) field.get(instance);
if (values.isEmpty()) {
message.append("Options: empty\n\n");
@@ -125,8 +126,7 @@ public class Processor extends AbstractProcessor {
private void findServices(StringBuilder message, Filer filer) {
try {
- Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager");
- filerFileManagerField.setAccessible(true);
+ Field filerFileManagerField = Permit.getField(JavacFiler.class, "fileManager");
JavaFileManager jfm = (JavaFileManager) filerFileManagerField.get(filer);
ClassLoader processorClassLoader = jfm.hasLocation(ANNOTATION_PROCESSOR_PATH) ? jfm.getClassLoader(ANNOTATION_PROCESSOR_PATH) : jfm.getClassLoader(CLASS_PATH);
Enumeration<URL> resources = processorClassLoader.getResources("META-INF/services/javax.annotation.processing.Processor");
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index 903844d2..0f6e21d0 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -84,7 +84,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return ((Boolean) expr).booleanValue();
}
- private static class BuilderFieldData {
+ static class BuilderFieldData {
List<JCAnnotation> annotations;
JCExpression type;
Name rawName;
@@ -484,6 +484,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
sb.append("__ERR__");
}
+ private static final String BUILDER_TEMP_VAR = "builder";
private JCMethodDecl generateToBuilderMethod(String toBuilderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, boolean fluent, JCAnnotation ast) {
// return new ThingieBuilder<A, B>().setA(this.a).setB(this.b);
JavacTreeMaker maker = type.getTreeMaker();
@@ -495,6 +496,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCExpression>nil(), null);
JCExpression invoke = call;
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
for (BuilderFieldData bfd : builderFields) {
Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString()));
JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2];
@@ -519,19 +521,21 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCExpression arg;
if (bfd.singularData == null) {
arg = tgt[0];
+ invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg));
} else {
- JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null));
- // Use the singularizer to create the type args (will remove possible wildcards on the type).
- List<JCExpression> tas = bfd.singularData.getSingularizer().createTypeArgs(bfd.singularData.getTypeArgs().size(), false, type, bfd.singularData.getTypeArgs(), type.get());
- JCExpression emptyList = maker.Apply(tas, chainDots(type, "java", "util", "Collections", "emptyList"), List.<JCExpression>nil());
- arg = maker.Conditional(eqNull, emptyList, tgt[1]);
+ JCExpression isNotNull = maker.Binary(CTC_NOT_EQUAL, tgt[0], maker.Literal(CTC_BOT, null));
+ JCExpression invokeBuilder = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_TEMP_VAR)), setterName), List.<JCExpression>of(tgt[1]));
+ statements.append(maker.If(isNotNull, maker.Exec(invokeBuilder), null));
}
-
- invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg));
}
- JCStatement statement = maker.Return(invoke);
-
- JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ if (!statements.isEmpty()) {
+ JCExpression tempVarType = namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams);
+ statements.prepend(maker.VarDef(maker.Modifiers(Flags.FINAL), type.toName(BUILDER_TEMP_VAR), tempVarType, invoke));
+ statements.append(maker.Return(maker.Ident(type.toName(BUILDER_TEMP_VAR))));
+ } else {
+ statements.append(maker.Return(invoke));
+ }
+ JCBlock body = maker.Block(0, statements.toList());
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
diff --git a/src/core/lombok/javac/handlers/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
index 66d6e47e..8af0a7c0 100644
--- a/src/core/lombok/javac/handlers/HandleSuperBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -27,6 +27,8 @@ import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.ArrayList;
+import javax.lang.model.element.Modifier;
+
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.BoundKind;
@@ -37,11 +39,13 @@ import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
@@ -61,6 +65,7 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.HandlerUtil.FieldAccess;
import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.experimental.NonFinal;
import lombok.experimental.SuperBuilder;
@@ -68,32 +73,23 @@ import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.HandleBuilder.BuilderFieldData;
import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker;
-import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
@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 HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
private static final String SELF_METHOD = "self";
-
- private static class BuilderFieldData {
- List<JCAnnotation> annotations;
- JCExpression type;
- Name rawName;
- Name name;
- Name nameOfDefaultProvider;
- Name nameOfSetFlag;
- SingularData singularData;
- ObtainVia obtainVia;
- JavacNode obtainViaNode;
- JavacNode originalFieldNode;
-
- java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
- }
-
+ private static final String TO_BUILDER_METHOD_NAME = "toBuilder";
+ private static final String FILL_VALUES_METHOD_NAME = "$fillValuesFrom";
+ private static final String STATIC_FILL_VALUES_METHOD_NAME = "$fillValuesFromInstanceIntoBuilder";
+ private static final String INSTANCE_VARIABLE_NAME = "instance";
+ private static final String BUILDER_VARIABLE_NAME = "b";
+
@Override
public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
@@ -110,10 +106,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
+ boolean toBuilder = superbuilderAnnotation.toBuilder();
+
JavacNode tdParent = annotationNode.up();
java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
- JCExpression returnType;
List<JCTypeParameter> typeParams = List.nil();
List<JCExpression> thrownExceptions = List.nil();
List<JCExpression> superclassTypeParams = List.nil();
@@ -192,7 +189,6 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
// If there is no superclass, superclassBuilderClassExpression is still == null at this point.
// You can use it to check whether to inherit or not.
- returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
// <C, B> are the generics for our builder.
@@ -231,10 +227,25 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
JavacNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
builderType = generateBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression,
- typeParams, superclassTypeParams, ast, classGenericName, builderGenericName);
+ typeParams, superclassTypeParams, classGenericName, builderGenericName);
} else {
- annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
- return;
+ JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get();
+ if (!builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)
+ || !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.ABSTRACT)) {
+ annotationNode.addError("Existing Builder must be an abstract static inner class.");
+ return;
+ }
+ 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;
+ }
+ }
}
// Generate the fields in the abstract builder class that hold the values for the instance.
@@ -245,6 +256,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
injectFieldAndMarkGenerated(builderType, uncleanField);
}
+ if (toBuilder) {
+ // Generate $fillValuesFrom() method in the abstract builder.
+ injectMethod(builderType, generateFillValuesMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName, classGenericName, builderClassName));
+ // Generate $fillValuesFromInstanceIntoBuilder() method in the builder implementation class.
+ injectMethod(builderType, generateStaticFillValuesMethod(tdParent, builderClassName, typeParams, builderFields));
+ }
+
// Generate abstract self() and build() methods in the abstract builder.
injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName));
injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null, classGenericName));
@@ -271,25 +289,33 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
- if ((td.mods.flags & Flags.ABSTRACT) == 0) {
+ boolean isAbstract = (td.mods.flags & Flags.ABSTRACT) != 0;
+ if (!isAbstract) {
// Only non-abstract classes get the Builder implementation.
// Create the builder implementation class.
JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName);
if (builderImplType == null) {
- builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast);
+ builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams);
} else {
- annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
- return;
+ JCClassDecl builderImplTypeDeclaration = (JCClassDecl) builderImplType.get();
+ if (!builderImplTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)
+ || builderImplTypeDeclaration.getModifiers().getFlags().contains(Modifier.ABSTRACT)) {
+ annotationNode.addError("Existing BuilderImpl must be a non-abstract static inner class.");
+ return;
+ }
+ sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderImplType, annotationNode);
}
-
+
// Create a simple constructor for the BuilderImpl class.
JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode);
if (cd != null) injectMethod(builderImplType, cd);
// Create the self() and build() methods in the BuilderImpl.
injectMethod(builderImplType, generateSelfMethod(builderImplType, typeParams));
- injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
+ if (methodExists(buildMethodName, builderImplType, -1) == MemberExistsResult.NOT_EXISTS) {
+ injectMethod(builderImplType, generateBuildMethod(buildMethodName, tdParent, builderImplType, thrownExceptions));
+ }
recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext());
}
@@ -298,15 +324,32 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
superclassBuilderClassExpression != null);
- if ((td.mods.flags & Flags.ABSTRACT) == 0) {
- // Only non-abstract classes get the Builder implementation and the builder() method.
+ if (isAbstract) {
+ // Only non-abstract classes get the builder() and toBuilder() methods.
+ return;
+ }
- // Add the builder() method to the annotated class.
- // Allow users to specify their own builder() methods, e.g., to provide default values.
- if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
- recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext());
- if (builderMethod != null) injectMethod(tdParent, builderMethod);
+ // Add the builder() method to the annotated class.
+ // Allow users to specify their own builder() methods, e.g., to provide default values.
+ if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
+ recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext());
+ if (builderMethod != null) injectMethod(tdParent, builderMethod);
+ }
+
+ // Add the toBuilder() method to the annotated class.
+ if (toBuilder) {
+ switch (methodExists(TO_BUILDER_METHOD_NAME, tdParent, 0)) {
+ case EXISTS_BY_USER:
+ annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+ return;
+ case NOT_EXISTS:
+ JCMethodDecl md = generateToBuilderMethod(builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
+ if (md != null) {
+ injectMethod(tdParent, md);
+ }
+ default:
+ // Should not happen.
}
}
}
@@ -316,7 +359,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
*/
private JavacNode generateBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass,
JCExpression superclassBuilderClassExpression, List<JCTypeParameter> typeParams,
- List<JCExpression> superclassTypeParams, JCAnnotation ast, String classGenericName, String builderGenericName) {
+ List<JCExpression> superclassTypeParams, String classGenericName, String builderGenericName) {
JavacTreeMaker maker = tdParent.getTreeMaker();
JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC);
@@ -358,7 +401,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
/**
* Creates and returns the concrete builder implementation class and injects it into the annotated class.
*/
- private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams, JCAnnotation ast) {
+ private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams) {
JavacTreeMaker maker = tdParent.getTreeMaker();
JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL);
@@ -410,7 +453,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
- Name builderVariableName = typeNode.toName("b");
+ Name builderVariableName = typeNode.toName(BUILDER_VARIABLE_NAME);
for (BuilderFieldData bfd : builderFields) {
JCExpression rhs;
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
@@ -496,6 +539,166 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
+ /**
+ * Generates a <code>toBuilder()</code> method in the annotated class that looks like this:
+ * <pre>
+ * public ParentBuilder&lt;?, ?&gt; toBuilder() {
+ * return new <i>Foobar</i>BuilderImpl().$fillValuesFrom(this);
+ * }
+ * </pre>
+ */
+ private JCMethodDecl generateToBuilderMethod(String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) {
+ JavacTreeMaker maker = type.getTreeMaker();
+
+ ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
+ for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name));
+
+ JCExpression newClass = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.<JCExpression>nil(), null);
+ List<JCExpression> methodArgs = List.<JCExpression>of(maker.Ident(type.toName("this")));
+ JCMethodInvocation invokeFillMethod = maker.Apply(List.<JCExpression>nil(), maker.Select(newClass, type.toName(FILL_VALUES_METHOD_NAME)), methodArgs);
+ JCStatement statement = maker.Return(invokeFillMethod);
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ int modifiers = Flags.PUBLIC;
+
+ // Add any type params of the annotated class to the return type.
+ ListBuffer<JCExpression> typeParameterNames = new ListBuffer<JCExpression>();
+ typeParameterNames.addAll(typeParameterNames(maker, typeParams));
+ // Now add the <?, ?>.
+ JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+ typeParameterNames.add(wildcard);
+ typeParameterNames.add(wildcard);
+ JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList());
+
+ return maker.MethodDef(maker.Modifiers(modifiers), type.toName(TO_BUILDER_METHOD_NAME), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
+ /**
+ * Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks
+ * like this:
+ * <pre>
+ * protected B $fillValuesFrom(final C instance) {
+ * super.$fillValuesFrom(instance);
+ * FoobarBuilderImpl.$fillValuesFromInstanceIntoBuilder(instance, this);
+ * return self();
+ * }
+ * </pre>
+ */
+ private JCMethodDecl generateFillValuesMethod(JavacNode type, boolean inherited, String builderGenericName, String classGenericName, String builderImplClassName) {
+ JavacTreeMaker maker = type.getTreeMaker();
+ List<JCAnnotation> annotations = List.nil();
+ if (inherited) {
+ JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil());
+ annotations = List.of(overrideAnnotation);
+ }
+ JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, annotations);
+ Name name = type.toName(FILL_VALUES_METHOD_NAME);
+ JCExpression returnType = maker.Ident(type.toName(builderGenericName));
+
+ JCExpression classGenericNameExpr = maker.Ident(type.toName(classGenericName));
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), classGenericNameExpr, null);
+
+ ListBuffer<JCStatement> body = new ListBuffer<JCStatement>();
+
+ if (inherited) {
+ // Call super.
+ JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
+ maker.Select(maker.Ident(type.toName("super")), name),
+ List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME))));
+ body.append(maker.Exec(callToSuper));
+ }
+
+ // Call the builder implemention's helper method that actually fills the values from the instance.
+ JCMethodInvocation callStaticFillValuesMethod = maker.Apply(List.<JCExpression>nil(),
+ maker.Select(maker.Ident(type.toName(builderImplClassName)), type.toName(STATIC_FILL_VALUES_METHOD_NAME)),
+ List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), maker.Ident(type.toName("this"))));
+ body.append(maker.Exec(callStaticFillValuesMethod));
+
+ JCReturn returnStatement = maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName(SELF_METHOD)), List.<JCExpression>nil()));
+ body.append(returnStatement);
+ JCBlock bodyBlock = maker.Block(0, body.toList());
+
+ return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.of(param), List.<JCExpression>nil(), bodyBlock, null);
+ }
+
+ /**
+ * Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in
+ * the builder implementation class that copies all fields from the instance
+ * to the builder. It looks like this:
+ *
+ * <pre>
+ * protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder&lt;?, ?&gt; b) {
+ * b.field(instance.field);
+ * }
+ * </pre>
+ */
+ private JCMethodDecl generateStaticFillValuesMethod(JavacNode type, String builderClassname, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields) {
+ JavacTreeMaker maker = type.getTreeMaker();
+ List<JCAnnotation> annotations = List.nil();
+ JCModifiers modifiers = maker.Modifiers(Flags.PRIVATE | Flags.STATIC, annotations);
+ Name name = type.toName(STATIC_FILL_VALUES_METHOD_NAME);
+ JCExpression returnType = maker.TypeIdent(CTC_VOID);
+
+ // 1st parameter: "Foobar instance"
+ JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(INSTANCE_VARIABLE_NAME), cloneSelfType(type), null);
+
+ // 2nd parameter: "FoobarBuilder<?, ?> b" (plus generics on the annotated type)
+ // First add all generics that are present on the parent type.
+ ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(typeParams, maker);
+ // Now add the <?, ?>.
+ JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+ typeParamsForBuilderParameter.add(wildcard);
+ wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+ typeParamsForBuilderParameter.add(wildcard);
+ JCTypeApply builderType = maker.TypeApply(maker.Ident(type.toName(builderClassname)), typeParamsForBuilderParameter.toList());
+ JCVariableDecl paramBuilder = maker.VarDef(maker.Modifiers(Flags.LocalVarFlags), type.toName(BUILDER_VARIABLE_NAME), builderType, null);
+
+ ListBuffer<JCStatement> body = new ListBuffer<JCStatement>();
+
+ // Call the builder's setter methods to fill the values from the instance.
+ for (BuilderFieldData bfd : builderFields) {
+ JCExpressionStatement exec = createSetterCallWithInstanceValue(bfd, type, maker);
+ body.append(exec);
+ }
+
+ JCBlock bodyBlock = maker.Block(0, body.toList());
+
+ return maker.MethodDef(modifiers, name, returnType, copyTypeParams(type, typeParams), List.of(paramInstance, paramBuilder), List.<JCExpression>nil(), bodyBlock, null);
+ }
+
+ private JCExpressionStatement createSetterCallWithInstanceValue(BuilderFieldData bfd, JavacNode type, JavacTreeMaker maker) {
+ JCExpression[] tgt = new JCExpression[bfd.singularData == null ? 1 : 2];
+ if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+ for (int i = 0; i < tgt.length; i++) {
+ tgt[i] = maker.Select(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field()));
+ }
+ } else {
+ if (bfd.obtainVia.isStatic()) {
+ for (int i = 0; i < tgt.length; i++) {
+ JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method()));
+ tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME))));
+ }
+ } else {
+ for (int i = 0; i < tgt.length; i++) {
+ JCExpression c = maker.Select(maker.Ident(type.toName(INSTANCE_VARIABLE_NAME)), type.toName(bfd.obtainVia.method()));
+ tgt[i] = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil());
+ }
+ }
+ }
+
+ JCExpression arg;
+ if (bfd.singularData == null) {
+ arg = tgt[0];
+ } else {
+ JCExpression eqNull = maker.Binary(CTC_EQUAL, tgt[0], maker.Literal(CTC_BOT, null));
+ JCExpression emptyList = maker.Apply(List.<JCExpression>nil(), chainDots(type, "java", "util", "Collections", "emptyList"), List.<JCExpression>nil());
+ arg = maker.Conditional(eqNull, emptyList, tgt[1]);
+ }
+ JCMethodInvocation apply = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(type.toName(BUILDER_VARIABLE_NAME)), bfd.name), List.of(arg));
+ JCExpressionStatement exec = maker.Exec(apply);
+ return exec;
+ }
+
private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) {
JavacTreeMaker maker = type.getTreeMaker();
List<JCAnnotation> annotations = List.nil();
@@ -506,7 +709,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations);
Name name = type.toName(SELF_METHOD);
JCExpression returnType = maker.Ident(type.toName(builderGenericName));
-
+
return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
}
@@ -538,7 +741,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
}
- private JCMethodDecl generateBuildMethod(String buildName, JCExpression returnType, JavacNode type, List<JCExpression> thrownExceptions) {
+ private JCMethodDecl generateBuildMethod(String buildName, JavacNode returnType, JavacNode type, List<JCExpression> thrownExceptions) {
JavacTreeMaker maker = type.getTreeMaker();
JCExpression call;
@@ -546,7 +749,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
// Use a constructor that only has this builder as parameter.
List<JCExpression> builderArg = List.<JCExpression>of(maker.Ident(type.toName("this")));
- call = maker.NewClass(null, List.<JCExpression>nil(), returnType, builderArg, null);
+ call = maker.NewClass(null, List.<JCExpression>nil(), cloneSelfType(returnType), builderArg, null);
statements.append(maker.Return(call));
JCBlock body = maker.Block(0, statements.toList());
@@ -554,7 +757,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil());
JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation));
- return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
+ return maker.MethodDef(modifiers, type.toName(buildName), cloneSelfType(returnType), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
@@ -618,13 +821,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}};
if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
- generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations);
+ generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, returnTypeMaker.make(), returnStatementMaker.make(), fieldNode.annotations);
} else {
fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker);
}
}
- private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam) {
+ private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, JCExpression returnType, JCStatement returnStatement, List<JCAnnotation> annosOnParam) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
for (JavacNode child : builderType.down()) {
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index e4e40095..c97c7ede 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -58,6 +58,7 @@ import lombok.experimental.Tolerate;
import lombok.javac.Javac;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
+import lombok.permit.Permit;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
@@ -1049,10 +1050,9 @@ public class JavacHandlerUtil {
if (TYPE != null) return;
if (!in.getName().equals("com.sun.tools.javac.tree.JCTree$JCAnnotatedType")) return;
try {
- CONSTRUCTOR = in.getDeclaredConstructor(List.class, JCExpression.class);
- CONSTRUCTOR.setAccessible(true);
- ANNOTATIONS = in.getDeclaredField("annotations");
- UNDERLYING_TYPE = in.getDeclaredField("underlyingType");
+ CONSTRUCTOR = Permit.getConstructor(in, List.class, JCExpression.class);
+ ANNOTATIONS = Permit.getField(in, "annotations");
+ UNDERLYING_TYPE = Permit.getField(in, "underlyingType");
TYPE = in;
} catch (Exception ignore) {}
}
@@ -1102,9 +1102,9 @@ public class JavacHandlerUtil {
Method r = null;
Method e = null;
try {
- f = ClassSymbol.class.getField("members_field");
- r = f.getType().getMethod("remove", Symbol.class);
- e = f.getType().getMethod("enter", Symbol.class);
+ f = Permit.getField(ClassSymbol.class, "members_field");
+ r = Permit.getMethod(f.getType(), "remove", Symbol.class);
+ e = Permit.getMethod(f.getType(), "enter", Symbol.class);
} catch (Exception ex) {}
membersField = f;
removeMethod = r;
diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java
index be0ce81c..69898668 100644
--- a/src/delombok/lombok/delombok/Delombok.java
+++ b/src/delombok/lombok/delombok/Delombok.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2017 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,14 +26,18 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
+import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
@@ -57,6 +61,7 @@ import lombok.javac.CommentCatcher;
import lombok.javac.Javac;
import lombok.javac.LombokOptions;
import lombok.javac.apt.LombokProcessor;
+import lombok.permit.Permit;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.comp.Todo;
@@ -88,7 +93,7 @@ public class Delombok {
private boolean noCopy;
private boolean onlyChanged;
private boolean force = false;
- private String classpath, sourcepath, bootclasspath;
+ private String classpath, sourcepath, bootclasspath, modulepath;
private LinkedHashMap<File, File> fileToBase = new LinkedHashMap<File, File>();
private List<File> filesToParse = new ArrayList<File>();
private Map<String, String> formatPrefs = new HashMap<String, String>();
@@ -138,6 +143,10 @@ public class Delombok {
@Description("override Bootclasspath (analogous to javac -bootclasspath option)")
private String bootclasspath;
+ @Description("Module path (analogous to javac --module-path option)")
+ @FullName("module-path")
+ private String modulepath;
+
@Description("Files to delombok. Provide either a file, or a directory. If you use a directory, all files in it (recursive) are delombok-ed")
@Sequential
private List<String> input = new ArrayList<String>();
@@ -182,21 +191,50 @@ public class Delombok {
return out.toString();
}
+ static String getPathOfSelf() {
+ String url = Delombok.class.getResource("Delombok.class").toString();
+ if (url.endsWith("lombok/delombok/Delombok.class")) {
+ url = urlDecode(url.substring(0, url.length() - "lombok/delombok/Delombok.class".length()));
+ } else if (url.endsWith("lombok/delombok/Delombok.SCL.lombok")) {
+ url = urlDecode(url.substring(0, url.length() - "lombok/delombok/Delombok.SCL.lombok".length()));
+ } else {
+ return null;
+ }
+ if (url.startsWith("jar:file:") && url.endsWith("!/")) return url.substring(9, url.length() - 2);
+ if (url.startsWith("file:")) return url.substring(5);
+ return null;
+ }
+
+ private static String urlDecode(String in) {
+ try {
+ return URLDecoder.decode(in, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError("UTF-8 not supported");
+ }
+ }
+
public static void main(String[] rawArgs) {
+ try {
+ rawArgs = fileExpand(rawArgs);
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ System.exit(1);
+ }
+
CmdReader<CmdArgs> reader = CmdReader.of(CmdArgs.class);
CmdArgs args;
try {
args = reader.make(rawArgs);
} catch (InvalidCommandLineException e) {
System.err.println("ERROR: " + e.getMessage());
- System.err.println(reader.generateCommandLineHelp("delombok"));
+ System.err.println(cmdHelp(reader));
System.exit(1);
return;
}
if (args.help || (args.input.isEmpty() && !args.formatHelp)) {
if (!args.help) System.err.println("ERROR: no files or directories to delombok specified.");
- System.err.println(reader.generateCommandLineHelp("delombok"));
+ System.err.println(cmdHelp(reader));
System.exit(args.help ? 0 : 1);
return;
}
@@ -253,6 +291,7 @@ public class Delombok {
if (args.classpath != null) delombok.setClasspath(args.classpath);
if (args.sourcepath != null) delombok.setSourcepath(args.sourcepath);
if (args.bootclasspath != null) delombok.setBootclasspath(args.bootclasspath);
+ if (args.modulepath != null) delombok.setModulepath(args.modulepath);
try {
for (String in : args.input) {
@@ -273,15 +312,123 @@ public class Delombok {
if (!args.quiet) {
String msg = e.getMessage();
if (msg != null && msg.startsWith("DELOMBOK: ")) System.err.println(msg.substring("DELOMBOK: ".length()));
- else {
- e.printStackTrace();
- }
+ else e.printStackTrace();
System.exit(1);
return;
}
}
}
+ private static String cmdHelp(CmdReader<CmdArgs> reader) {
+ String x = reader.generateCommandLineHelp("delombok");
+ int idx = x.indexOf('\n');
+ return x.substring(0, idx) + "\n You can use @filename.args to read arguments from the file 'filename.args'.\n" + x.substring(idx);
+ }
+
+ private static String[] fileExpand(String[] rawArgs) throws IOException {
+ String[] out = rawArgs;
+ int offset = 0;
+ for (int i = 0; i < rawArgs.length; i++) {
+ if (rawArgs[i].length() > 0 && rawArgs[i].charAt(0) == '@') {
+ String[] parts = readArgsFromFile(rawArgs[i].substring(1));
+ String[] newOut = new String[out.length + parts.length - 1];
+ System.arraycopy(out, 0, newOut, 0, i + offset);
+ System.arraycopy(parts, 0, newOut, i + offset, parts.length);
+ System.arraycopy(out, i + offset + 1, newOut, i + offset + parts.length, out.length - (i + offset + 1));
+ offset += parts.length - 1;
+ out = newOut;
+ }
+ }
+
+ return out;
+ }
+
+ private static String[] readArgsFromFile(String file) throws IOException {
+ InputStream in = new FileInputStream(file);
+ StringBuilder s = new StringBuilder();
+ try {
+ InputStreamReader isr = new InputStreamReader(in, "UTF-8");
+ char[] c = new char[4096];
+ while (true) {
+ int r = isr.read(c);
+ if (r == -1) break;
+ s.append(c, 0, r);
+ }
+ } finally {
+ in.close();
+ }
+
+ List<String> x = new ArrayList<String>();
+ StringBuilder a = new StringBuilder();
+ int state = 1;
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (state < 0) {
+ state = -state;
+ if (c != '\n') a.append(c);
+ continue;
+ }
+ if (state == 1) {
+ if (c == '\\') {
+ state = -1;
+ continue;
+ }
+ if (c == '"') {
+ state = 2;
+ continue;
+ }
+ if (c == '\'') {
+ state = 3;
+ continue;
+ }
+ if (Character.isWhitespace(c)) {
+ String aa = a.toString();
+ if (!aa.isEmpty()) x.add(aa);
+ a.setLength(0);
+ continue;
+ }
+ a.append(c);
+ continue;
+ }
+ if (state == 2) {
+ if (c == '\\') {
+ state = -2;
+ continue;
+ }
+ if (c == '"') {
+ state = 1;
+ x.add(a.toString());
+ a.setLength(0);
+ continue;
+ }
+ a.append(c);
+ continue;
+ }
+ if (state == 3) {
+ if (c == '\'') {
+ state = 1;
+ x.add(a.toString());
+ a.setLength(0);
+ continue;
+ }
+ a.append(c);
+ continue;
+ }
+ }
+ if (state == 1) {
+ String aa = a.toString();
+ if (!aa.isEmpty()) x.add(aa);
+ } else if (state < 0) {
+ throw new IOException("Unclosed backslash escape in @ file");
+ } else if (state == 2) {
+ throw new IOException("Unclosed \" in @ file");
+ } else if (state == 3) {
+ throw new IOException("Unclosed ' in @ file");
+ }
+
+ return x.toArray(new String[x.size()]);
+ }
+
public static class InvalidFormatOptionException extends Exception {
public InvalidFormatOptionException(String msg) {
super(msg);
@@ -385,6 +532,10 @@ public class Delombok {
this.output = null;
}
+ public void setModulepath(String modulepath) {
+ this.modulepath = modulepath;
+ }
+
public void addDirectory(File base) throws IOException {
addDirectory0(false, base, "", 0);
}
@@ -488,7 +639,7 @@ public class Delombok {
private static final Field MODULE_FIELD = getModuleField();
private static Field getModuleField() {
try {
- return JCCompilationUnit.class.getField("modle");
+ return Permit.getField(JCCompilationUnit.class, "modle");
} catch (NoSuchFieldException e) {
return null;
} catch (SecurityException e) {
@@ -525,9 +676,19 @@ public class Delombok {
argsList.add("-encoding");
argsList.add(charset.name());
}
+ String pathToSelfJar = getPathOfSelf();
+ if (pathToSelfJar != null) {
+ argsList.add("--module-path");
+ argsList.add((modulepath == null || modulepath.isEmpty()) ? pathToSelfJar : (pathToSelfJar + File.pathSeparator + modulepath));
+ } else if (modulepath != null && !modulepath.isEmpty()) {
+ argsList.add("--module-path");
+ argsList.add(modulepath);
+ }
String[] argv = argsList.toArray(new String[0]);
args.init("javac", argv);
options.put("diags.legacy", "TRUE");
+ } else {
+ if (modulepath != null && !modulepath.isEmpty()) throw new IllegalStateException("DELOMBOK: Option --module-path requires usage of JDK9 or higher.");
}
CommentCatcher catcher = CommentCatcher.create(context);
@@ -648,10 +809,10 @@ public class Delombok {
private static Object callAttributeMethodOnJavaCompiler(JavaCompiler compiler, Todo arg) {
if (attributeMethod == null) {
try {
- attributeMethod = JavaCompiler.class.getDeclaredMethod("attribute", java.util.Queue.class);
+ attributeMethod = Permit.getMethod(JavaCompiler.class, "attribute", java.util.Queue.class);
} catch (NoSuchMethodException e) {
try {
- attributeMethod = JavaCompiler.class.getDeclaredMethod("attribute", com.sun.tools.javac.util.ListBuffer.class);
+ attributeMethod = Permit.getMethod(JavaCompiler.class, "attribute", com.sun.tools.javac.util.ListBuffer.class);
} catch (NoSuchMethodException e2) {
throw Lombok.sneakyThrow(e2);
}
@@ -670,10 +831,10 @@ public class Delombok {
private static void callFlowMethodOnJavaCompiler(JavaCompiler compiler, Object arg) {
if (flowMethod == null) {
try {
- flowMethod = JavaCompiler.class.getDeclaredMethod("flow", java.util.Queue.class);
+ flowMethod = Permit.getMethod(JavaCompiler.class, "flow", java.util.Queue.class);
} catch (NoSuchMethodException e) {
try {
- flowMethod = JavaCompiler.class.getDeclaredMethod("flow", com.sun.tools.javac.util.List.class);
+ flowMethod = Permit.getMethod(JavaCompiler.class, "flow", com.sun.tools.javac.util.List.class);
} catch (NoSuchMethodException e2) {
throw Lombok.sneakyThrow(e2);
}
diff --git a/src/delombok/lombok/delombok/DelombokApp.java b/src/delombok/lombok/delombok/DelombokApp.java
index 2ba4c6ed..da1975b4 100644
--- a/src/delombok/lombok/delombok/DelombokApp.java
+++ b/src/delombok/lombok/delombok/DelombokApp.java
@@ -36,6 +36,7 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import lombok.core.LombokApp;
+import lombok.permit.Permit;
import org.mangosdk.spi.ProviderFor;
@@ -52,7 +53,7 @@ public class DelombokApp extends LombokApp {
return 1;
}
try {
- loadDelombok(args).getMethod("main", String[].class).invoke(null, new Object[] {args.toArray(new String[0])});
+ Permit.getMethod(loadDelombok(args), "main", String[].class).invoke(null, new Object[] {args.toArray(new String[0])});
} catch (InvocationTargetException e1) {
Throwable t = e1.getCause();
if (t instanceof Error) throw (Error)t;
diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java
index 4261a558..3fb1f1b1 100644
--- a/src/delombok/lombok/delombok/PrettyPrinter.java
+++ b/src/delombok/lombok/delombok/PrettyPrinter.java
@@ -93,6 +93,7 @@ import com.sun.tools.javac.util.Position;
import lombok.javac.CommentInfo;
import lombok.javac.PackageName;
+import lombok.permit.Permit;
import lombok.javac.CommentInfo.EndConnection;
import lombok.javac.CommentInfo.StartConnection;
import lombok.javac.JavacTreeMaker.TreeTag;
@@ -1335,7 +1336,6 @@ public class PrettyPrinter extends JCTree.Visitor {
static {
getExtendsClause = getMethod(JCClassDecl.class, "getExtendsClause", new Class<?>[0]);
- getExtendsClause.setAccessible(true);
if (getJavaCompilerVersion() < 8) {
getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class);
@@ -1350,11 +1350,11 @@ public class PrettyPrinter extends JCTree.Visitor {
throw sneakyThrow(ex);
}
try {
- storeEndMethodTemp = endPosTable.getMethod("storeEnd", JCTree.class, int.class);
+ storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class);
} catch (NoSuchMethodException e) {
try {
endPosTable = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable");
- storeEndMethodTemp = endPosTable.getDeclaredMethod("storeEnd", JCTree.class, int.class);
+ storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class);
} catch (NoSuchMethodException ex) {
throw sneakyThrow(ex);
} catch (ClassNotFoundException ex) {
@@ -1363,13 +1363,13 @@ public class PrettyPrinter extends JCTree.Visitor {
}
storeEnd = storeEndMethodTemp;
}
- getEndPosition.setAccessible(true);
- storeEnd.setAccessible(true);
+ Permit.setAccessible(getEndPosition);
+ Permit.setAccessible(storeEnd);
}
private static Method getMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
try {
- return clazz.getMethod(name, paramTypes);
+ return Permit.getMethod(clazz, name, paramTypes);
} catch (NoSuchMethodException e) {
throw sneakyThrow(e);
}
@@ -1379,7 +1379,7 @@ public class PrettyPrinter extends JCTree.Visitor {
try {
Class<?>[] c = new Class[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]);
- return clazz.getMethod(name, c);
+ return Permit.getMethod(clazz, name, c);
} catch (NoSuchMethodException e) {
throw sneakyThrow(e);
} catch (ClassNotFoundException e) {
@@ -1418,11 +1418,10 @@ public class PrettyPrinter extends JCTree.Visitor {
Field f = c.get(fieldName);
if (f == null) {
try {
- f = tClass.getDeclaredField(fieldName);
+ f = Permit.getField(tClass, fieldName);
} catch (Exception e) {
return defaultValue;
}
- f.setAccessible(true);
c.put(fieldName, f);
}
diff --git a/src/delombok/lombok/delombok/ant/DelombokTask.java b/src/delombok/lombok/delombok/ant/DelombokTask.java
index 06bbe3e0..3828a5db 100644
--- a/src/delombok/lombok/delombok/ant/DelombokTask.java
+++ b/src/delombok/lombok/delombok/ant/DelombokTask.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 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
@@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.List;
import lombok.Lombok;
+import lombok.permit.Permit;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Location;
@@ -78,6 +79,7 @@ class Tasks {
private File fromDir, toDir;
private Path classpath;
private Path sourcepath;
+ private Path modulepath;
private boolean verbose;
private String encoding;
private Path path;
@@ -92,9 +94,7 @@ class Tasks {
}
public Path createClasspath() {
- if (classpath == null) {
- classpath = new Path(getProject());
- }
+ if (classpath == null) classpath = new Path(getProject());
return classpath.createPath();
}
@@ -111,9 +111,7 @@ class Tasks {
}
public Path createSourcepath() {
- if (sourcepath == null) {
- sourcepath = new Path(getProject());
- }
+ if (sourcepath == null) sourcepath = new Path(getProject());
return sourcepath.createPath();
}
@@ -121,6 +119,23 @@ class Tasks {
createSourcepath().setRefid(r);
}
+ public void setModulepath(Path modulepath) {
+ if (this.modulepath == null) {
+ this.modulepath = modulepath;
+ } else {
+ this.modulepath.append(modulepath);
+ }
+ }
+
+ public Path createModulepath() {
+ if (modulepath == null) modulepath = new Path(getProject());
+ return modulepath.createPath();
+ }
+
+ public void setModulepathRef(Reference r) {
+ createModulepath().setRefid(r);
+ }
+
public void setFrom(File dir) {
this.fromDir = dir;
}
@@ -162,8 +177,7 @@ class Tasks {
} catch (ClassNotFoundException e) {
// If we get here, it isn't, and we should use the shadowloader.
Class<?> launcherMain = Class.forName("lombok.launch.Main");
- Method m = launcherMain.getDeclaredMethod("createShadowClassLoader");
- m.setAccessible(true);
+ Method m = Permit.getMethod(launcherMain, "createShadowClassLoader");
shadowLoader = (ClassLoader) m.invoke(null);
}
}
@@ -180,11 +194,10 @@ class Tasks {
try {
Object instance = shadowLoadClass("lombok.delombok.ant.DelombokTaskImpl").newInstance();
- for(Field selfField : getClass().getDeclaredFields()) {
+ for (Field selfField : getClass().getDeclaredFields()) {
+ Permit.setAccessible(selfField);
if (selfField.isSynthetic() || Modifier.isStatic(selfField.getModifiers())) continue;
- Field otherField = instance.getClass().getDeclaredField(selfField.getName());
- otherField.setAccessible(true);
- selfField.setAccessible(true);
+ Field otherField = Permit.getField(instance.getClass(), selfField.getName());
if (selfField.getName().equals("formatOptions")) {
List<String> rep = new ArrayList<String>();
for (Format f : formatOptions) {
@@ -197,8 +210,7 @@ class Tasks {
}
}
- Method m = instance.getClass().getMethod("execute", Location.class);
- m.setAccessible(true);
+ Method m = Permit.getMethod(instance.getClass(), "execute", Location.class);
m.invoke(instance, loc);
} catch (InvocationTargetException e) {
throw Lombok.sneakyThrow(e.getCause());
diff --git a/src/delombok/lombok/delombok/ant/DelombokTaskImpl.java b/src/delombok/lombok/delombok/ant/DelombokTaskImpl.java
index 470819cd..bd2f93e7 100644
--- a/src/delombok/lombok/delombok/ant/DelombokTaskImpl.java
+++ b/src/delombok/lombok/delombok/ant/DelombokTaskImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 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
@@ -40,6 +40,7 @@ public class DelombokTaskImpl {
private File fromDir, toDir;
private Path classpath;
private Path sourcepath;
+ private Path modulepath;
private boolean verbose;
private String encoding;
private Path path;
@@ -60,6 +61,7 @@ public class DelombokTaskImpl {
if (classpath != null) delombok.setClasspath(classpath.toString());
if (sourcepath != null) delombok.setSourcepath(sourcepath.toString());
+ if (modulepath != null) delombok.setModulepath(modulepath.toString());
try {
delombok.setFormatPreferences(Delombok.formatOptionsToMap(formatOptions));
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
index c11a49cd..ace97a4d 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
@@ -33,6 +33,7 @@ import java.util.List;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.agent.PatchExtensionMethod.Extension;
import lombok.experimental.ExtensionMethod;
+import lombok.permit.Permit;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.internal.codeassist.InternalCompletionContext;
@@ -220,7 +221,7 @@ public class PatchExtensionMethodCompletionProposal {
return null;
}
}
-
+
private static Method accessMethod(Class<?> clazz, String methodName, Class<?> parameter) {
try {
return makeAccessible(clazz.getDeclaredMethod(methodName, parameter));
@@ -228,10 +229,9 @@ public class PatchExtensionMethodCompletionProposal {
return null;
}
}
-
+
private static <T extends AccessibleObject> T makeAccessible(T object) {
- object.setAccessible(true);
- return object;
+ return Permit.setAccessible(object);
}
}
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
index fee104d3..8d581819 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
@@ -43,6 +43,8 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import lombok.permit.Permit;
+
import java.lang.reflect.Field;
import static lombok.eclipse.Eclipse.poss;
@@ -196,8 +198,8 @@ public class PatchVal {
Field a = null, b = null;
try {
- a = LocalDeclaration.class.getDeclaredField("$initCopy");
- b = LocalDeclaration.class.getDeclaredField("$iterableCopy");
+ a = Permit.getField(LocalDeclaration.class, "$initCopy");
+ b = Permit.getField(LocalDeclaration.class, "$iterableCopy");
} catch (Throwable t) {
//ignore - no $initCopy exists when running in ecj.
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
index 99447bae..fc6e7de2 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
@@ -30,6 +30,7 @@ import java.lang.reflect.Method;
import java.util.List;
import lombok.Lombok;
+import lombok.permit.Permit;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.IExtendedModifier;
@@ -277,8 +278,7 @@ public class PatchValEclipse {
static {
Field f = null;
try {
- f = Name.class.getDeclaredField("index");
- f.setAccessible(true);
+ f = Permit.getField(Name.class, "index");
} catch (Throwable t) {
// Leave it null, in which case we don't set index. That'll result in error log messages but its better than crashing here.
}
@@ -308,24 +308,19 @@ public class PatchValEclipse {
Method h = null;
try {
- a = LocalDeclaration.class.getDeclaredField("$initCopy");
- b = LocalDeclaration.class.getDeclaredField("$iterableCopy");
+ a = Permit.getField(LocalDeclaration.class, "$initCopy");
+ b = Permit.getField(LocalDeclaration.class, "$iterableCopy");
} catch (Throwable t) {
//ignore - no $initCopy exists when running in ecj.
}
try {
- c = Parser.class.getDeclaredField("astStack");
- c.setAccessible(true);
- d = Parser.class.getDeclaredField("astPtr");
- d.setAccessible(true);
- f = Modifier.class.getDeclaredConstructor(AST.class);
- f.setAccessible(true);
- g = MarkerAnnotation.class.getDeclaredConstructor(AST.class);
- g.setAccessible(true);
+ c = Permit.getField(Parser.class, "astStack");
+ d = Permit.getField(Parser.class, "astPtr");
+ f = Permit.getConstructor(Modifier.class, AST.class);
+ g = Permit.getConstructor(MarkerAnnotation.class, AST.class);
Class<?> z = Class.forName("org.eclipse.jdt.core.dom.ASTConverter");
- h = z.getDeclaredMethod("recordNodes", org.eclipse.jdt.core.dom.ASTNode.class, org.eclipse.jdt.internal.compiler.ast.ASTNode.class);
- h.setAccessible(true);
+ h = Permit.getMethod(z, "recordNodes", org.eclipse.jdt.core.dom.ASTNode.class, org.eclipse.jdt.internal.compiler.ast.ASTNode.class);
} catch (Throwable t) {
// Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
}
diff --git a/src/utils/lombok/core/FieldAugment.java b/src/utils/lombok/core/FieldAugment.java
index ee8acf4d..4a32ad04 100644
--- a/src/utils/lombok/core/FieldAugment.java
+++ b/src/utils/lombok/core/FieldAugment.java
@@ -27,6 +27,8 @@ import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.WeakHashMap;
+import lombok.permit.Permit;
+
public abstract class FieldAugment<T, F> {
private static Object getDefaultValue(Class<?> type) {
if (type == boolean.class) return false;
@@ -101,10 +103,9 @@ public abstract class FieldAugment<T, F> {
private static Field findField(Class<?> type, Class<?> wantedType, String name) {
try {
- Field f = type.getDeclaredField(name);
+ Field f = Permit.getField(type, name);
if (Modifier.isStatic(f.getModifiers()) || Modifier.isFinal(f.getModifiers())) return null;
if (!typeIsAssignmentCompatible(f.getType(), wantedType)) return null;
- f.setAccessible(true);
return f;
} catch (Exception e) {
return null;
diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java
index afbd7b52..f8b73b0a 100644
--- a/src/utils/lombok/javac/CommentCatcher.java
+++ b/src/utils/lombok/javac/CommentCatcher.java
@@ -26,6 +26,7 @@ import java.util.Collections;
import java.util.List;
import lombok.core.FieldAugment;
+import lombok.permit.Permit;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
@@ -79,7 +80,7 @@ public class CommentCatcher {
} else {
scannerFactory = Class.forName("lombok.javac.java8.CommentCollectingScannerFactory");
}
- scannerFactory.getMethod("preRegister", Context.class).invoke(null, context);
+ Permit.getMethod(scannerFactory, "preRegister", Context.class).invoke(null, context);
} catch (InvocationTargetException e) {
throw Javac.sneakyThrow(e.getCause());
} catch (Exception e) {
@@ -100,7 +101,7 @@ public class CommentCatcher {
} else {
parserFactory = Class.forName("lombok.javac.java9.CommentCollectingParserFactory");
}
- parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class).invoke(null, compiler, context);
+ Permit.getMethod(parserFactory, "setInCompiler", JavaCompiler.class, Context.class).invoke(null, compiler, context);
} catch (InvocationTargetException e) {
throw Javac.sneakyThrow(e.getCause());
} catch (Exception e) {
diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java
index 92961726..a2cb32c6 100644
--- a/src/utils/lombok/javac/Javac.java
+++ b/src/utils/lombok/javac/Javac.java
@@ -40,6 +40,7 @@ import lombok.core.ClassLiteral;
import lombok.core.FieldSelect;
import lombok.javac.JavacTreeMaker.TreeTag;
import lombok.javac.JavacTreeMaker.TypeTag;
+import lombok.permit.Permit;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.code.Symtab;
@@ -199,7 +200,6 @@ public class Javac {
static {
getExtendsClause = getMethod(JCClassDecl.class, "getExtendsClause", new Class<?>[0]);
- getExtendsClause.setAccessible(true);
if (getJavaCompilerVersion() < 8) {
getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class);
@@ -214,11 +214,11 @@ public class Javac {
throw sneakyThrow(ex);
}
try {
- storeEndMethodTemp = endPosTable.getMethod("storeEnd", JCTree.class, int.class);
+ storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class);
} catch (NoSuchMethodException e) {
try {
endPosTable = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable");
- storeEndMethodTemp = endPosTable.getDeclaredMethod("storeEnd", JCTree.class, int.class);
+ storeEndMethodTemp = Permit.getMethod(endPosTable, "storeEnd", JCTree.class, int.class);
} catch (NoSuchMethodException ex) {
throw sneakyThrow(ex);
} catch (ClassNotFoundException ex) {
@@ -227,13 +227,13 @@ public class Javac {
}
storeEnd = storeEndMethodTemp;
}
- getEndPosition.setAccessible(true);
- storeEnd.setAccessible(true);
+ Permit.setAccessible(getEndPosition);
+ Permit.setAccessible(storeEnd);
}
private static Method getMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
try {
- return clazz.getMethod(name, paramTypes);
+ return Permit.getMethod(clazz, name, paramTypes);
} catch (NoSuchMethodException e) {
throw sneakyThrow(e);
}
@@ -243,7 +243,7 @@ public class Javac {
try {
Class<?>[] c = new Class[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]);
- return clazz.getMethod(name, c);
+ return Permit.getMethod(clazz, name, c);
} catch (NoSuchMethodException e) {
throw sneakyThrow(e);
} catch (ClassNotFoundException e) {
@@ -320,7 +320,7 @@ public class Javac {
private static Field getFieldIfExists(Class<?> c, String fieldName) {
try {
- return c.getField("voidType");
+ return Permit.getField(c, "voidType");
} catch (Exception e) {
return null;
}
@@ -370,13 +370,13 @@ public class Javac {
static {
Field f = null;
try {
- f = JCCompilationUnit.class.getDeclaredField("endPositions");
+ f = Permit.getField(JCCompilationUnit.class, "endPositions");
} catch (NoSuchFieldException e) {}
JCCOMPILATIONUNIT_ENDPOSITIONS = f;
f = null;
try {
- f = JCCompilationUnit.class.getDeclaredField("docComments");
+ f = Permit.getField(JCCompilationUnit.class, "docComments");
} catch (NoSuchFieldException e) {}
JCCOMPILATIONUNIT_DOCCOMMENTS = f;
}
diff --git a/src/utils/lombok/javac/JavacTreeMaker.java b/src/utils/lombok/javac/JavacTreeMaker.java
index 5f4fb09c..68a16af2 100644
--- a/src/utils/lombok/javac/JavacTreeMaker.java
+++ b/src/utils/lombok/javac/JavacTreeMaker.java
@@ -88,6 +88,8 @@ import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;
+import lombok.permit.Permit;
+
public class JavacTreeMaker {
private final TreeMaker tm;
@@ -153,7 +155,7 @@ public class JavacTreeMaker {
Object value = cache.get(fieldName);
if (value != null) return value;
try {
- value = Class.forName(className).getField(fieldName).get(null);
+ value = Permit.getField(Class.forName(className), fieldName).get(null);
} catch (NoSuchFieldException e) {
throw Javac.sneakyThrow(e);
} catch (IllegalAccessException e) {
@@ -169,7 +171,7 @@ public class JavacTreeMaker {
private static Field NOSUCHFIELDEX_MARKER;
static {
try {
- NOSUCHFIELDEX_MARKER = SchroedingerType.class.getDeclaredField("NOSUCHFIELDEX_MARKER");
+ NOSUCHFIELDEX_MARKER = Permit.getField(SchroedingerType.class, "NOSUCHFIELDEX_MARKER");
} catch (NoSuchFieldException e) {
throw Javac.sneakyThrow(e);
}
@@ -180,12 +182,12 @@ public class JavacTreeMaker {
Field field = cache.get(c);
if (field == null) {
try {
- field = c.getField(fieldName);
+ field = Permit.getField(c, fieldName);
} catch (NoSuchFieldException e) {
cache.putIfAbsent(c, NOSUCHFIELDEX_MARKER);
throw Javac.sneakyThrow(e);
}
- field.setAccessible(true);
+ Permit.setAccessible(field);
Field old = cache.putIfAbsent(c, field);
if (old != null) field = old;
}
@@ -207,8 +209,7 @@ public class JavacTreeMaker {
static {
Method m = null;
try {
- m = Type.class.getDeclaredMethod("getTag");
- m.setAccessible(true);
+ m = Permit.getMethod(Type.class, "getTag");
} catch (NoSuchMethodException e) {}
TYPE_TYPETAG_METHOD = m;
}
@@ -255,8 +256,7 @@ public class JavacTreeMaker {
static {
Method m = null;
try {
- m = JCTree.class.getDeclaredMethod("getTag");
- m.setAccessible(true);
+ m = Permit.getMethod(JCTree.class, "getTag");
} catch (NoSuchMethodException e) {}
if (m != null) {
@@ -265,8 +265,7 @@ public class JavacTreeMaker {
} else {
Field f = null;
try {
- f = JCTree.class.getDeclaredField("tag");
- f.setAccessible(true);
+ f = Permit.getField(JCTree.class, "tag");
} catch (NoSuchFieldException e) {}
TAG_FIELD = f;
TAG_METHOD = null;
@@ -381,7 +380,7 @@ public class JavacTreeMaker {
else throw new IllegalStateException("Lombok TreeMaker frontend issue: multiple matches when looking for method: " + m);
}
if (found == null) throw new IllegalStateException("Lombok TreeMaker frontend issue: no match when looking for method: " + m);
- found.setAccessible(true);
+ Permit.setAccessible(found);
Object marker = METHOD_CACHE.putIfAbsent(m, found);
if (marker == null) return found;
return METHOD_CACHE.get(m);
diff --git a/src/utils/lombok/javac/PackageName.java b/src/utils/lombok/javac/PackageName.java
index e4dd6b20..72892cca 100644
--- a/src/utils/lombok/javac/PackageName.java
+++ b/src/utils/lombok/javac/PackageName.java
@@ -28,13 +28,15 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
+import lombok.permit.Permit;
+
// Supports JDK6-9
public class PackageName {
private static final Method packageNameMethod = getPackageNameMethod();
private static Method getPackageNameMethod() {
try {
- return JCCompilationUnit.class.getDeclaredMethod("getPackageName");
+ return Permit.getMethod(JCCompilationUnit.class, "getPackageName");
} catch (Exception e) {
return null;
}
diff --git a/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java
index 0cf8fdcd..5d96f9d1 100644
--- a/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java
+++ b/src/utils/lombok/javac/java6/CommentCollectingParserFactory.java
@@ -28,6 +28,8 @@ import com.sun.tools.javac.parser.Lexer;
import com.sun.tools.javac.parser.Parser;
import com.sun.tools.javac.util.Context;
+import lombok.permit.Permit;
+
public class CommentCollectingParserFactory extends Parser.Factory {
static Context.Key<Parser.Factory> key() {
return parserFactoryKey;
@@ -49,8 +51,7 @@ public class CommentCollectingParserFactory extends Parser.Factory {
context.put(CommentCollectingParserFactory.key(), (Parser.Factory)null);
Field field;
try {
- field = JavaCompiler.class.getDeclaredField("parserFactory");
- field.setAccessible(true);
+ field = Permit.getField(JavaCompiler.class, "parserFactory");
field.set(compiler, new CommentCollectingParserFactory(context));
} catch (Exception e) {
throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e);
diff --git a/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java
index ffd68f55..7ef937cd 100644
--- a/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java
+++ b/src/utils/lombok/javac/java7/CommentCollectingParserFactory.java
@@ -30,6 +30,8 @@ import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.parser.ScannerFactory;
import com.sun.tools.javac.util.Context;
+import lombok.permit.Permit;
+
public class CommentCollectingParserFactory extends ParserFactory {
private final Context context;
@@ -56,8 +58,7 @@ public class CommentCollectingParserFactory extends ParserFactory {
context.put(CommentCollectingParserFactory.key(), (ParserFactory)null);
Field field;
try {
- field = JavaCompiler.class.getDeclaredField("parserFactory");
- field.setAccessible(true);
+ field = Permit.getField(JavaCompiler.class, "parserFactory");
field.set(compiler, new CommentCollectingParserFactory(context));
} catch (Exception e) {
throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e);
diff --git a/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java
index 2fdaddfe..0988ce6d 100644
--- a/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java
+++ b/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java
@@ -30,6 +30,8 @@ import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.parser.ScannerFactory;
import com.sun.tools.javac.util.Context;
+import lombok.permit.Permit;
+
public class CommentCollectingParserFactory extends ParserFactory {
private final Context context;
@@ -66,8 +68,7 @@ public class CommentCollectingParserFactory extends ParserFactory {
context.put(CommentCollectingParserFactory.key(), (ParserFactory) null);
Field field;
try {
- field = JavaCompiler.class.getDeclaredField("parserFactory");
- field.setAccessible(true);
+ field = Permit.getField(JavaCompiler.class, "parserFactory");
field.set(compiler, new CommentCollectingParserFactory(context));
} catch (Exception e) {
throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e);
diff --git a/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java
index 5af4a419..208d2570 100644
--- a/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java
+++ b/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java
@@ -30,6 +30,8 @@ import com.sun.tools.javac.parser.ParserFactory;
import com.sun.tools.javac.parser.ScannerFactory;
import com.sun.tools.javac.util.Context;
+import lombok.permit.Permit;
+
public class CommentCollectingParserFactory extends ParserFactory {
private final Context context;
@@ -60,8 +62,7 @@ public class CommentCollectingParserFactory extends ParserFactory {
context.put(CommentCollectingParserFactory.key(), (ParserFactory) null);
Field field;
try {
- field = JavaCompiler.class.getDeclaredField("parserFactory");
- field.setAccessible(true);
+ field = Permit.getField(JavaCompiler.class, "parserFactory");
field.set(compiler, new CommentCollectingParserFactory(context));
} catch (Exception e) {
throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e);
diff --git a/src/utils/lombok/permit/Permit.java b/src/utils/lombok/permit/Permit.java
new file mode 100644
index 00000000..00b8274c
--- /dev/null
+++ b/src/utils/lombok/permit/Permit.java
@@ -0,0 +1,100 @@
+package lombok.permit;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.util.List;
+
+// sunapi suppresses javac's warning about using Unsafe; 'all' suppresses eclipse's warning about the unspecified 'sunapi' key. Leave them both.
+// Yes, javac's definition of the word 'all' is quite contrary to what the dictionary says it means. 'all' does NOT include 'sunapi' according to javac.
+@SuppressWarnings({"sunapi", "all"})
+public class Permit {
+ private Permit() {}
+
+
+ private static final long ACCESSIBLE_OVERRIDE_FIELD_OFFSET;
+ private static final IllegalAccessException INIT_ERROR;
+ private static final sun.misc.Unsafe UNSAFE = (sun.misc.Unsafe) reflectiveStaticFieldAccess(sun.misc.Unsafe.class, "theUnsafe");
+
+ static {
+ Field f;
+ long g;
+ Throwable ex;
+
+ try {
+ f = AccessibleObject.class.getDeclaredField("override");
+ g = UNSAFE.objectFieldOffset(f);
+ ex = null;
+ } catch (Throwable t) {
+ f = null;
+ g = -1L;
+ ex = t;
+ }
+
+ ACCESSIBLE_OVERRIDE_FIELD_OFFSET = g;
+ if (ex == null) INIT_ERROR = null;
+ else if (ex instanceof IllegalAccessException) INIT_ERROR = (IllegalAccessException) ex;
+ else {
+ INIT_ERROR = new IllegalAccessException("Cannot initialize Unsafe-based permit");
+ INIT_ERROR.initCause(ex);
+ }
+ }
+
+ public static <T extends AccessibleObject> T setAccessible(T accessor) {
+ if (INIT_ERROR == null) {
+ UNSAFE.putBoolean(accessor, ACCESSIBLE_OVERRIDE_FIELD_OFFSET, true);
+ } else {
+ accessor.setAccessible(true);
+ }
+
+ return accessor;
+ }
+
+ public static Method getMethod(Class<?> c, String mName, Class<?>... parameterTypes) throws NoSuchMethodException {
+ Method m = null;
+ Class<?> oc = c;
+ while (c != null) {
+ try {
+ m = c.getDeclaredMethod(mName, parameterTypes);
+ break;
+ } catch (NoSuchMethodException e) {}
+ c = c.getSuperclass();
+ }
+
+ if (m == null) throw new NoSuchMethodException(oc.getName() + " :: " + mName + "(args)");
+ return setAccessible(m);
+ }
+
+ public static Field getField(Class<?> c, String fName) throws NoSuchFieldException {
+ Field f = null;
+ Class<?> oc = c;
+ while (c != null) {
+ try {
+ f = c.getDeclaredField(fName);
+ break;
+ } catch (NoSuchFieldException e) {}
+ c = c.getSuperclass();
+ }
+
+ if (f == null) throw new NoSuchFieldException(oc.getName() + " :: " + fName);
+
+ return setAccessible(f);
+ }
+
+ public static <T> Constructor<T> getConstructor(Class<T> c, Class<?>... parameterTypes) throws NoSuchMethodException {
+ return setAccessible(c.getDeclaredConstructor(parameterTypes));
+ }
+
+ private static Object reflectiveStaticFieldAccess(Class<?> c, String fName) {
+ try {
+ Field f = c.getDeclaredField(fName);
+ f.setAccessible(true);
+ return f.get(null);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/src/utils/lombok/permit/package-info.java b/src/utils/lombok/permit/package-info.java
new file mode 100644
index 00000000..a28f7160
--- /dev/null
+++ b/src/utils/lombok/permit/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * This is a reduced copy of the nqzero Permit-reflect project.
+ * https://github.com/nqzero/permit-reflect
+ *
+ * Many thanks to nqzero. The permit-reflect project is, like lombok itself, licensed under the MIT license.
+ * See https://github.com/nqzero/permit-reflect/blob/master/License for license info.
+ */
+package lombok.permit; \ No newline at end of file
diff --git a/test/core/src/lombok/CompilerMessageMatcher.java b/test/core/src/lombok/CompilerMessageMatcher.java
index 0d6c0889..49c81b70 100644
--- a/test/core/src/lombok/CompilerMessageMatcher.java
+++ b/test/core/src/lombok/CompilerMessageMatcher.java
@@ -67,7 +67,8 @@ public class CompilerMessageMatcher {
public boolean matches(CompilerMessage message) {
outer:
for (int i = 0; i < lineNumbers.size(); i++) {
- if (message.getLine() != lineNumbers.get(i)) continue;
+ //Allow an off-by-1 in line numbers; when running tests that sometimes happens for as yet unknown reasons.
+ if (message.getLine() != lineNumbers.get(i) && message.getLine() -1 != lineNumbers.get(i)) continue;
for (String token : messages.get(i)) {
if (!message.getMessage().contains(token)) continue outer;
}
diff --git a/test/manual/about.txt b/test/manual/about.txt
new file mode 100644
index 00000000..280f491a
--- /dev/null
+++ b/test/manual/about.txt
@@ -0,0 +1 @@
+This directory contains test cases which must be manually run. The aim is to get these automated as unit tests, but, walk before you can run.
diff --git a/test/manual/moduleBasedMultiProject/.gitignore b/test/manual/moduleBasedMultiProject/.gitignore
new file mode 100644
index 00000000..e2e7327c
--- /dev/null
+++ b/test/manual/moduleBasedMultiProject/.gitignore
@@ -0,0 +1 @@
+/out
diff --git a/test/manual/moduleBasedMultiProject/projA/module-info.java b/test/manual/moduleBasedMultiProject/projA/module-info.java
new file mode 100644
index 00000000..1ae75d49
--- /dev/null
+++ b/test/manual/moduleBasedMultiProject/projA/module-info.java
@@ -0,0 +1,4 @@
+module projA {
+ requires static lombok;
+ exports pkgA;
+}
diff --git a/test/manual/moduleBasedMultiProject/projA/pkgA/ClassA.java b/test/manual/moduleBasedMultiProject/projA/pkgA/ClassA.java
new file mode 100644
index 00000000..2e17e142
--- /dev/null
+++ b/test/manual/moduleBasedMultiProject/projA/pkgA/ClassA.java
@@ -0,0 +1,5 @@
+package pkgA;
+
+public class ClassA {
+ @lombok.Getter private String hello = "hello";
+}
diff --git a/test/manual/moduleBasedMultiProject/projB/module-info.java b/test/manual/moduleBasedMultiProject/projB/module-info.java
new file mode 100644
index 00000000..7b82f362
--- /dev/null
+++ b/test/manual/moduleBasedMultiProject/projB/module-info.java
@@ -0,0 +1,5 @@
+module projB {
+ requires static lombok;
+ requires projA;
+}
+
diff --git a/test/manual/moduleBasedMultiProject/projB/pkgB/ClassB.java b/test/manual/moduleBasedMultiProject/projB/pkgB/ClassB.java
new file mode 100644
index 00000000..3f57f31e
--- /dev/null
+++ b/test/manual/moduleBasedMultiProject/projB/pkgB/ClassB.java
@@ -0,0 +1,10 @@
+import lombok.Getter;
+import pkgA.ClassA;
+
+public class ClassB {
+ @Getter private String world = "world";
+ public void test() {
+ new ClassA().getHello();
+ getWorld();
+ }
+}
diff --git a/test/manual/moduleBasedMultiProject/runTests b/test/manual/moduleBasedMultiProject/runTests
new file mode 100755
index 00000000..48557b43
--- /dev/null
+++ b/test/manual/moduleBasedMultiProject/runTests
@@ -0,0 +1,10 @@
+#!/bin/sh
+echo 'This will build, module-style, 2 modules with lombok dependencies. If the compilation works without error or warning, lombok is working as designed.'
+mkdir -p out/projA
+mkdir -p out/projB
+javac --processor-path ../../../dist/lombok.jar -p ../../../dist/lombok.jar -d out/projA projA/module-info.java projA/pkgA/ClassA.java
+javac --processor-path ../../../dist/lombok.jar -p ../../../dist/lombok.jar:out/projA -d out/projB projB/module-info.java projB/pkgB/ClassB.java
+
+echo Now we try to delombok and see if it works as designed.
+
+java -jar ../../../dist/lombok.jar delombok -p --module-path out/projA projB/pkgB/ClassB.java projB/module-info.java
diff --git a/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java b/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java
index b0563858..96ffd2dc 100644
--- a/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java
+++ b/test/transform/resource/after-delombok/BuilderSingularToBuilderWithNull.java
@@ -58,6 +58,8 @@ class BuilderSingularToBuilderWithNull {
}
@java.lang.SuppressWarnings("all")
public BuilderSingularToBuilderWithNullBuilder toBuilder() {
- return new BuilderSingularToBuilderWithNullBuilder().elems(this.elems == null ? java.util.Collections.<String>emptyList() : this.elems);
+ final BuilderSingularToBuilderWithNullBuilder builder = new BuilderSingularToBuilderWithNullBuilder();
+ if (this.elems != null) builder.elems(this.elems);
+ return builder;
}
}
diff --git a/test/transform/resource/after-delombok/BuilderWithToBuilder.java b/test/transform/resource/after-delombok/BuilderWithToBuilder.java
index 445a65e5..53256107 100644
--- a/test/transform/resource/after-delombok/BuilderWithToBuilder.java
+++ b/test/transform/resource/after-delombok/BuilderWithToBuilder.java
@@ -86,7 +86,9 @@ class BuilderWithToBuilder<T> {
}
@java.lang.SuppressWarnings("all")
public BuilderWithToBuilderBuilder<T> toBuilder() {
- return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.<T>rrr(this)).bars(this.bars == null ? java.util.Collections.<T>emptyList() : this.bars);
+ final BuilderWithToBuilderBuilder<T> builder = new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this));
+ if (this.bars != null) builder.bars(this.bars);
+ return builder;
}
}
class ConstructorWithToBuilder<T> {
@@ -94,15 +96,17 @@ class ConstructorWithToBuilder<T> {
private String mTwo;
private T foo;
@lombok.Singular
- private List<T> bars;
- public ConstructorWithToBuilder(String mOne, T bar) {
+ private com.google.common.collect.ImmutableList<T> bars;
+ public ConstructorWithToBuilder(String mOne, T baz, com.google.common.collect.ImmutableList<T> bars) {
}
@java.lang.SuppressWarnings("all")
public static class ConstructorWithToBuilderBuilder<T> {
@java.lang.SuppressWarnings("all")
private String mOne;
@java.lang.SuppressWarnings("all")
- private T bar;
+ private T baz;
+ @java.lang.SuppressWarnings("all")
+ private com.google.common.collect.ImmutableList<T> bars;
@java.lang.SuppressWarnings("all")
ConstructorWithToBuilderBuilder() {
}
@@ -112,18 +116,23 @@ class ConstructorWithToBuilder<T> {
return this;
}
@java.lang.SuppressWarnings("all")
- public ConstructorWithToBuilderBuilder<T> bar(final T bar) {
- this.bar = bar;
+ public ConstructorWithToBuilderBuilder<T> baz(final T baz) {
+ this.baz = baz;
+ return this;
+ }
+ @java.lang.SuppressWarnings("all")
+ public ConstructorWithToBuilderBuilder<T> bars(final com.google.common.collect.ImmutableList<T> bars) {
+ this.bars = bars;
return this;
}
@java.lang.SuppressWarnings("all")
public ConstructorWithToBuilder<T> build() {
- return new ConstructorWithToBuilder<T>(mOne, bar);
+ return new ConstructorWithToBuilder<T>(mOne, baz, bars);
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
public java.lang.String toString() {
- return "ConstructorWithToBuilder.ConstructorWithToBuilderBuilder(mOne=" + this.mOne + ", bar=" + this.bar + ")";
+ return "ConstructorWithToBuilder.ConstructorWithToBuilderBuilder(mOne=" + this.mOne + ", baz=" + this.baz + ", bars=" + this.bars + ")";
}
}
@java.lang.SuppressWarnings("all")
@@ -132,54 +141,6 @@ class ConstructorWithToBuilder<T> {
}
@java.lang.SuppressWarnings("all")
public ConstructorWithToBuilderBuilder<T> toBuilder() {
- return new ConstructorWithToBuilderBuilder<T>().mOne(this.mOne).bar(this.foo);
+ return new ConstructorWithToBuilderBuilder<T>().mOne(this.mOne).baz(this.foo).bars(this.bars);
}
-}
-class StaticWithToBuilder<T, K> {
- private String mOne;
- private String mTwo;
- private T foo;
- private K bar;
- @lombok.Singular
- private List<T> bars;
- public static <Z> StaticWithToBuilder<Z, String> test(String mOne, Z bar) {
- return new StaticWithToBuilder<Z, String>();
- }
- @java.lang.SuppressWarnings("all")
- public static class StaticWithToBuilderBuilder<Z> {
- @java.lang.SuppressWarnings("all")
- private String mOne;
- @java.lang.SuppressWarnings("all")
- private Z bar;
- @java.lang.SuppressWarnings("all")
- StaticWithToBuilderBuilder() {
- }
- @java.lang.SuppressWarnings("all")
- public StaticWithToBuilderBuilder<Z> mOne(final String mOne) {
- this.mOne = mOne;
- return this;
- }
- @java.lang.SuppressWarnings("all")
- public StaticWithToBuilderBuilder<Z> bar(final Z bar) {
- this.bar = bar;
- return this;
- }
- @java.lang.SuppressWarnings("all")
- public StaticWithToBuilder<Z, String> build() {
- return StaticWithToBuilder.<Z>test(mOne, bar);
- }
- @java.lang.Override
- @java.lang.SuppressWarnings("all")
- public java.lang.String toString() {
- return "StaticWithToBuilder.StaticWithToBuilderBuilder(mOne=" + this.mOne + ", bar=" + this.bar + ")";
- }
- }
- @java.lang.SuppressWarnings("all")
- public static <Z> StaticWithToBuilderBuilder<Z> builder() {
- return new StaticWithToBuilderBuilder<Z>();
- }
- @java.lang.SuppressWarnings("all")
- public StaticWithToBuilderBuilder<T> toBuilder() {
- return new StaticWithToBuilderBuilder<T>().mOne(this.mOne).bar(this.foo);
- }
-}
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/EqualsAndHashCodeExplicitInclude.java b/test/transform/resource/after-delombok/EqualsAndHashCodeExplicitInclude.java
new file mode 100644
index 00000000..7de3d5fe
--- /dev/null
+++ b/test/transform/resource/after-delombok/EqualsAndHashCodeExplicitInclude.java
@@ -0,0 +1,22 @@
+class EqualsAndHashCodeExplicitInclude {
+ int x;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public boolean equals(final java.lang.Object o) {
+ if (o == this) return true;
+ if (!(o instanceof EqualsAndHashCodeExplicitInclude)) return false;
+ final EqualsAndHashCodeExplicitInclude other = (EqualsAndHashCodeExplicitInclude) o;
+ if (!other.canEqual((java.lang.Object) this)) return false;
+ return true;
+ }
+ @java.lang.SuppressWarnings("all")
+ protected boolean canEqual(final java.lang.Object other) {
+ return other instanceof EqualsAndHashCodeExplicitInclude;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public int hashCode() {
+ int result = 1;
+ return result;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-delombok/NoArgsConstructorForce.java b/test/transform/resource/after-delombok/NoArgsConstructorForce.java
index 3336ca19..d4bfcda2 100644
--- a/test/transform/resource/after-delombok/NoArgsConstructorForce.java
+++ b/test/transform/resource/after-delombok/NoArgsConstructorForce.java
@@ -3,6 +3,7 @@ public class NoArgsConstructorForce {
private final int[] i;
private final Object[] o;
private final java.util.List<?>[] fullQualifiedList;
+ private final String alreadyInitialized = "yes";
@java.lang.SuppressWarnings("all")
public NoArgsConstructorForce() {
diff --git a/test/transform/resource/after-delombok/SuperBuilderAbstractToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderAbstractToBuilder.java
new file mode 100644
index 00000000..096bd533
--- /dev/null
+++ b/test/transform/resource/after-delombok/SuperBuilderAbstractToBuilder.java
@@ -0,0 +1,168 @@
+public class SuperBuilderAbstractToBuilder {
+ public static class Parent {
+ int parentField;
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+ @java.lang.SuppressWarnings("all")
+ private int parentField;
+ @java.lang.SuppressWarnings("all")
+ protected B $fillValuesFrom(final C instance) {
+ ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
+ b.parentField(instance.parentField);
+ }
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B parentField(final int parentField) {
+ this.parentField = parentField;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderAbstractToBuilder.Parent.ParentBuilder(parentField=" + this.parentField + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ private static final class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+ @java.lang.SuppressWarnings("all")
+ private ParentBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ParentBuilderImpl self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public Parent build() {
+ return new Parent(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Parent(final ParentBuilder<?, ?> b) {
+ this.parentField = b.parentField;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static ParentBuilder<?, ?> builder() {
+ return new ParentBuilderImpl();
+ }
+ @java.lang.SuppressWarnings("all")
+ public ParentBuilder<?, ?> toBuilder() {
+ return new ParentBuilderImpl().$fillValuesFrom(this);
+ }
+ }
+ public static abstract class Child extends Parent {
+ double childField;
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
+ @java.lang.SuppressWarnings("all")
+ private double childField;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected B $fillValuesFrom(final C instance) {
+ super.$fillValuesFrom(instance);
+ ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ private static void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
+ b.childField(instance.childField);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B childField(final double childField) {
+ this.childField = childField;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderAbstractToBuilder.Child.ChildBuilder(super=" + super.toString() + ", childField=" + this.childField + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Child(final ChildBuilder<?, ?> b) {
+ super(b);
+ this.childField = b.childField;
+ }
+ }
+ public static class GrandChild extends Child {
+ String grandChildField;
+ @java.lang.SuppressWarnings("all")
+ public static abstract class GrandChildBuilder<C extends GrandChild, B extends GrandChildBuilder<C, B>> extends Child.ChildBuilder<C, B> {
+ @java.lang.SuppressWarnings("all")
+ private String grandChildField;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected B $fillValuesFrom(final C instance) {
+ super.$fillValuesFrom(instance);
+ GrandChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ private static void $fillValuesFromInstanceIntoBuilder(final GrandChild instance, final GrandChildBuilder<?, ?> b) {
+ b.grandChildField(instance.grandChildField);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B grandChildField(final String grandChildField) {
+ this.grandChildField = grandChildField;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderAbstractToBuilder.GrandChild.GrandChildBuilder(super=" + super.toString() + ", grandChildField=" + this.grandChildField + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ private static final class GrandChildBuilderImpl extends GrandChildBuilder<GrandChild, GrandChildBuilderImpl> {
+ @java.lang.SuppressWarnings("all")
+ private GrandChildBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected GrandChildBuilderImpl self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public GrandChild build() {
+ return new GrandChild(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected GrandChild(final GrandChildBuilder<?, ?> b) {
+ super(b);
+ this.grandChildField = b.grandChildField;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static GrandChildBuilder<?, ?> builder() {
+ return new GrandChildBuilderImpl();
+ }
+ @java.lang.SuppressWarnings("all")
+ public GrandChildBuilder<?, ?> toBuilder() {
+ return new GrandChildBuilderImpl().$fillValuesFrom(this);
+ }
+ }
+ public static void test() {
+ GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
new file mode 100644
index 00000000..dc0b4e70
--- /dev/null
+++ b/test/transform/resource/after-delombok/SuperBuilderBasicToBuilder.java
@@ -0,0 +1,198 @@
+import java.util.List;
+public class SuperBuilderBasicToBuilder {
+ public static class Parent {
+ private int field1;
+ int obtainViaField;
+ int obtainViaMethod;
+ String obtainViaStaticMethod;
+ List<String> items;
+ private int method() {
+ return 2;
+ }
+ private static String staticMethod(Parent instance) {
+ return "staticMethod";
+ }
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+ @java.lang.SuppressWarnings("all")
+ private int field1;
+ @java.lang.SuppressWarnings("all")
+ private int obtainViaField;
+ @java.lang.SuppressWarnings("all")
+ private int obtainViaMethod;
+ @java.lang.SuppressWarnings("all")
+ private String obtainViaStaticMethod;
+ @java.lang.SuppressWarnings("all")
+ private java.util.ArrayList<String> items;
+ @java.lang.SuppressWarnings("all")
+ protected B $fillValuesFrom(final C instance) {
+ ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ private static void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
+ b.field1(instance.field1);
+ b.obtainViaField(instance.field1);
+ b.obtainViaMethod(instance.method());
+ b.obtainViaStaticMethod(Parent.staticMethod(instance));
+ b.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
+ }
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B field1(final int field1) {
+ this.field1 = field1;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B obtainViaField(final int obtainViaField) {
+ this.obtainViaField = obtainViaField;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B obtainViaMethod(final int obtainViaMethod) {
+ this.obtainViaMethod = obtainViaMethod;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B obtainViaStaticMethod(final String obtainViaStaticMethod) {
+ this.obtainViaStaticMethod = obtainViaStaticMethod;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B item(final String item) {
+ if (this.items == null) this.items = new java.util.ArrayList<String>();
+ this.items.add(item);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B items(final java.util.Collection<? extends String> items) {
+ if (this.items == null) this.items = new java.util.ArrayList<String>();
+ this.items.addAll(items);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B clearItems() {
+ if (this.items != null) this.items.clear();
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderBasicToBuilder.Parent.ParentBuilder(field1=" + this.field1 + ", obtainViaField=" + this.obtainViaField + ", obtainViaMethod=" + this.obtainViaMethod + ", obtainViaStaticMethod=" + this.obtainViaStaticMethod + ", items=" + this.items + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ private static final class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+ @java.lang.SuppressWarnings("all")
+ private ParentBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ParentBuilderImpl self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public Parent build() {
+ return new Parent(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Parent(final ParentBuilder<?, ?> b) {
+ this.field1 = b.field1;
+ this.obtainViaField = b.obtainViaField;
+ this.obtainViaMethod = b.obtainViaMethod;
+ this.obtainViaStaticMethod = b.obtainViaStaticMethod;
+ java.util.List<String> items;
+ switch (b.items == null ? 0 : b.items.size()) {
+ case 0:
+ items = java.util.Collections.emptyList();
+ break;
+ case 1:
+ items = java.util.Collections.singletonList(b.items.get(0));
+ break;
+ default:
+ items = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(b.items));
+ }
+ this.items = items;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static ParentBuilder<?, ?> builder() {
+ return new ParentBuilderImpl();
+ }
+ @java.lang.SuppressWarnings("all")
+ public ParentBuilder<?, ?> toBuilder() {
+ return new ParentBuilderImpl().$fillValuesFrom(this);
+ }
+ }
+ public static class Child extends Parent {
+ private double field3;
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
+ @java.lang.SuppressWarnings("all")
+ private double field3;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected B $fillValuesFrom(final C instance) {
+ super.$fillValuesFrom(instance);
+ ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ private static void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
+ b.field3(instance.field3);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B field3(final double field3) {
+ this.field3 = field3;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderBasicToBuilder.Child.ChildBuilder(super=" + super.toString() + ", field3=" + this.field3 + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ private static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
+ @java.lang.SuppressWarnings("all")
+ private ChildBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ChildBuilderImpl self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public Child build() {
+ return new Child(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Child(final ChildBuilder<?, ?> b) {
+ super(b);
+ this.field3 = b.field3;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static ChildBuilder<?, ?> builder() {
+ return new ChildBuilderImpl();
+ }
+ @java.lang.SuppressWarnings("all")
+ public ChildBuilder<?, ?> toBuilder() {
+ return new ChildBuilderImpl().$fillValuesFrom(this);
+ }
+ }
+ public static void test() {
+ Child x = Child.builder().field3(0.0).field1(5).item("").build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/after-delombok/SuperBuilderCustomized.java b/test/transform/resource/after-delombok/SuperBuilderCustomized.java
new file mode 100644
index 00000000..dcc2613f
--- /dev/null
+++ b/test/transform/resource/after-delombok/SuperBuilderCustomized.java
@@ -0,0 +1,102 @@
+import java.util.List;
+public class SuperBuilderCustomized {
+ public static class Parent {
+ public static abstract class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+ @java.lang.SuppressWarnings("all")
+ private int field1;
+ public B resetToDefault() {
+ field1 = 0;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B field1(final int field1) {
+ this.field1 = field1;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderCustomized.Parent.ParentBuilder(field1=" + this.field1 + ")";
+ }
+ }
+ int field1;
+ @java.lang.SuppressWarnings("all")
+ private static final class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+ @java.lang.SuppressWarnings("all")
+ private ParentBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ParentBuilderImpl self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public Parent build() {
+ return new Parent(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Parent(final ParentBuilder<?, ?> b) {
+ this.field1 = b.field1;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static ParentBuilder<?, ?> builder() {
+ return new ParentBuilderImpl();
+ }
+ }
+ public static class Child extends Parent {
+ private static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
+ @Override
+ public Child build() {
+ this.resetToDefault();
+ return new Child(this);
+ }
+ @java.lang.SuppressWarnings("all")
+ private ChildBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ChildBuilderImpl self() {
+ return this;
+ }
+ }
+ double field2;
+ public static ChildBuilder<?, ?> builder() {
+ return new ChildBuilderImpl().field2(10.0);
+ }
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
+ @java.lang.SuppressWarnings("all")
+ private double field2;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B field2(final double field2) {
+ this.field2 = field2;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderCustomized.Child.ChildBuilder(super=" + super.toString() + ", field2=" + this.field2 + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Child(final ChildBuilder<?, ?> b) {
+ super(b);
+ this.field2 = b.field2;
+ }
+ }
+ public static void test() {
+ Child x = Child.builder().field2(1.0).field1(5).resetToDefault().build();
+ }
+}
diff --git a/test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java b/test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java
new file mode 100644
index 00000000..deb5a223
--- /dev/null
+++ b/test/transform/resource/after-delombok/SuperBuilderWithGenericsAndToBuilder.java
@@ -0,0 +1,162 @@
+import java.util.List;
+public class SuperBuilderWithGenericsAndToBuilder {
+ public static class Parent<A> {
+ A field1;
+ List<String> items;
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ParentBuilder<A, C extends Parent<A>, B extends ParentBuilder<A, C, B>> {
+ @java.lang.SuppressWarnings("all")
+ private A field1;
+ @java.lang.SuppressWarnings("all")
+ private java.util.ArrayList<String> items;
+ @java.lang.SuppressWarnings("all")
+ protected B $fillValuesFrom(final C instance) {
+ ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ private static <A> void $fillValuesFromInstanceIntoBuilder(final Parent<A> instance, final ParentBuilder<A, ?, ?> b) {
+ b.field1(instance.field1);
+ b.items(instance.items == null ? java.util.Collections.emptyList() : instance.items);
+ }
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B field1(final A field1) {
+ this.field1 = field1;
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B item(final String item) {
+ if (this.items == null) this.items = new java.util.ArrayList<String>();
+ this.items.add(item);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B items(final java.util.Collection<? extends String> items) {
+ if (this.items == null) this.items = new java.util.ArrayList<String>();
+ this.items.addAll(items);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ public B clearItems() {
+ if (this.items != null) this.items.clear();
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderWithGenericsAndToBuilder.Parent.ParentBuilder(field1=" + this.field1 + ", items=" + this.items + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ private static final class ParentBuilderImpl<A> extends ParentBuilder<A, Parent<A>, ParentBuilderImpl<A>> {
+ @java.lang.SuppressWarnings("all")
+ private ParentBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ParentBuilderImpl<A> self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public Parent<A> build() {
+ return new Parent<A>(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Parent(final ParentBuilder<A, ?, ?> b) {
+ this.field1 = b.field1;
+ java.util.List<String> items;
+ switch (b.items == null ? 0 : b.items.size()) {
+ case 0:
+ items = java.util.Collections.emptyList();
+ break;
+ case 1:
+ items = java.util.Collections.singletonList(b.items.get(0));
+ break;
+ default:
+ items = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(b.items));
+ }
+ this.items = items;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static <A> ParentBuilder<A, ?, ?> builder() {
+ return new ParentBuilderImpl<A>();
+ }
+ @java.lang.SuppressWarnings("all")
+ public ParentBuilder<A, ?, ?> toBuilder() {
+ return new ParentBuilderImpl<A>().$fillValuesFrom(this);
+ }
+ }
+ public static class Child<A> extends Parent<A> {
+ double field3;
+ @java.lang.SuppressWarnings("all")
+ public static abstract class ChildBuilder<A, C extends Child<A>, B extends ChildBuilder<A, C, B>> extends Parent.ParentBuilder<A, C, B> {
+ @java.lang.SuppressWarnings("all")
+ private double field3;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected B $fillValuesFrom(final C instance) {
+ super.$fillValuesFrom(instance);
+ ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ @java.lang.SuppressWarnings("all")
+ private static <A> void $fillValuesFromInstanceIntoBuilder(final Child<A> instance, final ChildBuilder<A, ?, ?> b) {
+ b.field3(instance.field3);
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected abstract B self();
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public abstract C build();
+ @java.lang.SuppressWarnings("all")
+ public B field3(final double field3) {
+ this.field3 = field3;
+ return self();
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "SuperBuilderWithGenericsAndToBuilder.Child.ChildBuilder(super=" + super.toString() + ", field3=" + this.field3 + ")";
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ private static final class ChildBuilderImpl<A> extends ChildBuilder<A, Child<A>, ChildBuilderImpl<A>> {
+ @java.lang.SuppressWarnings("all")
+ private ChildBuilderImpl() {
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ protected ChildBuilderImpl<A> self() {
+ return this;
+ }
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public Child<A> build() {
+ return new Child<A>(this);
+ }
+ }
+ @java.lang.SuppressWarnings("all")
+ protected Child(final ChildBuilder<A, ?, ?> b) {
+ super(b);
+ this.field3 = b.field3;
+ }
+ @java.lang.SuppressWarnings("all")
+ public static <A> ChildBuilder<A, ?, ?> builder() {
+ return new ChildBuilderImpl<A>();
+ }
+ @java.lang.SuppressWarnings("all")
+ public ChildBuilder<A, ?, ?> toBuilder() {
+ return new ChildBuilderImpl<A>().$fillValuesFrom(this);
+ }
+ }
+ public static void test() {
+ Child<Integer> x = Child.<Integer>builder().field3(0.0).field1(5).item("").build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/after-delombok/ToStringExplicitInclude.java b/test/transform/resource/after-delombok/ToStringExplicitInclude.java
new file mode 100644
index 00000000..e6b4c09a
--- /dev/null
+++ b/test/transform/resource/after-delombok/ToStringExplicitInclude.java
@@ -0,0 +1,8 @@
+class ToStringExplicitInclude {
+ int x;
+ @java.lang.Override
+ @java.lang.SuppressWarnings("all")
+ public java.lang.String toString() {
+ return "ToStringExplicitInclude()";
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java b/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java
index bbbf9268..ccd6335a 100644
--- a/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java
+++ b/test/transform/resource/after-ecj/BuilderSingularToBuilderWithNull.java
@@ -52,6 +52,9 @@ import lombok.Singular;
return new BuilderSingularToBuilderWithNullBuilder();
}
public @java.lang.SuppressWarnings("all") BuilderSingularToBuilderWithNullBuilder toBuilder() {
- return new BuilderSingularToBuilderWithNullBuilder().elems(((this.elems == null) ? java.util.Collections.<String>emptyList() : this.elems));
+ final BuilderSingularToBuilderWithNullBuilder builder = new BuilderSingularToBuilderWithNullBuilder();
+ if ((this.elems != null))
+ builder.elems(this.elems);
+ return builder;
}
-}
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/BuilderWithToBuilder.java b/test/transform/resource/after-ecj/BuilderWithToBuilder.java
index 320f37f1..420f1583 100644
--- a/test/transform/resource/after-ecj/BuilderWithToBuilder.java
+++ b/test/transform/resource/after-ecj/BuilderWithToBuilder.java
@@ -74,13 +74,17 @@ import lombok.Builder;
return new BuilderWithToBuilderBuilder<T>();
}
public @java.lang.SuppressWarnings("all") BuilderWithToBuilderBuilder<T> toBuilder() {
- return new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.<T>rrr(this)).bars(((this.bars == null) ? java.util.Collections.<T>emptyList() : this.bars));
+ final BuilderWithToBuilderBuilder<T> builder = new BuilderWithToBuilderBuilder<T>().one(this.mOne).two(this.mTwo).foo(BuilderWithToBuilder.rrr(this));
+ if ((this.bars != null))
+ builder.bars(this.bars);
+ return builder;
}
}
@lombok.experimental.Accessors(prefix = "m") class ConstructorWithToBuilder<T> {
public static @java.lang.SuppressWarnings("all") class ConstructorWithToBuilderBuilder<T> {
private @java.lang.SuppressWarnings("all") String mOne;
- private @java.lang.SuppressWarnings("all") T bar;
+ private @java.lang.SuppressWarnings("all") T baz;
+ private @java.lang.SuppressWarnings("all") com.google.common.collect.ImmutableList<T> bars;
@java.lang.SuppressWarnings("all") ConstructorWithToBuilderBuilder() {
super();
}
@@ -88,68 +92,32 @@ import lombok.Builder;
this.mOne = mOne;
return this;
}
- public @java.lang.SuppressWarnings("all") ConstructorWithToBuilderBuilder<T> bar(final T bar) {
- this.bar = bar;
+ public @java.lang.SuppressWarnings("all") ConstructorWithToBuilderBuilder<T> baz(final T baz) {
+ this.baz = baz;
+ return this;
+ }
+ public @java.lang.SuppressWarnings("all") ConstructorWithToBuilderBuilder<T> bars(final com.google.common.collect.ImmutableList<T> bars) {
+ this.bars = bars;
return this;
}
public @java.lang.SuppressWarnings("all") ConstructorWithToBuilder<T> build() {
- return new ConstructorWithToBuilder<T>(mOne, bar);
+ return new ConstructorWithToBuilder<T>(mOne, baz, bars);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
- return (((("ConstructorWithToBuilder.ConstructorWithToBuilderBuilder(mOne=" + this.mOne) + ", bar=") + this.bar) + ")");
+ return (((((("ConstructorWithToBuilder.ConstructorWithToBuilderBuilder(mOne=" + this.mOne) + ", baz=") + this.baz) + ", bars=") + this.bars) + ")");
}
}
private String mOne;
private String mTwo;
private T foo;
- private @lombok.Singular List<T> bars;
- public @Builder(toBuilder = true) ConstructorWithToBuilder(String mOne, @Builder.ObtainVia(field = "foo") T bar) {
+ private @lombok.Singular com.google.common.collect.ImmutableList<T> bars;
+ public @Builder(toBuilder = true) ConstructorWithToBuilder(String mOne, @Builder.ObtainVia(field = "foo") T baz, com.google.common.collect.ImmutableList<T> bars) {
super();
}
public static @java.lang.SuppressWarnings("all") <T>ConstructorWithToBuilderBuilder<T> builder() {
return new ConstructorWithToBuilderBuilder<T>();
}
public @java.lang.SuppressWarnings("all") ConstructorWithToBuilderBuilder<T> toBuilder() {
- return new ConstructorWithToBuilderBuilder<T>().mOne(this.mOne).bar(this.foo);
- }
-}
-@lombok.experimental.Accessors(prefix = "m") class StaticWithToBuilder<T, K> {
- public static @java.lang.SuppressWarnings("all") class StaticWithToBuilderBuilder<Z> {
- private @java.lang.SuppressWarnings("all") String mOne;
- private @java.lang.SuppressWarnings("all") Z bar;
- @java.lang.SuppressWarnings("all") StaticWithToBuilderBuilder() {
- super();
- }
- public @java.lang.SuppressWarnings("all") StaticWithToBuilderBuilder<Z> mOne(final String mOne) {
- this.mOne = mOne;
- return this;
- }
- public @java.lang.SuppressWarnings("all") StaticWithToBuilderBuilder<Z> bar(final Z bar) {
- this.bar = bar;
- return this;
- }
- public @java.lang.SuppressWarnings("all") StaticWithToBuilder<Z, String> build() {
- return StaticWithToBuilder.<Z>test(mOne, bar);
- }
- public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
- return (((("StaticWithToBuilder.StaticWithToBuilderBuilder(mOne=" + this.mOne) + ", bar=") + this.bar) + ")");
- }
- }
- private String mOne;
- private String mTwo;
- private T foo;
- private K bar;
- private @lombok.Singular List<T> bars;
- StaticWithToBuilder() {
- super();
- }
- public static @Builder(toBuilder = true) <Z>StaticWithToBuilder<Z, String> test(String mOne, @Builder.ObtainVia(field = "foo") Z bar) {
- return new StaticWithToBuilder<Z, String>();
- }
- public static @java.lang.SuppressWarnings("all") <Z>StaticWithToBuilderBuilder<Z> builder() {
- return new StaticWithToBuilderBuilder<Z>();
- }
- public @java.lang.SuppressWarnings("all") StaticWithToBuilderBuilder<T> toBuilder() {
- return new StaticWithToBuilderBuilder<T>().mOne(this.mOne).bar(this.foo);
+ return new ConstructorWithToBuilderBuilder<T>().mOne(this.mOne).baz(this.foo).bars(this.bars);
}
}
diff --git a/test/transform/resource/after-ecj/EqualsAndHashCodeExplicitInclude.java b/test/transform/resource/after-ecj/EqualsAndHashCodeExplicitInclude.java
new file mode 100644
index 00000000..01d1526c
--- /dev/null
+++ b/test/transform/resource/after-ecj/EqualsAndHashCodeExplicitInclude.java
@@ -0,0 +1,23 @@
+@lombok.EqualsAndHashCode(onlyExplicitlyIncluded = true) class EqualsAndHashCodeExplicitInclude {
+ int x;
+ EqualsAndHashCodeExplicitInclude() {
+ super();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") boolean equals(final java.lang.Object o) {
+ if ((o == this))
+ return true;
+ if ((! (o instanceof EqualsAndHashCodeExplicitInclude)))
+ return false;
+ final EqualsAndHashCodeExplicitInclude other = (EqualsAndHashCodeExplicitInclude) o;
+ if ((! other.canEqual((java.lang.Object) this)))
+ return false;
+ return true;
+ }
+ protected @java.lang.SuppressWarnings("all") boolean canEqual(final java.lang.Object other) {
+ return (other instanceof EqualsAndHashCodeExplicitInclude);
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") int hashCode() {
+ int result = 1;
+ return result;
+ }
+} \ No newline at end of file
diff --git a/test/transform/resource/after-ecj/NoArgsConstructorForce.java b/test/transform/resource/after-ecj/NoArgsConstructorForce.java
index 850aa8a5..59713673 100644
--- a/test/transform/resource/after-ecj/NoArgsConstructorForce.java
+++ b/test/transform/resource/after-ecj/NoArgsConstructorForce.java
@@ -3,6 +3,7 @@ public @NoArgsConstructor(force = true) class NoArgsConstructorForce {
private final int[] i;
private final Object[] o;
private final java.util.List<?>[] fullQualifiedList;
+ private final String alreadyInitialized = "yes";
public @java.lang.SuppressWarnings("all") NoArgsConstructorForce() {
super();
this.i = null;
diff --git a/test/transform/resource/after-ecj/SuperBuilderAbstractToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderAbstractToBuilder.java
new file mode 100644
index 00000000..668f6acf
--- /dev/null
+++ b/test/transform/resource/after-ecj/SuperBuilderAbstractToBuilder.java
@@ -0,0 +1,131 @@
+public class SuperBuilderAbstractToBuilder {
+ public static @lombok.experimental.SuperBuilder(toBuilder = true) class Parent {
+ public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+ private @java.lang.SuppressWarnings("all") int parentField;
+ public ParentBuilder() {
+ super();
+ }
+ protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+ ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
+ b.parentField(instance.parentField);
+ }
+ protected abstract @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B parentField(final int parentField) {
+ this.parentField = parentField;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("SuperBuilderAbstractToBuilder.Parent.ParentBuilder(parentField=" + this.parentField) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+ private ParentBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") Parent build() {
+ return new Parent(this);
+ }
+ }
+ int parentField;
+ protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder<?, ?> b) {
+ super();
+ this.parentField = b.parentField;
+ }
+ public @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> toBuilder() {
+ return new ParentBuilderImpl().$fillValuesFrom(this);
+ }
+ public static @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> builder() {
+ return new ParentBuilderImpl();
+ }
+ }
+ public static abstract @lombok.experimental.SuperBuilder(toBuilder = true) class Child extends Parent {
+ public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
+ private @java.lang.SuppressWarnings("all") double childField;
+ public ChildBuilder() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+ super.$fillValuesFrom(instance);
+ ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
+ b.childField(instance.childField);
+ }
+ protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B childField(final double childField) {
+ this.childField = childField;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("SuperBuilderAbstractToBuilder.Child.ChildBuilder(super=" + super.toString()) + ", childField=") + this.childField) + ")");
+ }
+ }
+ double childField;
+ protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder<?, ?> b) {
+ super(b);
+ this.childField = b.childField;
+ }
+ }
+ public static @lombok.experimental.SuperBuilder(toBuilder = true) class GrandChild extends Child {
+ public static abstract @java.lang.SuppressWarnings("all") class GrandChildBuilder<C extends GrandChild, B extends GrandChildBuilder<C, B>> extends Child.ChildBuilder<C, B> {
+ private @java.lang.SuppressWarnings("all") String grandChildField;
+ public GrandChildBuilder() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+ super.$fillValuesFrom(instance);
+ GrandChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final GrandChild instance, final GrandChildBuilder<?, ?> b) {
+ b.grandChildField(instance.grandChildField);
+ }
+ protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B grandChildField(final String grandChildField) {
+ this.grandChildField = grandChildField;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("SuperBuilderAbstractToBuilder.GrandChild.GrandChildBuilder(super=" + super.toString()) + ", grandChildField=") + this.grandChildField) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class GrandChildBuilderImpl extends GrandChildBuilder<GrandChild, GrandChildBuilderImpl> {
+ private GrandChildBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") GrandChildBuilderImpl self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") GrandChild build() {
+ return new GrandChild(this);
+ }
+ }
+ String grandChildField;
+ protected @java.lang.SuppressWarnings("all") GrandChild(final GrandChildBuilder<?, ?> b) {
+ super(b);
+ this.grandChildField = b.grandChildField;
+ }
+ public @java.lang.SuppressWarnings("all") GrandChildBuilder<?, ?> toBuilder() {
+ return new GrandChildBuilderImpl().$fillValuesFrom(this);
+ }
+ public static @java.lang.SuppressWarnings("all") GrandChildBuilder<?, ?> builder() {
+ return new GrandChildBuilderImpl();
+ }
+ }
+ public SuperBuilderAbstractToBuilder() {
+ super();
+ }
+ public static void test() {
+ GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
new file mode 100644
index 00000000..f4d99112
--- /dev/null
+++ b/test/transform/resource/after-ecj/SuperBuilderBasicToBuilder.java
@@ -0,0 +1,164 @@
+import java.util.List;
+public class SuperBuilderBasicToBuilder {
+ public static @lombok.experimental.SuperBuilder(toBuilder = true) class Parent {
+ public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+ private @java.lang.SuppressWarnings("all") int field1;
+ private @java.lang.SuppressWarnings("all") int obtainViaField;
+ private @java.lang.SuppressWarnings("all") int obtainViaMethod;
+ private @java.lang.SuppressWarnings("all") String obtainViaStaticMethod;
+ private @java.lang.SuppressWarnings("all") java.util.ArrayList<String> items;
+ public ParentBuilder() {
+ super();
+ }
+ protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+ ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Parent instance, final ParentBuilder<?, ?> b) {
+ b.field1(instance.field1);
+ b.obtainViaField(instance.field1);
+ b.obtainViaMethod(instance.method());
+ b.obtainViaStaticMethod(Parent.staticMethod(instance));
+ b.items(((instance.items == null) ? java.util.Collections.emptyList() : instance.items));
+ }
+ protected abstract @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B field1(final int field1) {
+ this.field1 = field1;
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B obtainViaField(final int obtainViaField) {
+ this.obtainViaField = obtainViaField;
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B obtainViaMethod(final int obtainViaMethod) {
+ this.obtainViaMethod = obtainViaMethod;
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B obtainViaStaticMethod(final String obtainViaStaticMethod) {
+ this.obtainViaStaticMethod = obtainViaStaticMethod;
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B item(final String item) {
+ if ((this.items == null))
+ this.items = new java.util.ArrayList<String>();
+ this.items.add(item);
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B items(final java.util.Collection<? extends String> items) {
+ if ((this.items == null))
+ this.items = new java.util.ArrayList<String>();
+ this.items.addAll(items);
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B clearItems() {
+ if ((this.items != null))
+ this.items.clear();
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((((((((("SuperBuilderBasicToBuilder.Parent.ParentBuilder(field1=" + this.field1) + ", obtainViaField=") + this.obtainViaField) + ", obtainViaMethod=") + this.obtainViaMethod) + ", obtainViaStaticMethod=") + this.obtainViaStaticMethod) + ", items=") + this.items) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+ private ParentBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") Parent build() {
+ return new Parent(this);
+ }
+ }
+ private int field1;
+ @lombok.Builder.ObtainVia(field = "field1") int obtainViaField;
+ @lombok.Builder.ObtainVia(method = "method") int obtainViaMethod;
+ @lombok.Builder.ObtainVia(method = "staticMethod",isStatic = true) String obtainViaStaticMethod;
+ @lombok.Singular List<String> items;
+ private int method() {
+ return 2;
+ }
+ private static String staticMethod(Parent instance) {
+ return "staticMethod";
+ }
+ protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder<?, ?> b) {
+ super();
+ this.field1 = b.field1;
+ this.obtainViaField = b.obtainViaField;
+ this.obtainViaMethod = b.obtainViaMethod;
+ this.obtainViaStaticMethod = b.obtainViaStaticMethod;
+ java.util.List<String> items;
+ switch (((b.items == null) ? 0 : b.items.size())) {
+ case 0 :
+ items = java.util.Collections.emptyList();
+ break;
+ case 1 :
+ items = java.util.Collections.singletonList(b.items.get(0));
+ break;
+ default :
+ items = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(b.items));
+ }
+ this.items = items;
+ }
+ public @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> toBuilder() {
+ return new ParentBuilderImpl().$fillValuesFrom(this);
+ }
+ public static @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> builder() {
+ return new ParentBuilderImpl();
+ }
+ }
+ public static @lombok.experimental.SuperBuilder(toBuilder = true) class Child extends Parent {
+ public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
+ private @java.lang.SuppressWarnings("all") double field3;
+ public ChildBuilder() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+ super.$fillValuesFrom(instance);
+ ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ private static @java.lang.SuppressWarnings("all") void $fillValuesFromInstanceIntoBuilder(final Child instance, final ChildBuilder<?, ?> b) {
+ b.field3(instance.field3);
+ }
+ protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B field3(final double field3) {
+ this.field3 = field3;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("SuperBuilderBasicToBuilder.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
+ private ChildBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") Child build() {
+ return new Child(this);
+ }
+ }
+ private double field3;
+ protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder<?, ?> b) {
+ super(b);
+ this.field3 = b.field3;
+ }
+ public @java.lang.SuppressWarnings("all") ChildBuilder<?, ?> toBuilder() {
+ return new ChildBuilderImpl().$fillValuesFrom(this);
+ }
+ public static @java.lang.SuppressWarnings("all") ChildBuilder<?, ?> builder() {
+ return new ChildBuilderImpl();
+ }
+ }
+ public SuperBuilderBasicToBuilder() {
+ super();
+ }
+ public static void test() {
+ Child x = Child.builder().field3(0.0).field1(5).item("").build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/after-ecj/SuperBuilderCustomized.java b/test/transform/resource/after-ecj/SuperBuilderCustomized.java
new file mode 100644
index 00000000..7336a662
--- /dev/null
+++ b/test/transform/resource/after-ecj/SuperBuilderCustomized.java
@@ -0,0 +1,86 @@
+import java.util.List;
+public class SuperBuilderCustomized {
+ public static @lombok.experimental.SuperBuilder class Parent {
+ public static abstract class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+ private @java.lang.SuppressWarnings("all") int field1;
+ public ParentBuilder() {
+ super();
+ }
+ public B resetToDefault() {
+ field1 = 0;
+ return self();
+ }
+ protected abstract @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B field1(final int field1) {
+ this.field1 = field1;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (("SuperBuilderCustomized.Parent.ParentBuilder(field1=" + this.field1) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
+ private ParentBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") Parent build() {
+ return new Parent(this);
+ }
+ }
+ int field1;
+ protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder<?, ?> b) {
+ super();
+ this.field1 = b.field1;
+ }
+ public static @java.lang.SuppressWarnings("all") ParentBuilder<?, ?> builder() {
+ return new ParentBuilderImpl();
+ }
+ }
+ public static @lombok.experimental.SuperBuilder class Child extends Parent {
+ private static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
+ private ChildBuilderImpl() {
+ super();
+ }
+ public @Override Child build() {
+ this.resetToDefault();
+ return new Child(this);
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl self() {
+ return this;
+ }
+ }
+ public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<C extends Child, B extends ChildBuilder<C, B>> extends Parent.ParentBuilder<C, B> {
+ private @java.lang.SuppressWarnings("all") double field2;
+ public ChildBuilder() {
+ super();
+ }
+ protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B field2(final double field2) {
+ this.field2 = field2;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("SuperBuilderCustomized.Child.ChildBuilder(super=" + super.toString()) + ", field2=") + this.field2) + ")");
+ }
+ }
+ double field2;
+ public static ChildBuilder<?, ?> builder() {
+ return new ChildBuilderImpl().field2(10.0);
+ }
+ protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder<?, ?> b) {
+ super(b);
+ this.field2 = b.field2;
+ }
+ }
+ public SuperBuilderCustomized() {
+ super();
+ }
+ public static void test() {
+ Child x = Child.builder().field2(1.0).field1(5).resetToDefault().build();
+ }
+}
diff --git a/test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java b/test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java
new file mode 100644
index 00000000..107ee362
--- /dev/null
+++ b/test/transform/resource/after-ecj/SuperBuilderWithGenericsAndToBuilder.java
@@ -0,0 +1,134 @@
+import java.util.List;
+public class SuperBuilderWithGenericsAndToBuilder {
+ public static @lombok.experimental.SuperBuilder(toBuilder = true) class Parent<A> {
+ public static abstract @java.lang.SuppressWarnings("all") class ParentBuilder<A, C extends Parent<A>, B extends ParentBuilder<A, C, B>> {
+ private @java.lang.SuppressWarnings("all") A field1;
+ private @java.lang.SuppressWarnings("all") java.util.ArrayList<String> items;
+ public ParentBuilder() {
+ super();
+ }
+ protected @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+ ParentBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ private static @java.lang.SuppressWarnings("all") <A>void $fillValuesFromInstanceIntoBuilder(final Parent<A> instance, final ParentBuilder<A, ?, ?> b) {
+ b.field1(instance.field1);
+ b.items(((instance.items == null) ? java.util.Collections.emptyList() : instance.items));
+ }
+ protected abstract @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B field1(final A field1) {
+ this.field1 = field1;
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B item(final String item) {
+ if ((this.items == null))
+ this.items = new java.util.ArrayList<String>();
+ this.items.add(item);
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B items(final java.util.Collection<? extends String> items) {
+ if ((this.items == null))
+ this.items = new java.util.ArrayList<String>();
+ this.items.addAll(items);
+ return self();
+ }
+ public @java.lang.SuppressWarnings("all") B clearItems() {
+ if ((this.items != null))
+ this.items.clear();
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("SuperBuilderWithGenericsAndToBuilder.Parent.ParentBuilder(field1=" + this.field1) + ", items=") + this.items) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class ParentBuilderImpl<A> extends ParentBuilder<A, Parent<A>, ParentBuilderImpl<A>> {
+ private ParentBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ParentBuilderImpl<A> self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") Parent<A> build() {
+ return new Parent<A>(this);
+ }
+ }
+ A field1;
+ @lombok.Singular List<String> items;
+ protected @java.lang.SuppressWarnings("all") Parent(final ParentBuilder<A, ?, ?> b) {
+ super();
+ this.field1 = b.field1;
+ java.util.List<String> items;
+ switch (((b.items == null) ? 0 : b.items.size())) {
+ case 0 :
+ items = java.util.Collections.emptyList();
+ break;
+ case 1 :
+ items = java.util.Collections.singletonList(b.items.get(0));
+ break;
+ default :
+ items = java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(b.items));
+ }
+ this.items = items;
+ }
+ public @java.lang.SuppressWarnings("all") ParentBuilder<A, ?, ?> toBuilder() {
+ return new ParentBuilderImpl<A>().$fillValuesFrom(this);
+ }
+ public static @java.lang.SuppressWarnings("all") <A>ParentBuilder<A, ?, ?> builder() {
+ return new ParentBuilderImpl<A>();
+ }
+ }
+ public static @lombok.experimental.SuperBuilder(toBuilder = true) class Child<A> extends Parent<A> {
+ public static abstract @java.lang.SuppressWarnings("all") class ChildBuilder<A, C extends Child<A>, B extends ChildBuilder<A, C, B>> extends Parent.ParentBuilder<A, C, B> {
+ private @java.lang.SuppressWarnings("all") double field3;
+ public ChildBuilder() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") B $fillValuesFrom(final C instance) {
+ super.$fillValuesFrom(instance);
+ ChildBuilder.$fillValuesFromInstanceIntoBuilder(instance, this);
+ return self();
+ }
+ private static @java.lang.SuppressWarnings("all") <A>void $fillValuesFromInstanceIntoBuilder(final Child<A> instance, final ChildBuilder<A, ?, ?> b) {
+ b.field3(instance.field3);
+ }
+ protected abstract @java.lang.Override @java.lang.SuppressWarnings("all") B self();
+ public abstract @java.lang.Override @java.lang.SuppressWarnings("all") C build();
+ public @java.lang.SuppressWarnings("all") B field3(final double field3) {
+ this.field3 = field3;
+ return self();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return (((("SuperBuilderWithGenericsAndToBuilder.Child.ChildBuilder(super=" + super.toString()) + ", field3=") + this.field3) + ")");
+ }
+ }
+ private static final @java.lang.SuppressWarnings("all") class ChildBuilderImpl<A> extends ChildBuilder<A, Child<A>, ChildBuilderImpl<A>> {
+ private ChildBuilderImpl() {
+ super();
+ }
+ protected @java.lang.Override @java.lang.SuppressWarnings("all") ChildBuilderImpl<A> self() {
+ return this;
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") Child<A> build() {
+ return new Child<A>(this);
+ }
+ }
+ double field3;
+ protected @java.lang.SuppressWarnings("all") Child(final ChildBuilder<A, ?, ?> b) {
+ super(b);
+ this.field3 = b.field3;
+ }
+ public @java.lang.SuppressWarnings("all") ChildBuilder<A, ?, ?> toBuilder() {
+ return new ChildBuilderImpl<A>().$fillValuesFrom(this);
+ }
+ public static @java.lang.SuppressWarnings("all") <A>ChildBuilder<A, ?, ?> builder() {
+ return new ChildBuilderImpl<A>();
+ }
+ }
+ public SuperBuilderWithGenericsAndToBuilder() {
+ super();
+ }
+ public static void test() {
+ Child<Integer> x = Child.<Integer>builder().field3(0.0).field1(5).item("").build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/after-ecj/ToStringExplicitInclude.java b/test/transform/resource/after-ecj/ToStringExplicitInclude.java
new file mode 100644
index 00000000..adb3109e
--- /dev/null
+++ b/test/transform/resource/after-ecj/ToStringExplicitInclude.java
@@ -0,0 +1,9 @@
+@lombok.ToString(onlyExplicitlyIncluded = true) class ToStringExplicitInclude {
+ int x;
+ ToStringExplicitInclude() {
+ super();
+ }
+ public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() {
+ return "ToStringExplicitInclude()";
+ }
+}
diff --git a/test/transform/resource/before/BuilderWithToBuilder.java b/test/transform/resource/before/BuilderWithToBuilder.java
index 63e16ae8..50938ab2 100644
--- a/test/transform/resource/before/BuilderWithToBuilder.java
+++ b/test/transform/resource/before/BuilderWithToBuilder.java
@@ -13,19 +13,8 @@ class BuilderWithToBuilder<T> {
class ConstructorWithToBuilder<T> {
private String mOne, mTwo;
private T foo;
- @lombok.Singular private List<T> bars;
- @Builder(toBuilder = true)
- public ConstructorWithToBuilder(String mOne, @Builder.ObtainVia(field = "foo") T bar) {
- }
-}
-@lombok.experimental.Accessors(prefix = "m")
-class StaticWithToBuilder<T, K> {
- private String mOne, mTwo;
- private T foo;
- private K bar;
- @lombok.Singular private List<T> bars;
+ @lombok.Singular private com.google.common.collect.ImmutableList<T> bars;
@Builder(toBuilder = true)
- public static <Z> StaticWithToBuilder<Z, String> test(String mOne, @Builder.ObtainVia(field = "foo") Z bar) {
- return new StaticWithToBuilder<Z, String>();
+ public ConstructorWithToBuilder(String mOne, @Builder.ObtainVia(field = "foo") T baz, com.google.common.collect.ImmutableList<T> bars) {
}
}
diff --git a/test/transform/resource/before/EqualsAndHashCodeExplicitInclude.java b/test/transform/resource/before/EqualsAndHashCodeExplicitInclude.java
new file mode 100644
index 00000000..06b77f14
--- /dev/null
+++ b/test/transform/resource/before/EqualsAndHashCodeExplicitInclude.java
@@ -0,0 +1,4 @@
+@lombok.EqualsAndHashCode(onlyExplicitlyIncluded = true)
+class EqualsAndHashCodeExplicitInclude {
+ int x;
+}
diff --git a/test/transform/resource/before/NoArgsConstructorForce.java b/test/transform/resource/before/NoArgsConstructorForce.java
index 48df1a37..6193eb0a 100644
--- a/test/transform/resource/before/NoArgsConstructorForce.java
+++ b/test/transform/resource/before/NoArgsConstructorForce.java
@@ -5,4 +5,5 @@ public class NoArgsConstructorForce {
private final int[] i;
private final Object[] o;
private final java.util.List<?>[] fullQualifiedList;
+ private final String alreadyInitialized = "yes";
} \ No newline at end of file
diff --git a/test/transform/resource/before/SuperBuilderAbstractToBuilder.java b/test/transform/resource/before/SuperBuilderAbstractToBuilder.java
new file mode 100644
index 00000000..3359829c
--- /dev/null
+++ b/test/transform/resource/before/SuperBuilderAbstractToBuilder.java
@@ -0,0 +1,20 @@
+public class SuperBuilderAbstractToBuilder {
+ @lombok.experimental.SuperBuilder(toBuilder = true)
+ public static class Parent {
+ int parentField;
+ }
+
+ @lombok.experimental.SuperBuilder(toBuilder = true)
+ public abstract static class Child extends Parent {
+ double childField;
+ }
+
+ @lombok.experimental.SuperBuilder(toBuilder = true)
+ public static class GrandChild extends Child {
+ String grandChildField;
+ }
+
+ public static void test() {
+ GrandChild x = GrandChild.builder().grandChildField("").parentField(5).childField(2.5).build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/before/SuperBuilderBasicToBuilder.java b/test/transform/resource/before/SuperBuilderBasicToBuilder.java
new file mode 100644
index 00000000..93161443
--- /dev/null
+++ b/test/transform/resource/before/SuperBuilderBasicToBuilder.java
@@ -0,0 +1,32 @@
+import java.util.List;
+
+public class SuperBuilderBasicToBuilder {
+ @lombok.experimental.SuperBuilder(toBuilder=true)
+ public static class Parent {
+ private int field1;
+ @lombok.Builder.ObtainVia(field="field1")
+ int obtainViaField;
+ @lombok.Builder.ObtainVia(method="method")
+ int obtainViaMethod;
+ @lombok.Builder.ObtainVia(method = "staticMethod", isStatic = true)
+ String obtainViaStaticMethod;
+ @lombok.Singular List<String> items;
+
+ private int method() {
+ return 2;
+ }
+
+ private static String staticMethod(Parent instance) {
+ return "staticMethod";
+ }
+ }
+
+ @lombok.experimental.SuperBuilder(toBuilder=true)
+ public static class Child extends Parent {
+ private double field3;
+ }
+
+ public static void test() {
+ Child x = Child.builder().field3(0.0).field1(5).item("").build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/before/SuperBuilderCustomized.java b/test/transform/resource/before/SuperBuilderCustomized.java
new file mode 100644
index 00000000..58f2797c
--- /dev/null
+++ b/test/transform/resource/before/SuperBuilderCustomized.java
@@ -0,0 +1,33 @@
+import java.util.List;
+
+public class SuperBuilderCustomized {
+ @lombok.experimental.SuperBuilder
+ public static class Parent {
+ public static abstract class ParentBuilder<C extends Parent, B extends ParentBuilder<C, B>> {
+ public B resetToDefault() {
+ field1 = 0;
+ return self();
+ }
+ }
+ int field1;
+ }
+
+ @lombok.experimental.SuperBuilder
+ public static class Child extends Parent {
+ private static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
+ @Override
+ public Child build() {
+ this.resetToDefault();
+ return new Child(this);
+ }
+ }
+ double field2;
+ public static ChildBuilder<?, ?> builder() {
+ return new ChildBuilderImpl().field2(10.0);
+ }
+ }
+
+ public static void test() {
+ Child x = Child.builder().field2(1.0).field1(5).resetToDefault().build();
+ }
+}
diff --git a/test/transform/resource/before/SuperBuilderWithGenericsAndToBuilder.java b/test/transform/resource/before/SuperBuilderWithGenericsAndToBuilder.java
new file mode 100644
index 00000000..dae68034
--- /dev/null
+++ b/test/transform/resource/before/SuperBuilderWithGenericsAndToBuilder.java
@@ -0,0 +1,18 @@
+import java.util.List;
+
+public class SuperBuilderWithGenericsAndToBuilder {
+ @lombok.experimental.SuperBuilder(toBuilder = true)
+ public static class Parent<A> {
+ A field1;
+ @lombok.Singular List<String> items;
+ }
+
+ @lombok.experimental.SuperBuilder(toBuilder = true)
+ public static class Child<A> extends Parent<A> {
+ double field3;
+ }
+
+ public static void test() {
+ Child<Integer> x = Child.<Integer>builder().field3(0.0).field1(5).item("").build().toBuilder().build();
+ }
+}
diff --git a/test/transform/resource/before/ToStringExplicitInclude.java b/test/transform/resource/before/ToStringExplicitInclude.java
new file mode 100644
index 00000000..31687bc6
--- /dev/null
+++ b/test/transform/resource/before/ToStringExplicitInclude.java
@@ -0,0 +1,4 @@
+@lombok.ToString(onlyExplicitlyIncluded = true)
+class ToStringExplicitInclude {
+ int x;
+}
diff --git a/website/templates/features/GetterLazy.html b/website/templates/features/GetterLazy.html
index b1f374a8..e921712c 100644
--- a/website/templates/features/GetterLazy.html
+++ b/website/templates/features/GetterLazy.html
@@ -9,6 +9,9 @@
<p>
You can let lombok generate a getter which will calculate a value once, the first time this getter is called, and cache it from then on. This can be useful if calculating the value takes a lot of CPU, or the value takes a lot of memory. To use this feature, create a <code>private final</code> variable, initialize it with the expression that's expensive to run, and annotate your field with <code>@Getter(lazy=true)</code>. The field will be hidden from the rest of your code, and the expression will be evaluated no more than once, when the getter is first called. There are no magic marker values (i.e. even if the result of your expensive calculation is <code>null</code>, the result is cached) and your expensive calculation need not be thread-safe, as lombok takes care of locking.
</p>
+ <p>
+ If the initialization expression is complex, or contains generics, we recommend moving the code to a private (if possible static) method, and call that instead.
+ </p>
</@f.overview>
<@f.snippets name="GetterLazy" />
diff --git a/website/templates/features/constructor.html b/website/templates/features/constructor.html
index fc0be1c6..b1f25321 100644
--- a/website/templates/features/constructor.html
+++ b/website/templates/features/constructor.html
@@ -17,7 +17,7 @@
</p><p>
To put annotations on the generated constructor, you can use <code>onConstructor=@__({@AnnotationsHere})</code>, but be careful; this is an experimental feature. For more details see the documentation on the <a href="/features/experimental/onX">onX</a> feature.
</p><p>
- Static fields are skipped by these annotations. Also, a <code>@java.beans.ConstructorProperties</code> annotation is added for all constructors with at least 1 argument, which allows bean editor tools to call the generated constructors. <code>@ConstructorProperties</code> is new in Java 1.6, which means that if your code is intended for compilation on Java 1.5, a compiler error will occur. <em>Running</em> on a JVM 1.5 should be no problem (the annotation will be ignored). To suppress the generation of the <code>@ConstructorProperties</code> annotation, add a parameter to your annotation: <code>@AllArgsConstructor(suppressConstructorProperties=true)</code>. However, as java 1.5, which has already been end-of-lifed, fades into obscurity, this parameter will eventually be removed. It has also been marked deprecated for this reason.
+ Static fields are skipped by these annotations.
</p><p>
Unlike most other lombok annotations, the existence of an explicit constructor does not stop these annotations from generating their own constructor. This means you can write your own specialized constructor, and let lombok generate the boilerplate ones as well. If a conflict arises (one of your constructors ends up with the same signature as one that lombok generates), a compiler error will occur.
</p>
@@ -27,9 +27,9 @@
<@f.confKeys>
<dt>
- <code>lombok.anyConstructor.suppressConstructorProperties</code> = [<code>true</code> | <code>false</code>] (default: <code>false</code>)
+ <code>lombok.anyConstructor.addConstructorProperties</code> = [<code>true</code> | <code>false</code>] (default: <code>false</code>)
</dt><dd>
- If set to <code>true</code>, then lombok will skip adding a <code>@java.beans.ConstructorProperties</code> to generated constructors. This is useful in android and GWT development where that annotation is not usually available.
+ If set to <code>true</code>, then lombok will add a <code>@java.beans.ConstructorProperties</code> to generated constructors.
</dd><dt>
<code>lombok.</code>[<code>allArgsConstructor</code>|<code>requiredArgsConstructor</code>|<code>noArgsConstructor</code>]<code>.flagUsage</code> = [<code>warning</code> | <code>error</code>] (default: not set)
</dt><dd>
@@ -49,8 +49,6 @@
</p><p>
<code>@XArgsConstructor</code> can also be used on an enum definition. The generated constructor will always be private, because non-private constructors aren't legal in enums. You don't have to specify <code>AccessLevel.PRIVATE</code>.
</p><p>
- While <code>suppressConstructorProperties</code> has been marked deprecated in anticipation of a world where all java environments have the <code>@ConstructorProperties</code> annotation available, first GWT 2.2 and Android 2.3.3, which do not (yet) have this annotation, will have to be ancient history before this annotation parameter will be removed.
- </p><p>
Various well known annotations about nullity cause null checks to be inserted and will be copied to the parameter. See <a href="/features/GetterSetter">Getter/Setter</a> documentation's small print for more information.
</p><p>
The <code>flagUsage</code> configuration keys do not trigger when a constructor is generated by <code>@Data</code>, <code>@Value</code> or any other lombok annotation.
diff --git a/website/templates/features/delombok.html b/website/templates/features/delombok.html
index 56b02f7d..75178740 100644
--- a/website/templates/features/delombok.html
+++ b/website/templates/features/delombok.html
@@ -23,6 +23,8 @@
</p><p>
To let delombok print the transformation result of a single java file directly to standard output, you can use:
<pre>java -jar lombok.jar delombok -p MyJavaFile.java</pre>
+ </p><p>
+ The <code>-classpath</code>, <code>-sourcepath</code>, and <code>--module-path</code> options of javac are replicated as <code>--classpath</code>, <code>--sourcepath</code>, and <code>--module-path</code> in delombok.
</p>
<@f.main.h3 title="Running delombok in ant" />
@@ -39,7 +41,7 @@
&lt;javadoc sourcepath="build/src-delomboked" defaultexcludes="yes" destdir="build/api" /&gt;
&lt;/target&gt;</pre>
<br />
- Instead of a <code>from</code> attribute, you can also nest <code>&lt;fileset&gt;</code> nodes.
+ Instead of a <code>from</code> attribute, you can also nest <code>&lt;fileset&gt;</code> nodes. The <code>delombok</code> supports <code>sourcepath</code>, <code>classpath</code>, and <code>modulepath</code> as parameter or as nested element, or as nested refid element, similar to the <code>javac</code> task.
</p>
<@f.main.h3 title="Running delombok in maven" />
diff --git a/website/templates/features/experimental/SuperBuilder.html b/website/templates/features/experimental/SuperBuilder.html
index 8189a254..26f0a949 100644
--- a/website/templates/features/experimental/SuperBuilder.html
+++ b/website/templates/features/experimental/SuperBuilder.html
@@ -4,6 +4,8 @@
<@f.history>
<p>
<code>@SuperBuilder</code> was introduced as experimental feature in lombok v1.18.2.
+ </p><p>
+ <code>@SuperBuilder</code>'s <code>toBuilder</code> feature and limited support for customization was added with lombok v1.18.4.
</p>
</@f.history>
@@ -11,7 +13,7 @@
<p>
The <code>@SuperBuilder</code> annotation produces complex builder APIs for your classes.
In contrast to <a href="/features/Builder"><code>@Builder</code></a>, <code>@SuperBuilder</code> also works with fields from superclasses.
- However, it only works for types, and cannot be customized by providing a partial builder implementation.
+ However, it only works for types, and customization possibilities are limited.
Most importantly, it requires that <em>all superclasses</em> also have the <code>@SuperBuilder</code> annotation.
</p><p>
<code>@SuperBuilder</code> lets you automatically produce the code required to have your class be instantiable with code such as:<br />
@@ -23,18 +25,25 @@
</p><p>
<code>@SuperBuilder</code> is not compatible with <code>@Builder</code>.
</p><p>
+ You can use <code>@SuperBuilder(toBuilder = true)</code> to also generate an instance method in your class called <code>toBuilder()</code>; it creates a new builder that starts out with all the values of this instance. You can put the <code>@Builder.ObtainVia</code> annotation on the fields to indicate alternative means by which the value for that field/parameter is obtained from this instance. For example, you can specify a method to be invoked: <code>@Builder.ObtainVia(method = "calculateFoo")</code>.
+ </p><p>
To ensure type-safety, <code>@SuperBuilder</code> generates two inner builder classes for each annotated class, one abstract and one concrete class named <code><em>Foobar</em>Builder</code> and <code><em>Foobar</em>BuilderImpl</code> (where <em>Foobar</em> is the name of the annotated class).
</p><p>
+ Customizing the code generated by <code>@SuperBuilder</code> is limited to adding new methods or annotations to the builder classes, and providing custom implementations of <code>builder()</code> and <code>build()</code>.
+ You have to make sure that the builder class declaration headers match those that would have been generated by lombok. Due to the heavy generics usage, we strongly advice to copy the builder class definition header from the uncustomized delomboked code.
+ </p><p>
The configurable aspects of builder are:
<ul>
<li>
The <em>build()</em> method's name (default: <code>"build"</code>)
</li><li>
The <em>builder()</em> method's name (default: <code>"builder"</code>)
+ </li><li>
+ If you want <code>toBuilder()</code> (default: no)
</li>
</ul>
Example usage where all options are changed from their defaults:<br />
- <code>@SuperBuilder(buildMethodName = "execute", builderMethodName = "helloWorld")</code><br />
+ <code>@SuperBuilder(buildMethodName = "execute", builderMethodName = "helloWorld", toBuilder = true)</code><br />
</p>
</@f.overview>
diff --git a/website/templates/setup/gwt.html b/website/templates/setup/gwt.html
index ac6659a5..ce45fe7f 100644
--- a/website/templates/setup/gwt.html
+++ b/website/templates/setup/gwt.html
@@ -18,7 +18,7 @@ java <strong>-javaagent:lombok.jar=ECJ</strong> <em>(rest of arguments)</em>
<p>
To use the GWT plugin in eclipse together with Lombok:<br />
Install lombok into eclipse as normal (start <code>lombok.jar</code> as an application).<br />
- Download the JDT variant that GWT uses. This is the most recent org.eclipse.jdt.core_VERSION-CUSTOM-GWT jar found on the <a href="https://github.com/gwtproject/tools/tree/master/lib/eclipse">lib/eclipse section GWT's github page</a>. As of October 2018, that would be <a href="https://github.com/gwtproject/tools/raw/master/lib/eclipse/org.eclipse.jdt.core_3.13.50-CUSTOM-GWT-20171215.jar">org.eclipse.jdt.core_3.13.50-CUSTOM-GWT-20171215.jar</a>.<br />
+ Download the JDT variant that GWT uses. For GWT v2.8.2 you'll need <a href="https://github.com/gwtproject/tools/raw/master/lib/eclipse/org.eclipse.jdt.core_3.11.2-CUSTOM-GWT-2.8-20160205.jar">org.eclipse.jdt.core 3.11.2</a>; for the current GWT development snapshot, this is <a href="https://github.com/gwtproject/tools/raw/master/lib/eclipse/org.eclipse.jdt.core_3.13.50-CUSTOM-GWT-20171215.jar">org.eclipse.jdt.core 3.13.50</a>. For more versions, search for jar files matching 'org.eclipse.jdt.core-{VERSION}-CUSTOM-GWT-{date}.jar in the <a href="https://github.com/gwtproject/tools/tree/master/lib/eclipse">lib/eclipse section GWT's github page</a>.<br />
Configure the 'GWT/Compile' option in the eclipse GWT plugin; in the advanced section, add the following VM arguments:<br />
<pre>-javaagent:/path/to/lombok.jar=ECJ
-Xbootclasspath/p:/path/to/lombok.jar:/path/to/org.eclipse.jdt.core_fromPreviousStep.jar</pre>
diff --git a/website/usageExamples/experimental/UtilityClassExample_post.jpage b/website/usageExamples/experimental/UtilityClassExample_post.jpage
index 70810230..ad6cf583 100644
--- a/website/usageExamples/experimental/UtilityClassExample_post.jpage
+++ b/website/usageExamples/experimental/UtilityClassExample_post.jpage
@@ -5,7 +5,7 @@ public final class UtilityClassExample {
throw new java.lang.UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
- public static void addSomething(int in) {
+ public static int addSomething(int in) {
return in + CONSTANT;
}
}
diff --git a/website/usageExamples/experimental/UtilityClassExample_pre.jpage b/website/usageExamples/experimental/UtilityClassExample_pre.jpage
index 85731b81..3c33a7c6 100644
--- a/website/usageExamples/experimental/UtilityClassExample_pre.jpage
+++ b/website/usageExamples/experimental/UtilityClassExample_pre.jpage
@@ -4,7 +4,7 @@ import lombok.experimental.UtilityClass;
public class UtilityClassExample {
private final int CONSTANT = 5;
- public void addSomething(int in) {
+ public int addSomething(int in) {
return in + CONSTANT;
}
}