aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/AllArgsConstructor.java28
-rw-r--r--src/core/lombok/Builder.java (renamed from src/core/lombok/experimental/Builder.java)78
-rw-r--r--src/core/lombok/Cleanup.java10
-rw-r--r--src/core/lombok/ConfigurationKeys.java181
-rw-r--r--src/core/lombok/Data.java6
-rw-r--r--src/core/lombok/Delegate.java6
-rw-r--r--src/core/lombok/EqualsAndHashCode.java29
-rw-r--r--src/core/lombok/Generated.java41
-rw-r--r--src/core/lombok/Getter.java23
-rw-r--r--src/core/lombok/Lombok.java33
-rw-r--r--src/core/lombok/NoArgsConstructor.java25
-rw-r--r--src/core/lombok/RequiredArgsConstructor.java27
-rw-r--r--src/core/lombok/Setter.java24
-rw-r--r--src/core/lombok/Singular.java38
-rw-r--r--src/core/lombok/SneakyThrows.java9
-rw-r--r--src/core/lombok/Synchronized.java6
-rw-r--r--src/core/lombok/ToString.java14
-rw-r--r--src/core/lombok/Value.java12
-rw-r--r--src/core/lombok/bytecode/AsmUtil.java2
-rw-r--r--src/core/lombok/bytecode/ClassFileMetaData.java7
-rw-r--r--src/core/lombok/bytecode/PostCompilerApp.java22
-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.java32
-rw-r--r--src/core/lombok/core/AgentLauncher.java (renamed from src/core/lombok/core/Agent.java)74
-rw-r--r--src/core/lombok/core/AnnotationProcessor.java26
-rw-r--r--src/core/lombok/core/GuavaTypeMap.java59
-rw-r--r--src/core/lombok/core/LombokInternalAliasing.java4
-rw-r--r--src/core/lombok/core/Main.java5
-rw-r--r--src/core/lombok/core/PostCompiler.java8
-rw-r--r--src/core/lombok/core/PublicApiCreatorApp.java2
-rw-r--r--src/core/lombok/core/TypeLibrary.java47
-rw-r--r--src/core/lombok/core/TypeResolver.java16
-rw-r--r--src/core/lombok/core/Version.java14
-rw-r--r--src/core/lombok/core/configuration/AllowHelper.java37
-rw-r--r--src/core/lombok/core/configuration/BubblingConfigurationResolver.java21
-rw-r--r--src/core/lombok/core/configuration/CallSuperType.java27
-rw-r--r--src/core/lombok/core/configuration/ConfigurationApp.java2
-rw-r--r--src/core/lombok/core/configuration/ConfigurationParser.java4
-rw-r--r--src/core/lombok/core/configuration/FileSystemSourceCache.java77
-rw-r--r--src/core/lombok/core/configuration/FlagUsageType.java2
-rw-r--r--src/core/lombok/core/debug/AssertionLogger.java115
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java42
-rw-r--r--src/core/lombok/core/handlers/Singulars.java86
-rw-r--r--src/core/lombok/core/handlers/singulars.txt56
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java8
-rw-r--r--src/core/lombok/eclipse/EclipseASTVisitor.java2
-rw-r--r--src/core/lombok/eclipse/EclipseNode.java48
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java2
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java402
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java349
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java678
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilderDefault.java46
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java195
-rw-r--r--src/core/lombok/eclipse/handlers/HandleData.java16
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java155
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldDefaults.java91
-rw-r--r--src/core/lombok/eclipse/handlers/HandleGetter.java14
-rw-r--r--src/core/lombok/eclipse/handlers/HandleHelper.java152
-rw-r--r--src/core/lombok/eclipse/handlers/HandleLog.java15
-rw-r--r--src/core/lombok/eclipse/handlers/HandleNonNull.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandlePrintAST.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSetter.java26
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java6
-rw-r--r--src/core/lombok/eclipse/handlers/HandleUtilityClass.java194
-rw-r--r--src/core/lombok/eclipse/handlers/HandleVal.java40
-rw-r--r--src/core/lombok/eclipse/handlers/HandleValue.java16
-rw-r--r--src/core/lombok/eclipse/handlers/HandleWither.java108
-rw-r--r--src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java667
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java58
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java56
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java261
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java51
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java189
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java129
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java313
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java52
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java300
-rw-r--r--src/core/lombok/experimental/Accessors.java18
-rw-r--r--src/core/lombok/experimental/Delegate.java8
-rw-r--r--src/core/lombok/experimental/ExtensionMethod.java10
-rw-r--r--src/core/lombok/experimental/FieldDefaults.java8
-rw-r--r--src/core/lombok/experimental/Helper.java (renamed from src/core/lombok/experimental/Value.java)33
-rw-r--r--src/core/lombok/experimental/NonFinal.java2
-rw-r--r--src/core/lombok/experimental/PackagePrivate.java2
-rw-r--r--src/core/lombok/experimental/UtilityClass.java41
-rw-r--r--src/core/lombok/experimental/Wither.java24
-rw-r--r--src/core/lombok/experimental/package-info.java4
-rw-r--r--src/core/lombok/experimental/var.java31
-rw-r--r--src/core/lombok/extern/apachecommons/CommonsLog.java15
-rw-r--r--src/core/lombok/extern/java/Log.java15
-rw-r--r--src/core/lombok/extern/jbosslog/JBossLog.java64
-rw-r--r--src/core/lombok/extern/log4j/Log4j.java15
-rw-r--r--src/core/lombok/extern/log4j/Log4j2.java17
-rw-r--r--src/core/lombok/extern/slf4j/Slf4j.java17
-rw-r--r--src/core/lombok/extern/slf4j/XSlf4j.java15
-rw-r--r--src/core/lombok/javac/CapturingDiagnosticListener.java4
-rw-r--r--src/core/lombok/javac/CompilerMessageSuppressor.java206
-rw-r--r--src/core/lombok/javac/HandlerLibrary.java13
-rw-r--r--src/core/lombok/javac/Javac8BasedLombokOptions.java4
-rw-r--r--src/core/lombok/javac/Javac9BasedLombokOptions.java48
-rw-r--r--src/core/lombok/javac/JavacAST.java282
-rw-r--r--src/core/lombok/javac/JavacASTAdapter.java4
-rw-r--r--src/core/lombok/javac/JavacASTVisitor.java5
-rw-r--r--src/core/lombok/javac/JavacAnnotationHandler.java7
-rw-r--r--src/core/lombok/javac/JavacImportList.java26
-rw-r--r--src/core/lombok/javac/JavacNode.java58
-rw-r--r--src/core/lombok/javac/JavacResolution.java80
-rw-r--r--src/core/lombok/javac/JavacTransformer.java9
-rw-r--r--src/core/lombok/javac/apt/EmptyLombokFileObject.java1
-rw-r--r--src/core/lombok/javac/apt/InterceptingJavaFileManager.java10
-rw-r--r--src/core/lombok/javac/apt/Javac9BaseFileObjectWrapper.java113
-rw-r--r--src/core/lombok/javac/apt/LombokFileObjects.java207
-rw-r--r--src/core/lombok/javac/apt/LombokProcessor.java402
-rw-r--r--src/core/lombok/javac/apt/Processor.java374
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java616
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilderDefault.java49
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java154
-rw-r--r--src/core/lombok/javac/handlers/HandleData.java17
-rw-r--r--src/core/lombok/javac/handlers/HandleDelegate.java10
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java188
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldDefaults.java85
-rw-r--r--src/core/lombok/javac/handlers/HandleGetter.java13
-rw-r--r--src/core/lombok/javac/handlers/HandleHelper.java151
-rw-r--r--src/core/lombok/javac/handlers/HandleLog.java15
-rw-r--r--src/core/lombok/javac/handlers/HandleNonNull.java3
-rw-r--r--src/core/lombok/javac/handlers/HandlePrintAST.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java35
-rw-r--r--src/core/lombok/javac/handlers/HandleSynchronized.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java6
-rw-r--r--src/core/lombok/javac/handlers/HandleUtilityClass.java161
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java70
-rw-r--r--src/core/lombok/javac/handlers/HandleValue.java27
-rw-r--r--src/core/lombok/javac/handlers/HandleWither.java112
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java557
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java310
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java58
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java56
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java222
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java51
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java162
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java120
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java229
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java57
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java196
-rw-r--r--src/core/lombok/package-info.java4
-rw-r--r--src/core/lombok/val.java4
-rw-r--r--src/core/lombok/var.java35
-rw-r--r--src/core9/module-info.java38
-rw-r--r--src/delombok/lombok/delombok/Delombok.java123
-rw-r--r--src/delombok/lombok/delombok/DelombokApp.java16
-rw-r--r--src/delombok/lombok/delombok/DelombokResult.java3
-rw-r--r--src/delombok/lombok/delombok/DocCommentIntegrator.java8
-rw-r--r--src/delombok/lombok/delombok/FormatPreferences.java15
-rw-r--r--src/delombok/lombok/delombok/LombokOptionsFactory.java13
-rw-r--r--src/delombok/lombok/delombok/PrettyCommentsPrinter.java1680
-rw-r--r--src/delombok/lombok/delombok/PrettyPrinter.java1524
-rw-r--r--src/delombok/lombok/delombok/UnicodeEscapeWriter.java2
-rw-r--r--src/delombok/lombok/delombok/ant/DelombokTask.java279
-rw-r--r--src/delombok/lombok/delombok/ant/DelombokTaskImpl.java91
-rw-r--r--src/delombok/lombok/delombok/ant/Format.java35
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java72
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java128
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java249
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java75
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java53
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java4
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java3
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java384
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java53
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java93
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java21
-rw-r--r--src/eclipseAgent/lombok/launch/PatchFixesHider.java671
-rw-r--r--src/installer/lombok/installer/IdeLocation.java7
-rw-r--r--src/installer/lombok/installer/IdeLocationProvider.java22
-rw-r--r--src/installer/lombok/installer/Installer.java19
-rw-r--r--src/installer/lombok/installer/InstallerGUI.java203
-rw-r--r--src/installer/lombok/installer/OsUtils.java (renamed from src/installer/lombok/installer/IdeFinder.java)21
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseLocationProvider.java144
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseProductDescriptor.java (renamed from src/installer/lombok/installer/eclipse/STSLocation.java)38
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseProductLocation.java (renamed from src/installer/lombok/installer/eclipse/EclipseLocation.java)87
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseProductLocationProvider.java (renamed from src/installer/lombok/installer/eclipse/EclipseFinder.java)262
-rw-r--r--src/installer/lombok/installer/eclipse/JbdsFinder.java70
-rw-r--r--src/installer/lombok/installer/eclipse/JbdsLocationProvider.java49
-rw-r--r--src/installer/lombok/installer/eclipse/MyEclipseLocationProvider.java (renamed from src/installer/lombok/installer/eclipse/JbdsLocation.java)33
-rw-r--r--src/installer/lombok/installer/eclipse/RhdsLocationProvider.java44
-rw-r--r--src/installer/lombok/installer/eclipse/STS4LocationProvider.java44
-rw-r--r--src/installer/lombok/installer/eclipse/STSFinder.java70
-rw-r--r--src/installer/lombok/installer/eclipse/STSLocationProvider.java48
-rw-r--r--src/installer/lombok/installer/eclipse/StandardProductDescriptor.java158
-rw-r--r--src/installer/lombok/installer/eclipse/myeclipse.pngbin0 -> 1073 bytes
-rw-r--r--src/installer/lombok/installer/eclipse/rhds.pngbin0 -> 3470 bytes
-rw-r--r--src/j9stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java48
-rw-r--r--src/launch/lombok/launch/Agent.java47
-rw-r--r--src/launch/lombok/launch/AnnotationProcessor.java129
-rw-r--r--src/launch/lombok/launch/Main.java41
-rw-r--r--src/launch/lombok/launch/ShadowClassLoader.java485
-rw-r--r--src/stubs/com/sun/tools/javac/code/Symbol.java84
-rw-r--r--src/stubs/com/sun/tools/javac/code/Symtab.java20
-rw-r--r--src/stubs/com/sun/tools/javac/file/BaseFileManager.java18
-rw-r--r--src/stubs/com/sun/tools/javac/file/PathFileObject.java12
-rw-r--r--src/stubs/com/sun/tools/javac/main/Arguments.java13
-rw-r--r--src/stubs/com/sun/tools/javac/main/JavaCompiler.java37
-rw-r--r--src/stubs/com/sun/tools/javac/main/Option.java1
-rw-r--r--src/stubs/com/sun/tools/javac/parser/JavacParser.java6
-rw-r--r--src/stubs/com/sun/tools/javac/util/Options.java20
-rw-r--r--src/stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java48
-rw-r--r--src/stubsstubs/com/sun/tools/javac/code/Attribute.java15
-rw-r--r--src/stubsstubs/com/sun/tools/javac/code/Type.java3
-rw-r--r--src/stubsstubs/com/sun/tools/javac/comp/Todo.java3
-rw-r--r--src/stubsstubs/com/sun/tools/javac/main/JavacOption.java5
-rw-r--r--src/stubsstubs/com/sun/tools/javac/main/Option.java3
-rw-r--r--src/stubsstubs/com/sun/tools/javac/main/OptionName.java3
-rw-r--r--src/stubsstubs/com/sun/tools/javac/util/Context.java5
-rw-r--r--src/stubsstubs/com/sun/tools/javac/util/List.java3
-rw-r--r--src/stubsstubs/com/sun/tools/javac/util/Name.java8
-rw-r--r--src/testAP/org/projectlombok/testAp/ExampleAnnotation.java10
-rw-r--r--src/testAP/org/projectlombok/testAp/TestAp.java77
-rw-r--r--src/useTestAP/UseTestAp.java13
-rw-r--r--src/utils/lombok/core/LombokImmutableList.java6
-rw-r--r--src/utils/lombok/eclipse/Eclipse.java19
-rw-r--r--src/utils/lombok/javac/CommentCatcher.java4
-rw-r--r--src/utils/lombok/javac/Javac.java55
-rw-r--r--src/utils/lombok/javac/JavacTreeMaker.java1
-rw-r--r--src/utils/lombok/javac/PackageName.java55
-rw-r--r--src/utils/lombok/javac/TreeMirrorMaker.java33
-rw-r--r--src/utils/lombok/javac/java8/CommentCollectingParserFactory.java10
-rw-r--r--src/utils/lombok/javac/java9/CommentCollectingParser.java53
-rw-r--r--src/utils/lombok/javac/java9/CommentCollectingParserFactory.java70
-rw-r--r--src/website/log4j.properties6
-rw-r--r--src/website/lombok/website/CompileChangelog.java147
-rw-r--r--src/website/lombok/website/FetchCurrentVersion.java33
-rw-r--r--src/website/lombok/website/WebsiteMaker.java403
233 files changed, 15782 insertions, 5219 deletions
diff --git a/src/core/lombok/AllArgsConstructor.java b/src/core/lombok/AllArgsConstructor.java
index cc494967..c059c65d 100644
--- a/src/core/lombok/AllArgsConstructor.java
+++ b/src/core/lombok/AllArgsConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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,7 +30,7 @@ import java.lang.annotation.Target;
* Generates an all-args constructor.
* An all-args constructor requires one argument for every field in the class.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Constructor.html">the project lombok features page for &#64;Constructor</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for &#64;Constructor</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details.
*
@@ -45,31 +45,33 @@ public @interface AllArgsConstructor {
* is generated with the same argument list that wraps the real constructor.
*
* Such a static 'constructor' is primarily useful as it infers type arguments.
+ *
+ * @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticName() default "";
/**
- * Any annotations listed here are put on the generated constructor. The syntax for this feature is: {@code @AllArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}
+ * Any annotations listed here are put on the generated constructor.
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @AllArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @AllArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}.
+ *
+ * @return List of annotations to apply to the generated constructor.
*/
AnyAnnotation[] onConstructor() default {};
/**
* Sets the access level of the constructor. By default, generated constructors are {@code public}.
- */
- AccessLevel access() default lombok.AccessLevel.PUBLIC;
-
- /**
- * Constructors are generated with the {@link java.beans.ConstructorProperties} annotation.
- * However, this annotation is new in 1.6 which means those compiling for 1.5 will need
- * to set this value to true.
*
- * @deprecated THIS FEATURE WILL BE REMOVED after March 31st 2015. Use configuration key {@link ConfigurationKeys#ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES} instead.
+ * @return The constructor will be generated with this access modifier.
*/
- @Deprecated
- boolean suppressConstructorProperties() default false;
+ AccessLevel access() default lombok.AccessLevel.PUBLIC;
/**
* Placeholder annotation to enable the placement of annotations on the generated code.
+ *
* @deprecated Don't use this annotation, ever - Read the documentation.
*/
@Deprecated
diff --git a/src/core/lombok/experimental/Builder.java b/src/core/lombok/Builder.java
index 1300e7d3..a16717cc 100644
--- a/src/core/lombok/experimental/Builder.java
+++ b/src/core/lombok/Builder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Project Lombok Authors.
+ * Copyright (C) 2013-2017 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
@@ -19,7 +19,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package lombok.experimental;
+package lombok;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@@ -31,25 +31,25 @@ import java.lang.annotation.Target;
* The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class
* that contains a member which is annotated with {@code @Builder}.
* <p>
- * If a member is annotated, it must be either a constructor or a static method. If a class is annotated,
+ * If a member is annotated, it must be either a constructor or a method. If a class is annotated,
* then a private constructor is generated with all fields as arguments
* (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present
* on the class), and it is as if this constructor has been annotated with {@code @Builder} instead.
* <p>
* The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>,
- * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the static
+ * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the
* method named {@code builder()} which is also generated for you in the class itself (not in the builder class).
* <p>
* The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
- * constructor / static method (each field, when annotating a class), which returns the builder itself.
+ * constructor / method (each field, when annotating a class), which returns the builder itself.
* The builder also has a <code>build()</code> method which returns a completed instance of the original type,
* created by passing all parameters as set via the various other methods in the builder to the constructor
- * or static method that was annotated with {@code @Builder}. The return type of this method will be the same
- * as the relevant class, unless a static method has been annotated, in which case it'll be equal to the
+ * or method that was annotated with {@code @Builder}. The return type of this method will be the same
+ * as the relevant class, unless a method has been annotated, in which case it'll be equal to the
* return type of that method.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for &#64;Builder</a>.
- * <p>
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Builder">the project lombok features page for &#64;Builder</a>.
+ * <br>
* <p>
* Before:
*
@@ -107,31 +107,65 @@ import java.lang.annotation.Target;
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
- /** Name of the static method that creates a new builder instance. Default: {@code builder}. */
+ /**
+ * The field annotated with {@code @Default} must have an initializing expression; that expression is taken as the default to be used if not explicitly set during building.
+ */
+ @Target(FIELD)
+ @Retention(SOURCE)
+ public @interface Default {}
+
+ /** @return Name of the method that creates a new builder instance. Default: {@code builder}. */
String builderMethodName() default "builder";
- /** Name of the instance method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
+ /** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
String buildMethodName() default "build";
- /** Name of the builder class.
+ /**
+ * Name of the builder class.
+ *
* Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
- * Default for {@code @Builder} on static methods: {@code (ReturnTypeName)Builder}.
+ * <p>
+ * Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}.
+ *
+ * @return Name of the builder class that will be generated (or if it already exists, will be filled with builder elements).
*/
String builderClassName() default "";
/**
- * Normally the builder's 'set' methods are fluent, meaning, they have the same name as the field. Set this
- * to {@code false} to name the setter method for field {@code someField}: {@code setSomeField}.
- * <p>
- * <strong>Default: true</strong>
+ * If true, generate an instance method to obtain a builder that is initialized with the values of this instance.
+ * Legal only if {@code @Builder} is used on a constructor, on the type itself, or on a static method that returns
+ * an instance of the declaring type.
+ *
+ * @return Whether to generate a {@code toBuilder()} method.
*/
- boolean fluent() default true;
+ boolean toBuilder() default false;
/**
- * Normally the builder's 'set' methods are chaining, meaning, they return the builder so that you can chain
- * calls to set methods. Set this to {@code false} to have these 'set' methods return {@code void} instead.
+ * Put on a field (in case of {@code @Builder} on a type) or a parameter (for {@code @Builder} on a constructor or static method) to
+ * indicate how lombok should obtain a value for this field or parameter given an instance; this is only relevant if {@code toBuilder} is {@code true}.
+ *
+ * You do not need to supply an {@code @ObtainVia} annotation unless you wish to change the default behaviour: Use a field with the same name.
+ * <p>
+ * Note that one of {@code field} or {@code method} should be set, or an error is generated.
* <p>
- * <strong>Default: true</strong>
+ * The default behaviour is to obtain a value by referencing the name of the parameter as a field on 'this'.
*/
- boolean chain() default true;
+ @Target({FIELD, PARAMETER})
+ @Retention(SOURCE)
+ public @interface ObtainVia {
+ /**
+ * @return Tells lombok to obtain a value with the expression {@code this.value}.
+ */
+ String field() default "";
+
+ /**
+ * @return Tells lombok to obtain a value with the expression {@code this.method()}.
+ */
+ String method() default "";
+
+ /**
+ * @return Tells lombok to obtain a value with the expression {@code SelfType.method(this)}; requires {@code method} to be set.
+ */
+ boolean isStatic() default false;
+ }
}
diff --git a/src/core/lombok/Cleanup.java b/src/core/lombok/Cleanup.java
index 4b5c6fc2..a3a1c198 100644
--- a/src/core/lombok/Cleanup.java
+++ b/src/core/lombok/Cleanup.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -31,7 +31,7 @@ import java.lang.annotation.Target;
* of what happens. Implemented by wrapping all statements following the local variable declaration to the
* end of your scope into a try block that, as a finally action, closes the resource.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Cleanup.html">the project lombok features page for &#64;Cleanup</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Cleanup">the project lombok features page for &#64;Cleanup</a>.
* <p>
* Example:
* <pre>
@@ -61,10 +61,10 @@ import java.lang.annotation.Target;
* outStream.write(b, 0, r);
* }
* } finally {
- * if (out != null) out.close();
+ * if (outStream != null) outStream.close();
* }
* } finally {
- * if (in != null) in.close();
+ * if (inStream != null) inStream.close();
* }
* }
* </pre>
@@ -72,6 +72,6 @@ import java.lang.annotation.Target;
@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.SOURCE)
public @interface Cleanup {
- /** The name of the method that cleans up the resource. By default, 'close'. The method must not have any parameters. */
+ /** @return The name of the method that cleans up the resource. By default, 'close'. The method must not have any parameters. */
String value() default "close";
}
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java
index 301563b8..29c43d3f 100644
--- a/src/core/lombok/ConfigurationKeys.java
+++ b/src/core/lombok/ConfigurationKeys.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-2017 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
@@ -23,6 +23,7 @@ package lombok;
import java.util.List;
+import lombok.core.configuration.CallSuperType;
import lombok.core.configuration.ConfigurationKey;
import lombok.core.configuration.FlagUsageType;
import lombok.core.configuration.NullCheckExceptionType;
@@ -35,6 +36,47 @@ public class ConfigurationKeys {
// ##### main package features #####
+ // ----- global -----
+
+ /**
+ * lombok configuration: {@code lombok.addGeneratedAnnotation} = {@code true} | {@code false}.
+ *
+ * If {@code true}, lombok generates {@code @javax.annotation.Generated("lombok")} on all fields, methods, and types that are generated, unless {@code lombok.addJavaxGeneratedAnnotation} is set.
+ * <br>
+ * <em>BREAKING CHANGE</em>: Starting with lombok v2.0.0, defaults to {@code false} instead of {@code true}, as this annotation is broken in JDK9.
+ *
+ * @see ConfigurationKeys#ADD_JAVAX_GENERATED_ANNOTATIONS
+ * @see ConfigurationKeys#ADD_LOMBOK_GENERATED_ANNOTATIONS
+ * @deprecated Since version 1.16.14, use {@link #ADD_JAVAX_GENERATED_ANNOTATIONS} instead.
+ */
+ @Deprecated
+ public static final ConfigurationKey<Boolean> ADD_GENERATED_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.addGeneratedAnnotation", "Generate @javax.annotation.Generated on all generated code (default: false). Deprecated, use 'lombok.addJavaxGeneratedAnnotation' instead.") {};
+
+ /**
+ * lombok configuration: {@code lombok.addJavaxGeneratedAnnotation} = {@code true} | {@code false}.
+ *
+ * If {@code true}, lombok generates {@code @javax.annotation.Generated("lombok")} on all fields, methods, and types that are generated.
+ * <br>
+ * <em>BREAKING CHANGE</em>: Starting with lombok v2.0.0, defaults to {@code false} instead of {@code true}, as this annotation is broken in JDK9.
+ */
+ public static final ConfigurationKey<Boolean> ADD_JAVAX_GENERATED_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.addJavaxGeneratedAnnotation", "Generate @javax.annotation.Generated on all generated code (default: follow lombok.addGeneratedAnnotation).") {};
+
+ /**
+ * lombok configuration: {@code lombok.addLombokGeneratedAnnotation} = {@code true} | {@code false}.
+ *
+ * If {@code true}, lombok generates {@code @lombok.Generated} on all fields, methods, and types that are generated.
+ */
+ public static final ConfigurationKey<Boolean> ADD_LOMBOK_GENERATED_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.addLombokGeneratedAnnotation", "Generate @lombok.Generated on all generated code (default: false).") {};
+
+ /**
+ * lombok configuration: {@code lombok.extern.findbugs.addSuppressFBWarnings} = {@code true} | {@code false}.
+ *
+ * If {@code true}, lombok generates {@code edu.umd.cs.findbugs.annotations.SuppressFBWarnings} on all fields, methods, and types that are generated.
+ *
+ * NB: If you enable this option, findbugs must be on the source or classpath, or you'll get errors that the type {@code SuppressFBWarnings} cannot be found.
+ */
+ public static final ConfigurationKey<Boolean> ADD_FINDBUGS_SUPPRESSWARNINGS_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.extern.findbugs.addSuppressFBWarnings", "Generate @edu.umd.cs.findbugs.annotations.SuppressFBWArnings on all generated code (default: false).") {};
+
// ----- *ArgsConstructor -----
/**
@@ -51,10 +93,25 @@ public class ConfigurationKeys {
* To suppress the generation of it, set this configuration to {@code true}.
*
* NB: GWT projects, and probably android projects, should explicitly set this key to {@code true} for the entire project.
+ *
+ * <br>
+ * <em>BREAKING CHANGE</em>: Starting with lombok v2.0.0, defaults to {@code false} instead of {@code true}, as {@code @ConstructorProperties} requires extra modules in JDK9.
+ *
+ * @see ConfigurationKeys#ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES
+ * @deprecated Since version 2.0, use {@link #ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES} instead.
*/
+ @Deprecated
public static final ConfigurationKey<Boolean> ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES = new ConfigurationKey<Boolean>("lombok.anyConstructor.suppressConstructorProperties", "Suppress the generation of @ConstructorProperties for generated constructors (default: false).") {};
/**
+ * lombok configuration: {@code lombok.anyConstructor.addConstructorProperties} = {@code true} | {@code false}.
+ *
+ * If {@code true}, all generated constructors with at least 1 argument get a {@code @ConstructorProperties}.
+ *
+ */
+ public static final ConfigurationKey<Boolean> ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES = new ConfigurationKey<Boolean>("lombok.anyConstructor.addConstructorProperties", "Generate @ConstructorProperties for generated constructors (default: false).") {};
+
+ /**
* lombok configuration: {@code lombok.allArgsConstructor.flagUsage} = {@code WARNING} | {@code ERROR}.
*
* If set, <em>any</em> usage of {@code @AllArgsConstructor} results in a warning / error.
@@ -133,9 +190,16 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.equalsAndHashCode.doNotUseGetters} = {@code true} | {@code false}.
*
- * For any class without an {@code @EqualsAndHashCode} that explicitly defines the {@code doNotUseGetters} option, this value is used.
+ * For any class without an {@code @EqualsAndHashCode} that explicitly defines the {@code doNotUseGetters} option, this value is used (default = false).
*/
- public static final ConfigurationKey<Boolean> EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.equalsAndHashCode.doNotUseGetters", "Don't call the getters but use the fields directly in the generated equalsAndHashCode method.") {};
+ public static final ConfigurationKey<Boolean> EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.equalsAndHashCode.doNotUseGetters", "Don't call the getters but use the fields directly in the generated equals and hashCode method (default = false).") {};
+
+ /**
+ * lombok configuration: {@code lombok.equalsAndHashCode.callSuper} = {@code call} | {@code ignore} | {@code warn}.
+ *
+ * For any class with an {@code @EqualsAndHashCode} annotation which extends a class other than {@code java.lang.Object}, should a call to superclass's implementation of {@code equals} and {@code hashCode} be included in the generated methods? (Default = warn)
+ */
+ public static final ConfigurationKey<CallSuperType> EQUALS_AND_HASH_CODE_CALL_SUPER = new ConfigurationKey<CallSuperType>("lombok.equalsAndHashCode.callSuper", "When generating equals and hashCode for classes that don't extend Object, either automatically take into account superclass implementation (call), or don't (skip), or warn and don't (warn). (default = warn).") {};
/**
* lombok configuration: {@code lombok.equalsAndHashCode.flagUsage} = {@code WARNING} | {@code ERROR}.
@@ -149,9 +213,9 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.toString.doNotUseGetters} = {@code true} | {@code false}.
*
- * For any class without an {@code @ToString} that explicitly defines the {@code doNotUseGetters} option, this value is used.
+ * For any class without an {@code @ToString} that explicitly defines the {@code doNotUseGetters} option, this value is used (default = false).
*/
- public static final ConfigurationKey<Boolean> TO_STRING_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.toString.doNotUseGetters", "Don't call the getters but use the fields directly in the generated toString method.") {};
+ public static final ConfigurationKey<Boolean> TO_STRING_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.toString.doNotUseGetters", "Don't call the getters but use the fields directly in the generated toString method (default = false).") {};
/**
* lombok configuration: {@code lombok.toString.flagUsage} = {@code WARNING} | {@code ERROR}.
@@ -163,9 +227,35 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.toString.includeFieldNames} = {@code true} | {@code false}.
*
- * For any class without an {@code @ToString} that explicitly defines the {@code includeFieldNames} option, this value is used.
+ * For any class without an {@code @ToString} that explicitly defines the {@code includeFieldNames} option, this value is used (default = true).
+ */
+ public static final ConfigurationKey<Boolean> TO_STRING_INCLUDE_FIELD_NAMES = new ConfigurationKey<Boolean>("lombok.toString.includeFieldNames", "Include the field names in the generated toString method (default = true).") {};
+
+ // ----- Builder -----
+
+ /**
+ * lombok configuration: {@code lombok.builder.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @Builder} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> BUILDER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.builder.flagUsage", "Emit a warning or error if @Builder is used.") {};
+
+ // ----- Singular -----
+
+ /**
+ * lombok configuration: {@code lombok.singular.useGuava} = {@code true} | {@code false}.
+ *
+ * If explicitly set to {@code true}, guava's {@code ImmutableList} etc are used to implement the immutable collection datatypes generated by @Singular @Builder for fields/parameters of type {@code java.util.List} and such.
+ * By default, unmodifiable-wrapped versions of {@code java.util} types are used.
*/
- public static final ConfigurationKey<Boolean> TO_STRING_INCLUDE_FIELD_NAMES = new ConfigurationKey<Boolean>("lombok.toString.includeFieldNames", "Include the field names in the generated toString method.") {};
+ public static final ConfigurationKey<Boolean> SINGULAR_USE_GUAVA = new ConfigurationKey<Boolean>("lombok.singular.useGuava", "Generate backing immutable implementations for @Singular on java.util.* types by using guava's ImmutableList, etc. Normally java.util's mutable types are used and wrapped to make them immutable.") {};
+
+ /**
+ * lombok configuration: {@code lombok.singular.auto} = {@code true} | {@code false}.
+ *
+ * By default or if explicitly set to {@code true}, lombok will attempt to automatically singularize the name of your variable/parameter when using {@code @Singular}; the name is assumed to be written in english, and a plural. If explicitly to {@code false}, you must always specify the singular form; this is especially useful if your identifiers are in a foreign language.
+ */
+ public static final ConfigurationKey<Boolean> SINGULAR_AUTO = new ConfigurationKey<Boolean>("lombok.singular.auto", "If true (default): Automatically singularize the assumed-to-be-plural name of your variable/parameter when using {@code @Singular}.") {};
// ##### Standalones #####
@@ -190,11 +280,11 @@ public class ConfigurationKeys {
// ----- NonNull -----
/**
- * lombok configuration: {@code lombok.nonNull.exceptionType} = &lt;String: <em>a java exception type; either [{@code IllegalArgumentException} or: {@code NullPointerException}].
+ * lombok configuration: {@code lombok.nonNull.exceptionType} = &lt;String: <em>a java exception type</em>; either [{@code IllegalArgumentException} or: {@code NullPointerException}].
*
* Sets the exception to throw if {@code @NonNull} is applied to a method parameter, and a caller passes in {@code null}.
*/
- public static final ConfigurationKey<NullCheckExceptionType> NON_NULL_EXCEPTION_TYPE = new ConfigurationKey<NullCheckExceptionType>("lombok.nonNull.exceptionType", "The type of the exception to throw if a passed-in argument is null. Default: NullPointerException.") {};
+ public static final ConfigurationKey<NullCheckExceptionType> NON_NULL_EXCEPTION_TYPE = new ConfigurationKey<NullCheckExceptionType>("lombok.nonNull.exceptionType", "The type of the exception to throw if a passed-in argument is null (Default: NullPointerException).") {};
/**
* lombok configuration: {@code lombok.nonNull.flagUsage} = {@code WARNING} | {@code ERROR}.
@@ -231,7 +321,8 @@ public class ConfigurationKeys {
* If set, <em>any</em> usage of {@code val} results in a warning / error.
*/
public static final ConfigurationKey<FlagUsageType> VAL_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.val.flagUsage", "Emit a warning or error if 'val' is used.") {};
-
+ public static final ConfigurationKey<FlagUsageType> VAR_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.var.flagUsage", "Emit a warning or error if 'var' is used.") {};
+
// ##### Extern #####
// ----- Logging -----
@@ -285,11 +376,18 @@ public class ConfigurationKeys {
public static final ConfigurationKey<FlagUsageType> LOG_XSLF4J_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.xslf4j.flagUsage", "Emit a warning or error if @XSlf4j is used.") {};
/**
+ * lombok configuration: {@code lombok.log.jbosslog.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @JBossLog} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> LOG_JBOSSLOG_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.jbosslog.flagUsage", "Emit a warning or error if @JBossLog is used.") {};
+
+ /**
* lombok configuration: {@code lombok.log.fieldName} = &lt;String: aJavaIdentifier&gt; (Default: {@code log}).
*
* If set the various log annotations (which make a log field) will use the stated identifier instead of {@code log} as a name.
*/
- public static final ConfigurationKey<String> LOG_ANY_FIELD_NAME = new ConfigurationKey<String>("lombok.log.fieldName", "Use this name for the generated logger fields (default: 'log')") {};
+ public static final ConfigurationKey<String> LOG_ANY_FIELD_NAME = new ConfigurationKey<String>("lombok.log.fieldName", "Use this name for the generated logger fields (default: 'log').") {};
/**
* lombok configuration: {@code lombok.log.fieldIsStatic} = {@code true} | {@code false}.
@@ -329,25 +427,16 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.accessors.chain} = {@code true} | {@code false}.
*
- * For any class without an {@code @Accessors} that explicitly defines the {@code chain} option, this value is used.
+ * For any class without an {@code @Accessors} that explicitly defines the {@code chain} option, this value is used (default = false).
*/
- public static final ConfigurationKey<Boolean> ACCESSORS_CHAIN = new ConfigurationKey<Boolean>("lombok.accessors.chain", "Generate setters that return 'this' instead of 'void'.") {};
+ public static final ConfigurationKey<Boolean> ACCESSORS_CHAIN = new ConfigurationKey<Boolean>("lombok.accessors.chain", "Generate setters that return 'this' instead of 'void' (default: false).") {};
/**
* lombok configuration: {@code lombok.accessors.fluent} = {@code true} | {@code false}.
*
- * For any class without an {@code @Accessors} that explicitly defines the {@code fluent} option, this value is used.
- */
- public static final ConfigurationKey<Boolean> ACCESSORS_FLUENT = new ConfigurationKey<Boolean>("lombok.accessors.fluent", "Generate getters and setters using only the field name (no get/set prefix).") {};
-
- // ----- Builder -----
-
- /**
- * lombok configuration: {@code lombok.builder.flagUsage} = {@code WARNING} | {@code ERROR}.
- *
- * If set, <em>any</em> usage of {@code @Builder} results in a warning / error.
+ * For any class without an {@code @Accessors} that explicitly defines the {@code fluent} option, this value is used (default = false).
*/
- public static final ConfigurationKey<FlagUsageType> BUILDER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.builder.flagUsage", "Emit a warning or error if @Builder is used.") {};
+ public static final ConfigurationKey<Boolean> ACCESSORS_FLUENT = new ConfigurationKey<Boolean>("lombok.accessors.fluent", "Generate getters and setters using only the field name (no get/set prefix) (default: false).") {};
// ----- ExtensionMethod -----
@@ -361,12 +450,55 @@ public class ConfigurationKeys {
// ----- FieldDefaults -----
/**
+ * lombok configuration: {@code lombok.fieldDefaults.defaultPrivate} = {@code true} | {@code false}.
+ *
+ * If set to <code>true</code> <em>any</em> field without an access modifier or {@code @PackagePrivate} is marked as {@code private} by lombok, in all source files compiled.
+ */
+ public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_PRIVATE_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultPrivate", "If true, fields without any access modifier, in any file (lombok annotated or not) are marked as private. Use @PackagePrivate or an explicit modifier to override this.") {};
+
+ /**
+ * lombok configuration: {@code lombok.fieldDefaults.defaultFinal} = {@code true} | {@code false}.
+ *
+ * If set to <code>true</code> <em>any</em> field without {@code @NonFinal} is marked as {@code final} by lombok, in all source files compiled.
+ */
+ public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_FINAL_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultFinal", "If true, fields, in any file (lombok annotated or not) are marked as final. Use @NonFinal to override this.") {};
+
+ /**
* lombok configuration: {@code lombok.fieldDefaults.flagUsage} = {@code WARNING} | {@code ERROR}.
*
* If set, <em>any</em> usage of {@code @FieldDefaults} results in a warning / error.
*/
public static final ConfigurationKey<FlagUsageType> FIELD_DEFAULTS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.fieldDefaults.flagUsage", "Emit a warning or error if @FieldDefaults is used.") {};
+ // ----- Helper -----
+
+ /**
+ * lombok configuration: {@code lombok.helper.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @Helper} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> HELPER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.helper.flagUsage", "Emit a warning or error if @Helper is used.") {};
+
+ // ----- onX -----
+
+ /**
+ * lombok configuration: {@code lombok.onX.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code onX} results in a warning / error.
+ * <br>
+ * Specifically, this flags usage of {@code @Getter(onMethod=...)}, {@code @Setter(onParam=...)}, {@code @Setter(onMethod=...)}, {@code @XArgsConstructor(onConstructor=...)}.
+ */
+ public static final ConfigurationKey<FlagUsageType> ON_X_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.onX.flagUsage", "Emit a warning or error if onX is used.") {};
+
+ // ----- UtilityClass -----
+
+ /**
+ * lombok configuration: {@code lombok.utilityClass.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @UtilityClass} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> UTILITY_CLASS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.utilityClass.flagUsage", "Emit a warning or error if @UtilityClass is used.") {};
+
// ----- Wither -----
/**
@@ -376,7 +508,6 @@ public class ConfigurationKeys {
*/
public static final ConfigurationKey<FlagUsageType> WITHER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.wither.flagUsage", "Emit a warning or error if @Wither is used.") {};
-
// ----- Configuration System -----
/**
diff --git a/src/core/lombok/Data.java b/src/core/lombok/Data.java
index bbc8d920..ffa968c1 100644
--- a/src/core/lombok/Data.java
+++ b/src/core/lombok/Data.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,7 +32,7 @@ import java.lang.annotation.Target;
* <p>
* Equivalent to {@code @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Data.html">the project lombok features page for &#64;Data</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Data">the project lombok features page for &#64;Data</a>.
*
* @see Getter
* @see Setter
@@ -54,6 +54,8 @@ public @interface Data {
* </pre>
*
* Default: No static constructor, instead the normal constructor is public.
+ *
+ * @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticConstructor() default "";
}
diff --git a/src/core/lombok/Delegate.java b/src/core/lombok/Delegate.java
index d5b4b48c..0c5a4c2c 100644
--- a/src/core/lombok/Delegate.java
+++ b/src/core/lombok/Delegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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,8 @@ public @interface Delegate {
* type listed here is used only to determine which delegate methods to generate.
*
* NB: All methods in {@code Object}, as well as {@code canEqual(Object other)} will never be delegated.
+ *
+ * @return For each method (not already in {@code java.lang.Object}) in these types, generate a delegate method.
*/
Class<?>[] types() default {};
@@ -47,6 +49,8 @@ public @interface Delegate {
* Each method in any of the types listed here (include supertypes) will <em>not</em> be delegated.
*
* NB: All methods in {@code Object}, as well as {@code canEqual(Object other)} will never be delegated.
+ *
+ * @return For each method (not already in {@code java.lang.Object}) in these types, skip generating a delegate method (overrides {@code types()}).
*/
Class<?>[] excludes() default {};
}
diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java
index dbce23b8..2f88ac50 100644
--- a/src/core/lombok/EqualsAndHashCode.java
+++ b/src/core/lombok/EqualsAndHashCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -29,15 +29,16 @@ import java.lang.annotation.Target;
/**
* Generates implementations for the {@code equals} and {@code hashCode} methods inherited by all objects, based on relevant fields.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/EqualsAndHashCode.html">the project lombok features page for &#64;EqualsAndHashCode</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/EqualsAndHashCode">the project lombok features page for &#64;EqualsAndHashCode</a>.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface EqualsAndHashCode {
/**
- * Any fields listed here will not be taken into account in the generated
- * {@code equals} and {@code hashCode} implementations.
+ * Any fields listed here will not be taken into account in the generated {@code equals} and {@code hashCode} implementations.
* Mutually exclusive with {@link #of()}.
+ *
+ * @return A list of fields to exclude.
*/
String[] exclude() default {};
@@ -46,25 +47,37 @@ public @interface EqualsAndHashCode {
* Normally, all non-static, non-transient fields are used for identity.
* <p>
* Mutually exclusive with {@link #exclude()}.
+ *
+ * @return A list of fields to use (<em>default</em>: all of them).
*/
String[] of() default {};
/**
- * Call on the superclass's implementations of {@code equals} and {@code hashCode} before calculating
- * for the fields in this class.
+ * Call on the superclass's implementations of {@code equals} and {@code hashCode} before calculating for the fields in this class.
* <strong>default: false</strong>
+ *
+ * @return Whether to call the superclass's {@code equals} implementation as part of the generated equals algorithm.
*/
boolean callSuper() default false;
/**
* Normally, if getters are available, those are called. To suppress this and let the generated code use the fields directly, set this to {@code true}.
* <strong>default: false</strong>
+ *
+ * @return If {@code true}, always use direct field access instead of calling the getter method.
*/
boolean doNotUseGetters() default false;
/**
- * Any annotations listed here are put on the generated parameter of {@code equals} and {@code canEqual}. The syntax for this feature is: {@code @EqualsAndHashCode(onParam=@__({@AnnotationsGoHere}))}
- * This is useful to add for example a {@code Nullable} annotation.
+ * Any annotations listed here are put on the generated parameter of {@code equals} and {@code canEqual}.
+ * This is useful to add for example a {@code Nullable} annotation.<br>
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @EqualsAndHashCode(onParam=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @EqualsAndHashCode(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}.
+ *
+ * @return List of annotations to apply to the generated parameter in the {@code equals()} method.
*/
AnyAnnotation[] onParam() default {};
diff --git a/src/core/lombok/Generated.java b/src/core/lombok/Generated.java
new file mode 100644
index 00000000..b99a1b58
--- /dev/null
+++ b/src/core/lombok/Generated.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015-2016 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Lombok will eventually automatically add this annotation to all generated constructors, methods, fields, and types.
+ *
+ * You can mark the presence of this annotation as 'ignore it' for all code style and bug finding tools.
+ * <p>
+ * NB: As of v1.16.2 which introduces this annotation, lombok doesn't actually add this annotation; we're setting
+ * it up so that lombok jars in widespread use start having this, which will make it easier to actually apply it
+ * later on.
+ */
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface Generated {
+}
diff --git a/src/core/lombok/Getter.java b/src/core/lombok/Getter.java
index 428f53ef..5a23fe30 100644
--- a/src/core/lombok/Getter.java
+++ b/src/core/lombok/Getter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Put on any field to make lombok build a standard getter.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/GetterSetter.html">the project lombok features page for &#64;Getter and &#64;Setter</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/GetterSetter">the project lombok features page for &#64;Getter and &#64;Setter</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onMethod} parameter. See the full documentation for more details.
* <p>
@@ -54,20 +54,29 @@ import java.lang.annotation.Target;
public @interface Getter {
/**
* If you want your getter to be non-public, you can specify an alternate access level here.
+ *
+ * @return The getter method will be generated with this access modifier.
*/
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
/**
- * Any annotations listed here are put on the generated method. The syntax for this feature is: {@code @Getter(onMethod=@__({@AnnotationsGoHere}))}
+ * Any annotations listed here are put on the generated method.
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @Getter(onMethod=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @Getter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}.
+ *
+ * @return List of annotations to apply to the generated getter method.
*/
- AnyAnnotation[] onMethod() default @AnyAnnotation;
+ AnyAnnotation[] onMethod() default {};
boolean lazy() default false;
/**
- * Placeholder annotation to enable the placement of annotations on the generated code.
- * @deprecated Don't use this annotation, ever - Read the documentation.
- */
+ * Placeholder annotation to enable the placement of annotations on the generated code.
+ * @deprecated Don't use this annotation, ever - Read the documentation.
+ */
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
diff --git a/src/core/lombok/Lombok.java b/src/core/lombok/Lombok.java
index 07fd083d..d86b6d1c 100644
--- a/src/core/lombok/Lombok.java
+++ b/src/core/lombok/Lombok.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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,7 +30,6 @@ public class Lombok {
* The exception is still thrown - javac will just stop whining about it.
* <p>
* Example usage:
- * <p>
* <pre>public void run() {
* throw sneakyThrow(new IOException("You don't need to catch me!"));
* }</pre>
@@ -49,26 +48,38 @@ public class Lombok {
*/
public static RuntimeException sneakyThrow(Throwable t) {
if (t == null) throw new NullPointerException("t");
- Lombok.<RuntimeException>sneakyThrow0(t);
- return null;
+ return Lombok.<RuntimeException>sneakyThrow0(t);
}
@SuppressWarnings("unchecked")
- private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
+ private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
throw (T)t;
}
/**
- * Returns the parameter directly. <br />
+ * Returns the parameter directly.
*
- * This method can be used to prevent a static analyzer to determine
- * the nullness of the passed parameter.
+ * This method can be used to prevent a static analyzer to determine the nullness of the passed parameter.
*
- * @param <T> the type of the parameter
- * @param value the value to return
- * @return value
+ * @param <T> the type of the parameter.
+ * @param value the value to return.
+ * @return value (this method just returns the parameter).
*/
public static <T> T preventNullAnalysis(T value) {
return value;
}
+
+ /**
+ * Ensures that the {@code value} is not {@code null}.
+ *
+ * @param <T> Type of the parameter.
+ * @param value the value to test for null.
+ * @param message the message of the {@link NullPointerException}.
+ * @return the value if it is not null.
+ * @throws NullPointerException with the {@code message} if the value is null.
+ */
+ public static <T> T checkNotNull(T value, String message) {
+ if (value == null) throw new NullPointerException(message);
+ return value;
+ }
}
diff --git a/src/core/lombok/NoArgsConstructor.java b/src/core/lombok/NoArgsConstructor.java
index cd3e5568..672cd1c2 100644
--- a/src/core/lombok/NoArgsConstructor.java
+++ b/src/core/lombok/NoArgsConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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,7 +30,7 @@ import java.lang.annotation.Target;
* Generates a no-args constructor.
* Will generate an error message if such a constructor cannot be written due to the existence of final fields.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Constructor.html">the project lombok features page for &#64;Constructor</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for &#64;Constructor</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details.
* <p>
@@ -47,20 +47,39 @@ public @interface NoArgsConstructor {
* is generated with the same argument list that wraps the real constructor.
*
* Such a static 'constructor' is primarily useful as it infers type arguments.
+ *
+ * @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticName() default "";
/**
- * Any annotations listed here are put on the generated constructor. The syntax for this feature is: {@code @NoArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}
+ * Any annotations listed here are put on the generated constructor.
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @NoArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @NoArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}.
+ *
+ * @return List of annotations to apply to the generated constructor.
*/
AnyAnnotation[] onConstructor() default {};
/**
* Sets the access level of the constructor. By default, generated constructors are {@code public}.
+ *
+ * @return The constructor will be generated with this access modifier.
*/
AccessLevel access() default lombok.AccessLevel.PUBLIC;
/**
+ * If {@code true}, initializes all final fields to 0 / null / false.
+ * Otherwise, a compile time error occurs.
+ *
+ * @return Return {@code} true to force generation of a no-args constructor, picking defaults if necessary to assign required fields.
+ */
+ boolean force() default false;
+
+ /**
* Placeholder annotation to enable the placement of annotations on the generated code.
* @deprecated Don't use this annotation, ever - Read the documentation.
*/
diff --git a/src/core/lombok/RequiredArgsConstructor.java b/src/core/lombok/RequiredArgsConstructor.java
index 8bb57c1a..f21bd647 100644
--- a/src/core/lombok/RequiredArgsConstructor.java
+++ b/src/core/lombok/RequiredArgsConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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,7 +30,7 @@ import java.lang.annotation.Target;
* Generates a constructor with required arguments.
* Required arguments are final fields and fields with constraints such as {@code @NonNull}.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Constructor.html">the project lombok features page for &#64;Constructor</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for &#64;Constructor</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details.
*
@@ -45,28 +45,29 @@ public @interface RequiredArgsConstructor {
* is generated with the same argument list that wraps the real constructor.
*
* Such a static 'constructor' is primarily useful as it infers type arguments.
+ *
+ * @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticName() default "";
/**
- * Any annotations listed here are put on the generated constructor. The syntax for this feature is: {@code @RequiredArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}
+ * Any annotations listed here are put on the generated constructor.
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @RequiredArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @RequiredArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}.
+ *
+ * @return List of annotations to apply to the generated constructor.
*/
AnyAnnotation[] onConstructor() default {};
/**
* Sets the access level of the constructor. By default, generated constructors are {@code public}.
- */
- AccessLevel access() default lombok.AccessLevel.PUBLIC;
-
- /**
- * Constructors are generated with the {@link java.beans.ConstructorProperties} annotation.
- * However, this annotation is new in 1.6 which means those compiling for 1.5 will need
- * to set this value to true.
*
- * @deprecated THIS FEATURE WILL BE REMOVED after March 31st 2015. Use configuration key {@link ConfigurationKeys#ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES} instead.
+ * @return The constructor will be generated with this access modifier.
*/
- @Deprecated
- boolean suppressConstructorProperties() default false;
+ AccessLevel access() default lombok.AccessLevel.PUBLIC;
/**
* Placeholder annotation to enable the placement of annotations on the generated code.
diff --git a/src/core/lombok/Setter.java b/src/core/lombok/Setter.java
index 5e07b802..1b70bac9 100644
--- a/src/core/lombok/Setter.java
+++ b/src/core/lombok/Setter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Put on any field to make lombok build a standard setter.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/GetterSetter.html">the project lombok features page for &#64;Getter and &#64;Setter</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/GetterSetter">the project lombok features page for &#64;Getter and &#64;Setter</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onParam} and {@code onMethod} parameter. See the full documentation for more details.
* <p>
@@ -55,16 +55,32 @@ import java.lang.annotation.Target;
public @interface Setter {
/**
* If you want your setter to be non-public, you can specify an alternate access level here.
+ *
+ * @return The setter method will be generated with this access modifier.
*/
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
/**
- * Any annotations listed here are put on the generated method. The syntax for this feature is: {@code @Setter(onMethod=@__({@AnnotationsGoHere}))}
+ * Any annotations listed here are put on the generated method.
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @Setter(onMethod=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @Setter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}.
+ *
+ * @return List of annotations to apply to the generated setter method.
*/
AnyAnnotation[] onMethod() default {};
/**
- * Any annotations listed here are put on the generated method's parameter. The syntax for this feature is: {@code @Setter(onParam=@__({@AnnotationsGoHere}))}
+ * Any annotations listed here are put on the generated method's parameter.
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @Setter(onParam=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @Setter(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}.
+ *
+ * @return List of annotations to apply to the generated parameter in the setter method.
*/
AnyAnnotation[] onParam() default {};
diff --git a/src/core/lombok/Singular.java b/src/core/lombok/Singular.java
new file mode 100644
index 00000000..67edab96
--- /dev/null
+++ b/src/core/lombok/Singular.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * The singular annotation is used together with {@code @Builder} to create single element 'add' methods in the builder for collections.
+ */
+@Target({FIELD, PARAMETER})
+@Retention(SOURCE)
+public @interface Singular {
+ /** @return The singular name of this field. If it's a normal english plural, lombok will figure it out automatically. Otherwise, this parameter is mandatory. */
+ String value() default "";
+}
diff --git a/src/core/lombok/SneakyThrows.java b/src/core/lombok/SneakyThrows.java
index 929b4578..0506d615 100644
--- a/src/core/lombok/SneakyThrows.java
+++ b/src/core/lombok/SneakyThrows.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -34,7 +34,7 @@ import java.lang.annotation.Target;
* checked exception types. The JVM does not check for the consistency of the checked exception system; javac does,
* and this annotation lets you opt out of its mechanism.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/SneakyThrows.html">the project lombok features page for &#64;SneakyThrows</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/SneakyThrows">the project lombok features page for &#64;SneakyThrows</a>.
* <p>
* Example:
* <pre>
@@ -59,8 +59,9 @@ import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.SOURCE)
public @interface SneakyThrows {
- /** The exception type(s) you want to sneakily throw onward. */
+ /** @return The exception type(s) you want to sneakily throw onward. */
Class<? extends Throwable>[] value() default java.lang.Throwable.class;
- //The package is mentioned in java.lang due to a bug in javac (presence of an annotation processor throws off the type resolver for some reason).
+ //The fully qualified name is used for java.lang.Throwable in the parameter only. This works around a bug in javac:
+ // presence of an annotation processor throws off the type resolver for some reason.
}
diff --git a/src/core/lombok/Synchronized.java b/src/core/lombok/Synchronized.java
index c5601a0c..5dff0fb2 100644
--- a/src/core/lombok/Synchronized.java
+++ b/src/core/lombok/Synchronized.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,7 +35,7 @@ import java.lang.annotation.Target;
* {@code $LOCK} is used. These will be generated if needed and if they aren't already present. The contents
* of the fields will be serializable.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Synchronized.html">the project lombok features page for &#64;Synchronized</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Synchronized">the project lombok features page for &#64;Synchronized</a>.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
@@ -43,6 +43,8 @@ public @interface Synchronized {
/**
* Optional: specify the name of a different field to lock on. It is a compile time error if this field
* doesn't already exist (the fields are automatically generated only if you don't specify a specific name.
+ *
+ * @return Name of the field to lock on (blank = generate one).
*/
String value() default "";
}
diff --git a/src/core/lombok/ToString.java b/src/core/lombok/ToString.java
index e87c71e7..0c43c40b 100644
--- a/src/core/lombok/ToString.java
+++ b/src/core/lombok/ToString.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Generates an implementation for the {@code toString} method inherited by all objects, consisting of printing the values of relevant fields.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/ToString.html">the project lombok features page for &#64;ToString</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/ToString">the project lombok features page for &#64;ToString</a>.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@@ -37,12 +37,16 @@ public @interface ToString {
/**
* Include the name of each field when printing it.
* <strong>default: true</strong>
+ *
+ * @return Whether or not to include the names of fields in the string produced by the generated {@code toString()}.
*/
boolean includeFieldNames() default true;
/**
* Any fields listed here will not be printed in the generated {@code toString} implementation.
* Mutually exclusive with {@link #of()}.
+ *
+ * @return A list of fields to exclude.
*/
String[] exclude() default {};
@@ -51,18 +55,24 @@ public @interface ToString {
* Normally, all non-static fields are printed.
* <p>
* Mutually exclusive with {@link #exclude()}.
+ *
+ * @return A list of fields to use (<em>default</em>: all of them).
*/
String[] of() default {};
/**
* Include the result of the superclass's implementation of {@code toString} in the output.
* <strong>default: false</strong>
+ *
+ * @return Whether to call the superclass's {@code toString} implementation as part of the generated toString algorithm.
*/
boolean callSuper() default false;
/**
* Normally, if getters are available, those are called. To suppress this and let the generated code use the fields directly, set this to {@code true}.
* <strong>default: false</strong>
+ *
+ * @return If {@code true}, always use direct field access instead of calling the getter method.
*/
boolean doNotUseGetters() default false;
}
diff --git a/src/core/lombok/Value.java b/src/core/lombok/Value.java
index 2cffe15b..562ba0cc 100644
--- a/src/core/lombok/Value.java
+++ b/src/core/lombok/Value.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 The Project Lombok Authors.
+ * Copyright (C) 2012-2017 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
@@ -29,13 +29,13 @@ import java.lang.annotation.Target;
/**
* Generates a lot of code which fits with a class that is a representation of an immutable entity.
* <p>
- * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
+ * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @AllArgsConstructor @ToString @EqualsAndHashCode}.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Value">the project lombok features page for &#64;Value</a>.
*
* @see lombok.Getter
* @see lombok.experimental.FieldDefaults
- * @see lombok.RequiredArgsConstructor
+ * @see lombok.AllArgsConstructor
* @see lombok.ToString
* @see lombok.EqualsAndHashCode
* @see lombok.Data
@@ -49,10 +49,12 @@ public @interface Value {
* We suggest the name: "of", like so:
*
* <pre>
- * public @Data(staticConstructor = "of") class Point { final int x, y; }
+ * public @Value(staticConstructor = "of") class Point { final int x, y; }
* </pre>
*
* Default: No static constructor, instead the normal constructor is public.
+ *
+ * @return Name of static 'constructor' method to generate (blank = generate a normal constructor).
*/
String staticConstructor() default "";
}
diff --git a/src/core/lombok/bytecode/AsmUtil.java b/src/core/lombok/bytecode/AsmUtil.java
index 26e5af1f..e3d33efa 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.ASM5, writer) {
+ ClassVisitor visitor = new ClassVisitor(Opcodes.ASM6, 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/ClassFileMetaData.java b/src/core/lombok/bytecode/ClassFileMetaData.java
index 68b8bb7d..0dc6a6c8 100644
--- a/src/core/lombok/bytecode/ClassFileMetaData.java
+++ b/src/core/lombok/bytecode/ClassFileMetaData.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Project Lombok Authors.
+ * Copyright (C) 2010-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
@@ -44,6 +44,9 @@ public class ClassFileMetaData {
private static final byte METHOD_HANDLE = 15;
private static final byte METHOD_TYPE = 16;
private static final byte INVOKE_DYNAMIC = 18;
+ // New in java9: support for modules
+ private static final byte MODULE = 19;
+ private static final byte PACKAGE = 20;
private static final int NOT_FOUND = -1;
private static final int START_OF_CONSTANT_POOL = 8;
@@ -79,6 +82,8 @@ public class ClassFileMetaData {
case CLASS:
case STRING:
case METHOD_TYPE:
+ case MODULE:
+ case PACKAGE:
position += 2;
break;
case METHOD_HANDLE:
diff --git a/src/core/lombok/bytecode/PostCompilerApp.java b/src/core/lombok/bytecode/PostCompilerApp.java
index d2c3157c..5f49bd81 100644
--- a/src/core/lombok/bytecode/PostCompilerApp.java
+++ b/src/core/lombok/bytecode/PostCompilerApp.java
@@ -98,7 +98,7 @@ public class PostCompilerApp extends LombokApp {
byte[] original = readFile(file);
byte[] clone = original.clone();
byte[] transformed = PostCompiler.applyTransformations(clone, file.toString(), DiagnosticsReceiver.CONSOLE);
- if (clone != transformed && !Arrays.equals(clone, transformed)) {
+ if (clone != transformed && !Arrays.equals(original, transformed)) {
filesTouched++;
if (args.verbose) System.out.println("Rewriting " + file.getAbsolutePath());
writeFile(file, transformed);
@@ -138,18 +138,24 @@ public class PostCompilerApp extends LombokApp {
byte[] buffer = new byte[1024];
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
FileInputStream fileInputStream = new FileInputStream(file);
- while (true) {
- int read = fileInputStream.read(buffer);
- if (read == -1) break;
- bytes.write(buffer, 0, read);
+ try {
+ while (true) {
+ int read = fileInputStream.read(buffer);
+ if (read == -1) break;
+ bytes.write(buffer, 0, read);
+ }
+ } finally {
+ fileInputStream.close();
}
- fileInputStream.close();
return bytes.toByteArray();
}
static void writeFile(File file, byte[] transformed) throws IOException {
FileOutputStream out = new FileOutputStream(file);
- out.write(transformed);
- out.close();
+ try {
+ out.write(transformed);
+ } finally {
+ out.close();
+ }
}
}
diff --git a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java
index 5f2f5f18..c06f2d7c 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.ASM5, mv);
+ super(Opcodes.ASM6, 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.ASM5, writer) {
+ reader.accept(new ClassVisitor(Opcodes.ASM6, 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 8ef64e59..a2ee59ea 100644
--- a/src/core/lombok/bytecode/SneakyThrowsRemover.java
+++ b/src/core/lombok/bytecode/SneakyThrowsRemover.java
@@ -47,12 +47,12 @@ public class SneakyThrowsRemover implements PostCompilerTransformation {
ClassReader reader = new ClassReader(fixedByteCode);
ClassWriter writer = new ClassWriter(reader, 0);
-
+
final AtomicBoolean changesMade = new AtomicBoolean();
class SneakyThrowsRemoverVisitor extends MethodVisitor {
SneakyThrowsRemoverVisitor(MethodVisitor mv) {
- super(Opcodes.ASM5, mv);
+ super(Opcodes.ASM6, mv);
}
private boolean methodInsnQueued = false;
diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java
index e6efe058..1142018f 100644
--- a/src/core/lombok/core/AST.java
+++ b/src/core/lombok/core/AST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
*/
package lombok.core;
-import static lombok.Lombok.*;
+import static lombok.Lombok.sneakyThrow;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
@@ -31,10 +31,11 @@ import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import lombok.core.configuration.ConfigurationKey;
import lombok.core.debug.HistogramTracker;
@@ -61,12 +62,18 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
Map<N, N> identityDetector = new IdentityHashMap<N, N>();
private Map<N, L> nodeMap = new IdentityHashMap<N, L>();
private boolean changed = false;
+
+ // The supertypes which are considered AST Node children. Usually, the Statement, and the Expression,
+ // though some platforms (such as Eclipse) group these under one common supertype.
+ private final Collection<Class<? extends N>> statementTypes;
+
private static final HistogramTracker configTracker = System.getProperty("lombok.timeConfig") == null ? null : new HistogramTracker("lombok.config");
- protected AST(String fileName, String packageDeclaration, ImportList imports) {
+ protected AST(String fileName, String packageDeclaration, ImportList imports, Collection<Class<? extends N>> statementTypes) {
this.fileName = fileName == null ? "(unknown).java" : fileName;
this.packageDeclaration = packageDeclaration;
this.imports = imports;
+ this.statementTypes = statementTypes;
}
/**
@@ -209,11 +216,11 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
}
}
- private static Map<Class<?>, Collection<FieldAccess>> fieldsOfASTClasses = new HashMap<Class<?>, Collection<FieldAccess>>();
+ private static final ConcurrentMap<Class<?>, Collection<FieldAccess>> fieldsOfASTClasses = new ConcurrentHashMap<Class<?>, Collection<FieldAccess>>();
/** Returns FieldAccess objects for the stated class. Each field that contains objects of the kind returned by
* {@link #getStatementTypes()}, either directly or inside of an array or java.util.collection (or array-of-arrays,
- * or collection-of-collections, etcetera), is returned.
+ * or collection-of-collections, et cetera), is returned.
*/
protected Collection<FieldAccess> fieldsOf(Class<?> c) {
Collection<FieldAccess> fields = fieldsOfASTClasses.get(c);
@@ -221,8 +228,8 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
fields = new ArrayList<FieldAccess>();
getFields(c, fields);
- fieldsOfASTClasses.put(c, fields);
- return fields;
+ fieldsOfASTClasses.putIfAbsent(c, fields);
+ return fieldsOfASTClasses.get(c);
}
private void getFields(Class<?> c, Collection<FieldAccess> fields) {
@@ -261,13 +268,8 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>,
return Object.class;
}
- /**
- * The supertypes which are considered AST Node children. Usually, the Statement, and the Expression,
- * though some platforms (such as Eclipse) group these under one common supertype. */
- protected abstract Collection<Class<? extends N>> getStatementTypes();
-
- protected boolean shouldDrill(Class<?> parentType, Class<?> childType, String fieldName) {
- for (Class<?> statementType : getStatementTypes()) {
+ private boolean shouldDrill(Class<?> parentType, Class<?> childType, String fieldName) {
+ for (Class<?> statementType : statementTypes) {
if (statementType.isAssignableFrom(childType)) return true;
}
diff --git a/src/core/lombok/core/Agent.java b/src/core/lombok/core/AgentLauncher.java
index 49c03bf3..1d5ab3e6 100644
--- a/src/core/lombok/core/Agent.java
+++ b/src/core/lombok/core/AgentLauncher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2014 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,45 +21,32 @@
*/
package lombok.core;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
-import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
-
-public abstract class Agent {
- protected abstract void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected) throws Exception;
-
- public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Throwable {
- runAgents(agentArgs, instrumentation, true);
+public class AgentLauncher {
+ public interface AgentLaunchable {
+ void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Exception;
}
- public static void premain(String agentArgs, Instrumentation instrumentation) throws Throwable {
- runAgents(agentArgs, instrumentation, false);
- }
-
- private static final List<AgentInfo> AGENTS = Collections.unmodifiableList(Arrays.asList(
- new NetbeansPatcherInfo(),
- new EclipsePatcherInfo()
- ));
-
- private static void runAgents(String agentArgs, Instrumentation instrumentation, boolean injected) throws Throwable {
+ public static void runAgents(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Throwable {
for (AgentInfo info : AGENTS) {
try {
Class<?> agentClass = Class.forName(info.className());
- Agent agent = (Agent) agentClass.newInstance();
- agent.runAgent(agentArgs, instrumentation, injected);
+ AgentLaunchable agent = (AgentLaunchable) agentClass.newInstance();
+ agent.runAgent(agentArgs, instrumentation, injected, launchingContext);
} catch (Throwable t) {
info.problem(t, instrumentation);
}
}
}
+ private static final List<AgentInfo> AGENTS = Collections.unmodifiableList(Arrays.<AgentInfo>asList(
+ new EclipsePatcherInfo()
+ ));
+
private static abstract class AgentInfo {
abstract String className();
@@ -91,45 +78,6 @@ public abstract class Agent {
}
}
- private static class NetbeansPatcherInfo extends AgentInfo {
- @Override String className() {
- return "lombok.netbeans.agent.NetbeansPatcher";
- }
-
- @Override void problem(Throwable in, Instrumentation instrumentation) throws Throwable {
- try {
- super.problem(in, instrumentation);
- } catch (InternalError ie) {
- throw ie;
- } catch (Throwable t) {
- final String error;
-
- if (t instanceof UnsupportedClassVersionError) {
- error = "Lombok only works on netbeans if you start netbeans using a 1.6 or higher JVM.\n" +
- "Change your platform's default JVM, or edit etc/netbeans.conf\n" +
- "and explicitly tell netbeans your 1.6 JVM's location.";
- } else {
- error = "Lombok disabled due to error: " + t;
- }
-
- instrumentation.addTransformer(new ClassFileTransformer() {
- @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
- if ("org/netbeans/modules/java/source/parsing/JavacParser".equals(className)) {
- //If that class gets loaded, this is definitely a netbeans(-esque) environment, and thus we SHOULD tell the user that lombok is not in fact loaded.
- SwingUtilities.invokeLater(new Runnable() {
- @Override public void run() {
- JOptionPane.showMessageDialog(null, error, "Lombok Disabled", JOptionPane.ERROR_MESSAGE);
- }
- });
- }
-
- return null;
- }
- });
- }
- }
- }
-
private static class EclipsePatcherInfo extends AgentInfo {
@Override String className() {
return "lombok.eclipse.agent.EclipsePatcher";
diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java
index eb44811b..5b4ef393 100644
--- a/src/core/lombok/core/AnnotationProcessor.java
+++ b/src/core/lombok/core/AnnotationProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 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 @@ import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
+import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
@@ -75,9 +76,9 @@ public class AnnotationProcessor extends AbstractProcessor {
try {
ClassLoader classLoader = findAndPatchClassLoader(procEnv);
- processor = (Processor)Class.forName("lombok.javac.apt.Processor", false, classLoader).newInstance();
+ processor = (Processor) Class.forName("lombok.javac.apt.LombokProcessor", false, classLoader).newInstance();
} catch (Exception e) {
- delayedWarnings.add("You found a bug in lombok; lombok.javac.apt.Processor is not available. Lombok will not run during this compilation: " + trace(e));
+ delayedWarnings.add("You found a bug in lombok; lombok.javac.apt.LombokProcessor is not available. Lombok will not run during this compilation: " + trace(e));
return false;
} catch (NoClassDefFoundError e) {
delayedWarnings.add("Can't load javac processor due to (most likely) a class loader problem: " + trace(e));
@@ -86,7 +87,7 @@ public class AnnotationProcessor extends AbstractProcessor {
try {
processor.init(procEnv);
} catch (Exception e) {
- delayedWarnings.add("lombok.javac.apt.Processor could not be initialized. Lombok will not run during this compilation: " + trace(e));
+ delayedWarnings.add("lombok.javac.apt.LombokProcessor could not be initialized. Lombok will not run during this compilation: " + trace(e));
return false;
} catch (NoClassDefFoundError e) {
delayedWarnings.add("Can't initialize javac processor due to (most likely) a class loader problem: " + trace(e));
@@ -103,7 +104,6 @@ public class AnnotationProcessor extends AbstractProcessor {
URL selfUrl = new File(ClassRootFinder.findClassRootOfClass(AnnotationProcessor.class)).toURI().toURL();
m.invoke(environmentClassLoader, selfUrl);
}
- return environmentClassLoader;
}
ClassLoader ourClassLoader = JavacDescriptor.class.getClassLoader();
@@ -164,7 +164,21 @@ public class AnnotationProcessor extends AbstractProcessor {
for (ProcessorDescriptor proc : active) proc.process(annotations, roundEnv);
- return false;
+ boolean onlyLombok = true;
+ boolean zeroElems = true;
+ for (TypeElement elem : annotations) {
+ zeroElems = false;
+ Name n = elem.getQualifiedName();
+ if (n.length() > 7 && n.subSequence(0, 7).toString().equals("lombok.")) continue;
+ onlyLombok = false;
+ }
+
+ // Normally we rely on the claiming processor to claim away all lombok annotations.
+ // One of the many Java9 oversights is that this 'process' API has not been fixed to address the point that 'files I want to look at' and 'annotations I want to claim' must be one and the same,
+ // and yet in java9 you can no longer have 2 providers for the same service, thus, if you go by module path, lombok no longer loads the ClaimingProcessor.
+ // This doesn't do as good a job, but it'll have to do. The only way to go from here, I think, is either 2 modules, or use reflection hackery to add ClaimingProcessor during our init.
+
+ return onlyLombok && !zeroElems;
}
/**
diff --git a/src/core/lombok/core/GuavaTypeMap.java b/src/core/lombok/core/GuavaTypeMap.java
new file mode 100644
index 00000000..282d5d81
--- /dev/null
+++ b/src/core/lombok/core/GuavaTypeMap.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.core;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class GuavaTypeMap {
+ private static final Map<String, String> TYPE_TO_GUAVA_TYPE; static {
+ Map<String, String> m = new HashMap<String, String>();
+
+ m.put("java.util.NavigableSet", "ImmutableSortedSet");
+ m.put("java.util.NavigableMap", "ImmutableSortedMap");
+ m.put("java.util.SortedSet", "ImmutableSortedSet");
+ m.put("java.util.SortedMap", "ImmutableSortedMap");
+ m.put("java.util.Set", "ImmutableSet");
+ m.put("java.util.Map", "ImmutableMap");
+ m.put("java.util.Collection", "ImmutableList");
+ m.put("java.util.List", "ImmutableList");
+
+ m.put("com.google.common.collect.ImmutableSet", "ImmutableSet");
+ m.put("com.google.common.collect.ImmutableSortedSet", "ImmutableSortedSet");
+ m.put("com.google.common.collect.ImmutableMap", "ImmutableMap");
+ m.put("com.google.common.collect.ImmutableBiMap", "ImmutableBiMap");
+ m.put("com.google.common.collect.ImmutableSortedMap", "ImmutableSortedMap");
+ m.put("com.google.common.collect.ImmutableList", "ImmutableList");
+ m.put("com.google.common.collect.ImmutableCollection", "ImmutableList");
+ m.put("com.google.common.collect.ImmutableTable", "ImmutableTable");
+
+ TYPE_TO_GUAVA_TYPE = Collections.unmodifiableMap(m);
+ }
+
+ public static String getGuavaTypeName(String fqn) {
+ String target = TYPE_TO_GUAVA_TYPE.get(fqn);
+ return target != null ? target : "ImmutableList";
+ }
+
+ private GuavaTypeMap() {}
+}
diff --git a/src/core/lombok/core/LombokInternalAliasing.java b/src/core/lombok/core/LombokInternalAliasing.java
index 8d6794ae..3dc1bfa2 100644
--- a/src/core/lombok/core/LombokInternalAliasing.java
+++ b/src/core/lombok/core/LombokInternalAliasing.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-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
@@ -50,6 +50,8 @@ public class LombokInternalAliasing {
Map<String, String> m2 = new HashMap<String, String>();
m2.put("lombok.experimental.Value", "lombok.Value");
+ m2.put("lombok.experimental.Builder", "lombok.Builder");
+ m2.put("lombok.experimental.var", "lombok.var");
m2.put("lombok.Delegate", "lombok.experimental.Delegate");
ALIASES = Collections.unmodifiableMap(m2);
}
diff --git a/src/core/lombok/core/Main.java b/src/core/lombok/core/Main.java
index d62fe3e4..6952ab78 100644
--- a/src/core/lombok/core/Main.java
+++ b/src/core/lombok/core/Main.java
@@ -38,6 +38,7 @@ public class Main {
));
public static void main(String[] args) throws IOException {
+ Thread.currentThread().setContextClassLoader(Main.class.getClassLoader());
int err = new Main(SpiLoadUtil.readAllFromIterator(
SpiLoadUtil.findServices(LombokApp.class)), Arrays.asList(args)).go();
System.exit(err);
@@ -94,7 +95,7 @@ public class Main {
in.close();
}
} catch (Exception e) {
- System.err.println("License file not found. Check http://projectlombok.org/LICENSE");
+ System.err.println("License file not found. Check https://projectlombok.org/LICENSE");
return 1;
}
}
@@ -142,7 +143,7 @@ public class Main {
out.println("------------------------------");
}
out.println("projectlombok.org " + Version.getFullVersion());
- out.println("Copyright (C) 2009-2012 The Project Lombok Authors.");
+ out.println("Copyright (C) 2009-2015 The Project Lombok Authors.");
out.println("Run 'lombok license' to see the lombok license agreement.");
out.println();
out.println("Run lombok without any parameters to start the graphical installer.");
diff --git a/src/core/lombok/core/PostCompiler.java b/src/core/lombok/core/PostCompiler.java
index 11091cb8..237867cb 100644
--- a/src/core/lombok/core/PostCompiler.java
+++ b/src/core/lombok/core/PostCompiler.java
@@ -24,6 +24,8 @@ package lombok.core;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Collections;
import java.util.List;
@@ -55,10 +57,12 @@ public final class PostCompiler {
transformations = SpiLoadUtil.readAllFromIterator(SpiLoadUtil.findServices(PostCompilerTransformation.class, PostCompilerTransformation.class.getClassLoader()));
} catch (IOException e) {
transformations = Collections.emptyList();
- diagnostics.addWarning("Could not load post-compile transformers: " + e.getMessage());
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw, true));
+ diagnostics.addWarning("Could not load post-compile transformers: " + e.getMessage() + "\n" + sw.toString());
}
}
-
+
public static OutputStream wrapOutputStream(final OutputStream originalStream, final String fileName, final DiagnosticsReceiver diagnostics) throws IOException {
if (System.getProperty("lombok.disablePostCompiler", null) != null) return originalStream;
return new ByteArrayOutputStream() {
diff --git a/src/core/lombok/core/PublicApiCreatorApp.java b/src/core/lombok/core/PublicApiCreatorApp.java
index 178a45e8..c1430c24 100644
--- a/src/core/lombok/core/PublicApiCreatorApp.java
+++ b/src/core/lombok/core/PublicApiCreatorApp.java
@@ -105,7 +105,7 @@ public class PublicApiCreatorApp extends LombokApp {
int firstSlash = subName.indexOf('/');
if (firstSlash == -1) {
// direct member of the lombok package.
- toCopy.add(name);
+ if (!subName.startsWith("ConfigurationKeys")) toCopy.add(name);
continue;
}
String topPkg = subName.substring(0, firstSlash);
diff --git a/src/core/lombok/core/TypeLibrary.java b/src/core/lombok/core/TypeLibrary.java
index c0e9dc43..cdaf7a70 100644
--- a/src/core/lombok/core/TypeLibrary.java
+++ b/src/core/lombok/core/TypeLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -37,6 +37,7 @@ import java.util.Map;
public class TypeLibrary {
private final Map<String, String> unqualifiedToQualifiedMap;
private final String unqualified, qualified;
+ private boolean locked;
public TypeLibrary() {
unqualifiedToQualifiedMap = new HashMap<String, String>();
@@ -44,15 +45,27 @@ public class TypeLibrary {
qualified = null;
}
+ public void lock() {
+ this.locked = true;
+ }
+
private TypeLibrary(String fqnSingleton) {
- unqualifiedToQualifiedMap = null;
- qualified = fqnSingleton;
- int idx = fqnSingleton.lastIndexOf('.');
- if (idx == -1) {
- unqualified = fqnSingleton;
+ if (fqnSingleton.indexOf("$") != -1) {
+ unqualifiedToQualifiedMap = new HashMap<String, String>();
+ unqualified = null;
+ qualified = null;
+ addType(fqnSingleton);
} else {
- unqualified = fqnSingleton.substring(idx + 1);
+ unqualifiedToQualifiedMap = null;
+ qualified = fqnSingleton;
+ int idx = fqnSingleton.lastIndexOf('.');
+ if (idx == -1) {
+ unqualified = fqnSingleton;
+ } else {
+ unqualified = fqnSingleton.substring(idx + 1);
+ }
}
+ locked = true;
}
public static TypeLibrary createLibraryForSingleType(String fqnSingleton) {
@@ -65,17 +78,29 @@ public class TypeLibrary {
* @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'.
*/
public void addType(String fullyQualifiedTypeName) {
- fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", ".");
+ String dotBased = fullyQualifiedTypeName.replace("$", ".");
+
+ if (locked) throw new IllegalStateException("locked");
int idx = fullyQualifiedTypeName.lastIndexOf('.');
if (idx == -1) throw new IllegalArgumentException(
"Only fully qualified types are allowed (and stuff in the default package is not palatable to us either!)");
String unqualified = fullyQualifiedTypeName.substring(idx + 1);
if (unqualifiedToQualifiedMap == null) throw new IllegalStateException("SingleType library");
- unqualifiedToQualifiedMap.put(unqualified, fullyQualifiedTypeName);
- unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, fullyQualifiedTypeName);
+ unqualifiedToQualifiedMap.put(unqualified.replace("$", "."), dotBased);
+ unqualifiedToQualifiedMap.put(unqualified, dotBased);
+ unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, dotBased);
+ unqualifiedToQualifiedMap.put(dotBased, dotBased);
for (Map.Entry<String, String> e : LombokInternalAliasing.ALIASES.entrySet()) {
- if (fullyQualifiedTypeName.equals(e.getValue())) unqualifiedToQualifiedMap.put(e.getKey(), fullyQualifiedTypeName);
+ if (fullyQualifiedTypeName.equals(e.getValue())) unqualifiedToQualifiedMap.put(e.getKey(), dotBased);
+ }
+
+ int idx2 = fullyQualifiedTypeName.indexOf('$', idx + 1);
+ while (idx2 != -1) {
+ String unq = fullyQualifiedTypeName.substring(idx2 + 1);
+ unqualifiedToQualifiedMap.put(unq.replace("$", "."), dotBased);
+ unqualifiedToQualifiedMap.put(unq, dotBased);
+ idx2 = fullyQualifiedTypeName.indexOf('$', idx2 + 1);
}
}
diff --git a/src/core/lombok/core/TypeResolver.java b/src/core/lombok/core/TypeResolver.java
index 287a085f..60ac6b6a 100644
--- a/src/core/lombok/core/TypeResolver.java
+++ b/src/core/lombok/core/TypeResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -53,10 +53,13 @@ public class TypeResolver {
if (typeRef.equals(qualified)) return typeRef;
// When asking if 'Getter' could possibly be referring to 'lombok.Getter' if 'import lombok.Getter;' is in the source file, the answer is yes.
- String fromExplicitImport = imports.getFullyQualifiedNameForSimpleName(typeRef);
+ int firstDot = typeRef.indexOf('.');
+ if (firstDot == -1) firstDot = typeRef.length();
+ String firstTypeRef = typeRef.substring(0, firstDot);
+ String fromExplicitImport = imports.getFullyQualifiedNameForSimpleName(firstTypeRef);
if (fromExplicitImport != null) {
// ... and if 'import foobar.Getter;' is in the source file, the answer is no.
- return fromExplicitImport.equals(qualified) ? qualified : null;
+ return (fromExplicitImport + typeRef.substring(firstDot)).equals(qualified) ? qualified : null;
}
// When asking if 'Getter' could possibly be referring to 'lombok.Getter' and 'import lombok.*; / package lombok;' isn't in the source file. the answer is no.
@@ -68,7 +71,7 @@ public class TypeResolver {
mainLoop:
while (n != null) {
- if (n.getKind() == Kind.TYPE && typeRef.equals(n.getName())) {
+ if (n.getKind() == Kind.TYPE && firstTypeRef.equals(n.getName())) {
// Our own class or one of our outer classes is named 'typeRef' so that's what 'typeRef' is referring to, not one of our type library classes.
return null;
}
@@ -81,7 +84,7 @@ public class TypeResolver {
for (LombokNode<?, ?, ?> child : newN.down()) {
// We found a method local with the same name above our code. That's the one 'typeRef' is referring to, not
// anything in the type library we're trying to find, so, no matches.
- if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null;
+ if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null;
if (child == n) break;
}
}
@@ -92,14 +95,13 @@ public class TypeResolver {
if (n.getKind() == Kind.TYPE || n.getKind() == Kind.COMPILATION_UNIT) {
for (LombokNode<?, ?, ?> child : n.down()) {
// Inner class that's visible to us has 'typeRef' as name, so that's the one being referred to, not one of our type library classes.
- if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null;
+ if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null;
}
}
n = n.directUp();
}
-
return qualified;
}
}
diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java
index 9f810525..98f1e575 100644
--- a/src/core/lombok/core/Version.java
+++ b/src/core/lombok/core/Version.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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,9 +30,15 @@ public class Version {
// ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries).
// Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example.
// Official builds always end in an even number. (Since 0.10.2).
- private static final String VERSION = "1.14.9";
+ private static final String VERSION = "1.16.21";
private static final String RELEASE_NAME = "Edgy Guinea Pig";
-// private static final String RELEASE_NAME = "Branching Cobra";
+// private static final String RELEASE_NAME = "Dancing Elephant";
+
+ // Named version history:
+ // Angry Butterfly
+ // Branching Cobra
+ // Candid Duck
+ // Dancing Elephant
private Version() {
//Prevent instantiation
@@ -43,7 +49,7 @@ public class Version {
*/
public static void main(String[] args) {
if (args.length > 0) {
- System.out.printf("Lombok %s\n", getFullVersion());
+ System.out.printf("%s\n", getFullVersion());
} else {
System.out.println(VERSION);
}
diff --git a/src/core/lombok/core/configuration/AllowHelper.java b/src/core/lombok/core/configuration/AllowHelper.java
new file mode 100644
index 00000000..1146ccde
--- /dev/null
+++ b/src/core/lombok/core/configuration/AllowHelper.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.core.configuration;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public final class AllowHelper {
+ private final static Collection<? extends ConfigurationKey<?>> ALLOWABLE = Collections.emptySet();
+
+ private AllowHelper() {
+ // Prevent instantiation
+ }
+
+ public static boolean isAllowable(ConfigurationKey<?> key) {
+ return ALLOWABLE.contains(key);
+ }
+}
diff --git a/src/core/lombok/core/configuration/BubblingConfigurationResolver.java b/src/core/lombok/core/configuration/BubblingConfigurationResolver.java
index f96b4468..0849eee2 100644
--- a/src/core/lombok/core/configuration/BubblingConfigurationResolver.java
+++ b/src/core/lombok/core/configuration/BubblingConfigurationResolver.java
@@ -45,32 +45,23 @@ public class BubblingConfigurationResolver implements ConfigurationResolver {
Result result = source.resolve(key);
if (result == null) continue;
if (isList) {
- if (listModificationsList == null) {
- listModificationsList = new ArrayList<List<ListModification>>();
- }
+ if (listModificationsList == null) listModificationsList = new ArrayList<List<ListModification>>();
listModificationsList.add((List<ListModification>)result.getValue());
}
if (result.isAuthoritative()) {
- if (isList) {
- break;
- }
+ if (isList) break;
return (T) result.getValue();
}
}
- if (!isList) {
- return null;
- }
- if (listModificationsList == null) {
- return (T) Collections.emptyList();
- }
+ if (!isList) return null;
+ if (listModificationsList == null) return (T) Collections.emptyList();
+
List<Object> listValues = new ArrayList<Object>();
Collections.reverse(listModificationsList);
for (List<ListModification> listModifications : listModificationsList) {
if (listModifications != null) for (ListModification modification : listModifications) {
listValues.remove(modification.getValue());
- if (modification.isAdded()) {
- listValues.add(modification.getValue());
- }
+ if (modification.isAdded()) listValues.add(modification.getValue());
}
}
return (T) listValues;
diff --git a/src/core/lombok/core/configuration/CallSuperType.java b/src/core/lombok/core/configuration/CallSuperType.java
new file mode 100644
index 00000000..b7152ea4
--- /dev/null
+++ b/src/core/lombok/core/configuration/CallSuperType.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.core.configuration;
+
+/** Used for lombok configuration for configuration whether or not to call the super implementation for certain lombok feature. */
+public enum CallSuperType {
+ CALL, SKIP, WARN;
+}
diff --git a/src/core/lombok/core/configuration/ConfigurationApp.java b/src/core/lombok/core/configuration/ConfigurationApp.java
index e441b4de..efe57e38 100644
--- a/src/core/lombok/core/configuration/ConfigurationApp.java
+++ b/src/core/lombok/core/configuration/ConfigurationApp.java
@@ -198,7 +198,7 @@ public class ConfigurationApp extends LombokApp {
if (paths.size() == 1) {
if (!(argsPaths.size() == 1)) out.printf("Configuration for '%s'.%n%n", paths.iterator().next());
} else {
- out.printf("Configuration for:%n", paths.iterator().next());
+ out.printf("Configuration for:%n");
for (String path : paths) out.printf("- %s%n", path);
out.println();
}
diff --git a/src/core/lombok/core/configuration/ConfigurationParser.java b/src/core/lombok/core/configuration/ConfigurationParser.java
index f0a9e142..e80793c1 100644
--- a/src/core/lombok/core/configuration/ConfigurationParser.java
+++ b/src/core/lombok/core/configuration/ConfigurationParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Project Lombok Authors.
+ * Copyright (C) 2014-2016 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
@@ -27,7 +27,7 @@ import java.util.regex.Pattern;
public class ConfigurationParser {
private static final Pattern LINE = Pattern.compile("(?:clear\\s+([^=]+))|(?:(\\S*?)\\s*([-+]?=)\\s*(.*?))");
- private static final Pattern NEWLINE_FINDER = Pattern.compile("^\\s*(.*?)\\s*$", Pattern.MULTILINE);
+ private static final Pattern NEWLINE_FINDER = Pattern.compile("^[\t ]*(.*?)[\t\r ]*$", Pattern.MULTILINE);
private ConfigurationProblemReporter reporter;
diff --git a/src/core/lombok/core/configuration/FileSystemSourceCache.java b/src/core/lombok/core/configuration/FileSystemSourceCache.java
index dc390eb6..8bc5050b 100644
--- a/src/core/lombok/core/configuration/FileSystemSourceCache.java
+++ b/src/core/lombok/core/configuration/FileSystemSourceCache.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Project Lombok Authors.
+ * Copyright (C) 2014-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -38,41 +38,68 @@ import lombok.core.debug.ProblemReporter;
public class FileSystemSourceCache {
private static final String LOMBOK_CONFIG_FILENAME = "lombok.config";
+ private static final long FULL_CACHE_CLEAR_INTERVAL = TimeUnit.MINUTES.toMillis(30);
private static final long RECHECK_FILESYSTEM = TimeUnit.SECONDS.toMillis(2);
private static final long NEVER_CHECKED = -1;
private static final long MISSING = -88; // Magic value; any lombok.config with this exact epochmillis last modified will never be read, so, let's ensure nobody accidentally has one with that exact last modified stamp.
- private final ConcurrentMap<File, Content> cache = new ConcurrentHashMap<File, Content>();
+ private final ConcurrentMap<File, Content> dirCache = new ConcurrentHashMap<File, Content>(); // caches files (representing dirs) to the content object that tracks content.
+ private final ConcurrentMap<URI, File> uriCache = new ConcurrentHashMap<URI, File>(); // caches URIs of java source files to the dir that contains it.
+ private volatile long lastCacheClear = System.currentTimeMillis();
+
+ private void cacheClear() {
+ // We never clear the caches, generally because it'd be weird if a compile run would continually create an endless stream of new java files.
+ // Still, eventually that's going to cause a bit of a memory leak, so lets just completely clear them out every many minutes.
+ long now = System.currentTimeMillis();
+ long delta = now - lastCacheClear;
+ if (delta > FULL_CACHE_CLEAR_INTERVAL) {
+ lastCacheClear = now;
+ dirCache.clear();
+ uriCache.clear();
+ }
+ }
public Iterable<ConfigurationSource> sourcesForJavaFile(URI javaFile, ConfigurationProblemReporter reporter) {
if (javaFile == null) return Collections.emptyList();
- URI uri = javaFile.normalize();
- if (!uri.isAbsolute()) uri = URI.create("file:" + uri.toString());
-
- File file;
- try {
- file = new File(uri);
- if (!file.exists()) throw new IllegalArgumentException("File does not exist: " + uri);
- return sourcesForDirectory(file.getParentFile(), reporter);
- } catch (IllegalArgumentException e) {
- // This means that the file as passed is not actually a file at all, and some exotic path system is involved.
- // examples: sourcecontrol://jazz stuff, or an actual relative path (uri.isAbsolute() is completely different, that checks presence of schema!),
- // or it's eclipse trying to parse a snippet, which has "/Foo.java" as uri.
- // At some point it might be worth investigating abstracting away the notion of "I can read lombok.config if present in
- // current context, and I can give you may parent context", using ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(javaFile) as basis.
+ cacheClear();
+ File dir = uriCache.get(javaFile);
+ if (dir == null) {
+ URI uri = javaFile.normalize();
+ if (!uri.isAbsolute()) uri = URI.create("file:" + uri.toString());
- // For now, we just carry on as if there is no lombok.config. (intentional fallthrough)
- } catch (Exception e) {
- // Especially for eclipse's sake, exceptions here make eclipse borderline unusable, so let's play nice.
- ProblemReporter.error("Can't find absolute path of file being compiled: " + javaFile, e);
+ try {
+ File file = new File(uri);
+ if (!file.exists()) throw new IllegalArgumentException("File does not exist: " + uri);
+ dir = file.isDirectory() ? file : file.getParentFile();
+ if (dir != null) uriCache.put(javaFile, dir);
+ } catch (IllegalArgumentException e) {
+ // This means that the file as passed is not actually a file at all, and some exotic path system is involved.
+ // examples: sourcecontrol://jazz stuff, or an actual relative path (uri.isAbsolute() is completely different, that checks presence of schema!),
+ // or it's eclipse trying to parse a snippet, which has "/Foo.java" as uri.
+ // At some point it might be worth investigating abstracting away the notion of "I can read lombok.config if present in
+ // current context, and I can give you may parent context", using ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(javaFile) as basis.
+
+ // For now, we just carry on as if there is no lombok.config. (intentional fallthrough)
+ } catch (Exception e) {
+ // Especially for eclipse's sake, exceptions here make eclipse borderline unusable, so let's play nice.
+ ProblemReporter.error("Can't find absolute path of file being compiled: " + javaFile, e);
+ }
+ }
+
+ if (dir != null) {
+ try {
+ return sourcesForDirectory(dir, reporter);
+ } catch (Exception e) {
+ // Especially for eclipse's sake, exceptions here make eclipse borderline unusable, so let's play nice.
+ ProblemReporter.error("Can't resolve config stack for dir: " + dir.getAbsolutePath(), e);
+ }
}
return Collections.emptyList();
}
public Iterable<ConfigurationSource> sourcesForDirectory(URI directory, ConfigurationProblemReporter reporter) {
- if (directory == null) return Collections.emptyList();
- return sourcesForDirectory(new File(directory.normalize()), reporter);
+ return sourcesForJavaFile(directory, reporter);
}
private Iterable<ConfigurationSource> sourcesForDirectory(final File directory, final ConfigurationProblemReporter reporter) {
@@ -139,12 +166,12 @@ public class FileSystemSourceCache {
}
private Content ensureContent(File directory) {
- Content content = cache.get(directory);
+ Content content = dirCache.get(directory);
if (content != null) {
return content;
}
- cache.putIfAbsent(directory, Content.empty());
- return cache.get(directory);
+ dirCache.putIfAbsent(directory, Content.empty());
+ return dirCache.get(directory);
}
private ConfigurationSource parse(File configFile, ConfigurationProblemReporter reporter) {
diff --git a/src/core/lombok/core/configuration/FlagUsageType.java b/src/core/lombok/core/configuration/FlagUsageType.java
index b7053b7c..8717c22b 100644
--- a/src/core/lombok/core/configuration/FlagUsageType.java
+++ b/src/core/lombok/core/configuration/FlagUsageType.java
@@ -23,5 +23,5 @@ package lombok.core.configuration;
/** Used for lombok configuration to flag usages of certain lombok feature. */
public enum FlagUsageType {
- WARNING, ERROR;
+ WARNING, ERROR, ALLOW;
}
diff --git a/src/core/lombok/core/debug/AssertionLogger.java b/src/core/lombok/core/debug/AssertionLogger.java
new file mode 100644
index 00000000..6b2c326d
--- /dev/null
+++ b/src/core/lombok/core/debug/AssertionLogger.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.core.debug;
+
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Date;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import lombok.core.Version;
+
+/**
+ * We make a number of assumptions in lombok code, and if these assumptions fail, we try to fall back to a 'least bad' scenario. However, we would prefer to
+ * just know about these cases, without confronting our users with error messages. The 'fix' is to log such assertion failures to this logger, which promptly
+ * ignores them, _unless_ you specifically enable logging them to a file. If you'd like to help out or want to assist in debugging, turn this on.
+ */
+public class AssertionLogger {
+ private static final String LOG_PATH;
+
+ static {
+ String log = System.getProperty("lombok.assertion.log", null);
+ if (log != null) {
+ LOG_PATH = log.isEmpty() ? null : log;
+ } else {
+ try {
+ log = System.getenv("LOMBOK_ASSERTION_LOG");
+ } catch (Exception e) {
+ log = null;
+ }
+ LOG_PATH = (log == null || log.isEmpty()) ? null : log;
+ }
+ }
+
+ private static final AtomicBoolean loggedIntro = new AtomicBoolean(false);
+ private static final String PROCESS_ID = generateProcessId();
+
+ private static final String ID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+ private static String generateProcessId() {
+ char[] ID = new char[4];
+ Random r = new Random();
+ for (int i = 0; i < ID.length; i++) ID[i] = ID_CHARS.charAt(r.nextInt(ID_CHARS.length()));
+ return new String(ID);
+ }
+
+ private static synchronized void logToFile(String msg) {
+ if (msg == null) return;
+
+ try {
+ OutputStream out = new FileOutputStream(LOG_PATH, true);
+ out.write(msg.getBytes("UTF-8"));
+ out.close();
+ } catch (Exception e) {
+ throw new RuntimeException("assertion logging can't write to log file", e);
+ }
+ }
+
+ private static void logIntro() {
+ if (loggedIntro.getAndSet(true)) return;
+
+ String version;
+ try {
+ version = Version.getFullVersion();
+ } catch (Exception e) {
+ version = Version.getVersion();
+ }
+
+ logToFile(String.format("{%s} [%s -- START %s]\n", PROCESS_ID, new Date(), version));
+ }
+
+ public static <T extends Throwable> T assertLog(String message, T throwable) {
+ if (LOG_PATH == null) return throwable;
+
+ logIntro();
+
+ if (message == null) message = "(No message)";
+ String stackMsg = "";
+ if (throwable != null) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ throwable.printStackTrace(pw);
+ pw.close();
+ stackMsg = "\n " + sw.toString().replace("\r", "").replace("\n", "\n ").trim();
+ }
+ logToFile(String.format("{%s} [%ty%<tm%<tdT%<tH%<tM%<tS.%<tL] %s%s\n", PROCESS_ID, new Date(), message, stackMsg));
+ return throwable;
+ }
+
+ public static void assertLog(String message) {
+ if (LOG_PATH == null) return;
+ assertLog(message, null);
+ }
+}
diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
index 621d8760..6b516904 100644
--- a/src/core/lombok/core/handlers/HandlerUtil.java
+++ b/src/core/lombok/core/handlers/HandlerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-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
@@ -43,6 +43,7 @@ import lombok.core.AST;
import lombok.core.AnnotationValues;
import lombok.core.JavaIdentifiers;
import lombok.core.LombokNode;
+import lombok.core.configuration.AllowHelper;
import lombok.core.configuration.ConfigurationKey;
import lombok.core.configuration.FlagUsageType;
import lombok.experimental.Accessors;
@@ -68,6 +69,10 @@ public class HandlerUtil {
return 97;
}
+ public static int primeForNull() {
+ return 43;
+ }
+
/** Checks if the given name is a valid identifier.
*
* If it is, this returns {@code true} and does nothing else.
@@ -87,16 +92,30 @@ public class HandlerUtil {
return true;
}
+ public static String autoSingularize(String plural) {
+ return Singulars.autoSingularize(plural);
+ }
public static void handleFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) {
FlagUsageType fut = node.getAst().readConfiguration(key);
+ if (fut == null && AllowHelper.isAllowable(key)) {
+ node.addError("Use of " + featureName + " is disabled by default. Please add '" + key.getKeyName() + " = " + FlagUsageType.ALLOW + "' to 'lombok.config' if you want to enable is.");
+ }
+
if (fut != null) {
String msg = "Use of " + featureName + " is flagged according to lombok configuration.";
if (fut == FlagUsageType.WARNING) node.addWarning(msg);
- else node.addError(msg);
+ else if (fut == FlagUsageType.ERROR) node.addError(msg);
}
}
+ @SuppressWarnings("deprecation")
+ public static boolean shouldAddGenerated(LombokNode<?, ?, ?> node) {
+ Boolean add = node.getAst().readConfiguration(ConfigurationKeys.ADD_JAVAX_GENERATED_ANNOTATIONS);
+ if (add != null) return add;
+ return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_GENERATED_ANNOTATIONS));
+ }
+
public static void handleExperimentalFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) {
handleFlagUsage(node, key, featureName, ConfigurationKeys.EXPERIMENTAL_FLAG_USAGE, "any lombok.experimental feature");
}
@@ -152,11 +171,12 @@ public class HandlerUtil {
}
@SuppressWarnings({"all", "unchecked", "deprecation"})
- public static final List<Class<? extends java.lang.annotation.Annotation>> INVALID_ON_BUILDERS = Collections.unmodifiableList(
- Arrays.<Class<? extends java.lang.annotation.Annotation>>asList(
- Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class,
- RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class,
- Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class));
+ public static final List<String> INVALID_ON_BUILDERS = Collections.unmodifiableList(
+ Arrays.<String>asList(
+ Getter.class.getName(), Setter.class.getName(), Wither.class.getName(),
+ ToString.class.getName(), EqualsAndHashCode.class.getName(),
+ RequiredArgsConstructor.class.getName(), AllArgsConstructor.class.getName(), NoArgsConstructor.class.getName(),
+ Data.class.getName(), Value.class.getName(), "lombok.experimental.Value", FieldDefaults.class.getName()));
/**
* Given the name of a field, return the 'base name' of that field. For example, {@code fFoobar} becomes {@code foobar} if {@code f} is in the prefix list.
@@ -303,7 +323,7 @@ public class HandlerUtil {
return booleanPrefix + fName.substring(2);
}
- return buildName(isBoolean ? booleanPrefix : normalPrefix, fName);
+ return buildAccessorName(isBoolean ? booleanPrefix : normalPrefix, fName);
}
/**
@@ -375,8 +395,8 @@ public class HandlerUtil {
if (adhereToFluent && fluent) {
names.add(baseName);
} else {
- names.add(buildName(normalPrefix, baseName));
- if (!normalPrefix.equals(booleanPrefix)) names.add(buildName(booleanPrefix, baseName));
+ names.add(buildAccessorName(normalPrefix, baseName));
+ if (!normalPrefix.equals(booleanPrefix)) names.add(buildAccessorName(booleanPrefix, baseName));
}
}
@@ -407,7 +427,7 @@ public class HandlerUtil {
* @param suffix Something like {@code running}.
* @return prefix + smartly title-cased suffix. For example, {@code setRunning}.
*/
- private static String buildName(String prefix, String suffix) {
+ public static String buildAccessorName(String prefix, String suffix) {
if (suffix.length() == 0) return prefix;
if (prefix.length() == 0) return suffix;
diff --git a/src/core/lombok/core/handlers/Singulars.java b/src/core/lombok/core/handlers/Singulars.java
new file mode 100644
index 00000000..87895790
--- /dev/null
+++ b/src/core/lombok/core/handlers/Singulars.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.core.handlers;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Singulars {
+ private static final List<String> SINGULAR_STORE; // intended to be immutable.
+
+ static {
+ SINGULAR_STORE = new ArrayList<String>();
+
+ try {
+ InputStream in = Singulars.class.getResourceAsStream("singulars.txt");
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ line = line.trim();
+ if (line.startsWith("#") || line.isEmpty()) continue;
+ if (line.endsWith(" =")) {
+ SINGULAR_STORE.add(line.substring(0, line.length() - 2));
+ SINGULAR_STORE.add("");
+ continue;
+ }
+
+ int idx = line.indexOf(" = ");
+ SINGULAR_STORE.add(line.substring(0, idx));
+ SINGULAR_STORE.add(line.substring(idx + 3));
+ }
+ } finally {
+ try {
+ in.close();
+ } catch (Throwable ignore) {}
+ }
+ } catch (IOException e) {
+ SINGULAR_STORE.clear();
+ }
+ }
+
+ public static String autoSingularize(String in) {
+ final int inLen = in.length();
+ for (int i = 0; i < SINGULAR_STORE.size(); i+= 2) {
+ final String lastPart = SINGULAR_STORE.get(i);
+ final boolean wholeWord = Character.isUpperCase(lastPart.charAt(0));
+ final int endingOnly = lastPart.charAt(0) == '-' ? 1 : 0;
+ final int len = lastPart.length();
+ if (inLen < len) continue;
+ if (!in.regionMatches(true, inLen - len + endingOnly, lastPart, endingOnly, len - endingOnly)) continue;
+ if (wholeWord && inLen != len && !Character.isUpperCase(in.charAt(inLen - len))) continue;
+
+ String replacement = SINGULAR_STORE.get(i + 1);
+ if (replacement.equals("!")) return null;
+
+ boolean capitalizeFirst = !replacement.isEmpty() && Character.isUpperCase(in.charAt(inLen - len + endingOnly));
+ String pre = in.substring(0, inLen - len + endingOnly);
+ String post = capitalizeFirst ? Character.toUpperCase(replacement.charAt(0)) + replacement.substring(1) : replacement;
+ return pre + post;
+ }
+
+ return null;
+ }
+}
diff --git a/src/core/lombok/core/handlers/singulars.txt b/src/core/lombok/core/handlers/singulars.txt
new file mode 100644
index 00000000..d77308cb
--- /dev/null
+++ b/src/core/lombok/core/handlers/singulars.txt
@@ -0,0 +1,56 @@
+#Based on https://github.com/rails/rails/blob/efff6c1fd4b9e2e4c9f705a45879373cb34a5b0e/activesupport/lib/active_support/inflections.rb
+
+quizzes = quiz
+matrices = matrix
+indices = index
+vertices = vertex
+statuses = status
+aliases = alias
+alias = !
+species = !
+Axes = !
+-axes = axe
+sexes = sex
+Testes = testis
+movies = movie
+octopodes = octopus
+buses = bus
+Mice = mouse
+Lice = louse
+News = !
+# We could add more detail (axemen, boatsmen, boogymen, cavemen, gentlemen, etc, but (A) there's stuff like 'cerumen', and (B) the 'men' ending is common in singulars and other languages.)
+# Therefore, the odds of a mistake are too high, so other than these 2 well known cases, force the explicit singular.
+Men = man
+Women = woman
+minutiae = minutia
+shoes = shoe
+synopses = synopsis
+prognoses = prognosis
+theses = thesis
+diagnoses = diagnosis
+bases = base
+analyses = analysis
+Crises = crisis
+children = child
+moves = move
+zombies = zombie
+-quies = quy
+-us = !
+-is = !
+series = !
+-ies = y
+-oes = o
+hives = hive
+-tives = tive
+-sses = ss
+-ches = ch
+-xes = x
+-shes = sh
+-lves = lf
+-rves = rf
+saves = save
+Leaves = leaf
+-ves = !
+-ss = !
+-us = !
+-s =
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java
index 6df5c4d7..dc2c9843 100644
--- a/src/core/lombok/eclipse/EclipseAST.java
+++ b/src/core/lombok/eclipse/EclipseAST.java
@@ -62,7 +62,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
* @param ast The compilation unit, which serves as the top level node in the tree to be built.
*/
public EclipseAST(CompilationUnitDeclaration ast) {
- super(toFileName(ast), packageDeclaration(ast), new EclipseImportList(ast));
+ super(toFileName(ast), packageDeclaration(ast), new EclipseImportList(ast), statementTypes());
this.compilationUnitDeclaration = ast;
setTop(buildCompilationUnit(ast));
this.completeParse = isComplete(ast);
@@ -70,7 +70,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
private static volatile boolean skipEclipseWorkspaceBasedFileResolver = false;
- private static final URI NOT_CALCULATED_MARKER = URI.create("http://projectlombok.org/not/calculated");
+ private static final URI NOT_CALCULATED_MARKER = URI.create("https://projectlombok.org/not/calculated");
private URI memoizedAbsoluteFileLocation = NOT_CALCULATED_MARKER;
public URI getAbsoluteFileLocation() {
@@ -477,9 +477,9 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
return putInMap(new EclipseNode(this, statement, childNodes, Kind.STATEMENT));
}
- /** For Eclipse, only Statement counts, as Expression is a subclass of it, even though this isn't
+ /* For Eclipse, only Statement counts, as Expression is a subclass of it, even though this isn't
* entirely correct according to the JLS spec (only some expressions can be used as statements, not all of them). */
- @Override protected Collection<Class<? extends ASTNode>> getStatementTypes() {
+ private static Collection<Class<? extends ASTNode>> statementTypes() {
return Collections.<Class<? extends ASTNode>>singleton(Statement.class);
}
diff --git a/src/core/lombok/eclipse/EclipseASTVisitor.java b/src/core/lombok/eclipse/EclipseASTVisitor.java
index aa19adc6..f5b49cbb 100644
--- a/src/core/lombok/eclipse/EclipseASTVisitor.java
+++ b/src/core/lombok/eclipse/EclipseASTVisitor.java
@@ -40,7 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
/**
- * Implement so you can ask any JavacAST.Node to traverse depth-first through all children,
+ * Implement so you can ask any EclipseAST.Node to traverse depth-first through all children,
* calling the appropriate visit and endVisit methods.
*/
public interface EclipseASTVisitor {
diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java
index 2c970db2..49867e62 100644
--- a/src/core/lombok/eclipse/EclipseNode.java
+++ b/src/core/lombok/eclipse/EclipseNode.java
@@ -54,70 +54,70 @@ public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode,
switch (getKind()) {
case COMPILATION_UNIT:
- visitor.visitCompilationUnit(this, (CompilationUnitDeclaration)get());
+ visitor.visitCompilationUnit(this, (CompilationUnitDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitCompilationUnit(this, (CompilationUnitDeclaration)get());
+ visitor.endVisitCompilationUnit(this, (CompilationUnitDeclaration) get());
break;
case TYPE:
- visitor.visitType(this, (TypeDeclaration)get());
+ visitor.visitType(this, (TypeDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitType(this, (TypeDeclaration)get());
+ visitor.endVisitType(this, (TypeDeclaration) get());
break;
case FIELD:
- visitor.visitField(this, (FieldDeclaration)get());
+ visitor.visitField(this, (FieldDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitField(this, (FieldDeclaration)get());
+ visitor.endVisitField(this, (FieldDeclaration) get());
break;
case INITIALIZER:
- visitor.visitInitializer(this, (Initializer)get());
+ visitor.visitInitializer(this, (Initializer) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitInitializer(this, (Initializer)get());
+ visitor.endVisitInitializer(this, (Initializer) get());
break;
case METHOD:
if (get() instanceof Clinit) return;
- visitor.visitMethod(this, (AbstractMethodDeclaration)get());
+ visitor.visitMethod(this, (AbstractMethodDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitMethod(this, (AbstractMethodDeclaration)get());
+ visitor.endVisitMethod(this, (AbstractMethodDeclaration) get());
break;
case ARGUMENT:
- AbstractMethodDeclaration method = (AbstractMethodDeclaration)up().get();
- visitor.visitMethodArgument(this, (Argument)get(), method);
+ AbstractMethodDeclaration method = (AbstractMethodDeclaration) up().get();
+ visitor.visitMethodArgument(this, (Argument) get(), method);
ast.traverseChildren(visitor, this);
- visitor.endVisitMethodArgument(this, (Argument)get(), method);
+ visitor.endVisitMethodArgument(this, (Argument) get(), method);
break;
case LOCAL:
- visitor.visitLocal(this, (LocalDeclaration)get());
+ visitor.visitLocal(this, (LocalDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitLocal(this, (LocalDeclaration)get());
+ visitor.endVisitLocal(this, (LocalDeclaration) get());
break;
case ANNOTATION:
switch (up().getKind()) {
case TYPE:
- visitor.visitAnnotationOnType((TypeDeclaration)up().get(), this, (Annotation)get());
+ visitor.visitAnnotationOnType((TypeDeclaration) up().get(), this, (Annotation) get());
break;
case FIELD:
- visitor.visitAnnotationOnField((FieldDeclaration)up().get(), this, (Annotation)get());
+ visitor.visitAnnotationOnField((FieldDeclaration) up().get(), this, (Annotation) get());
break;
case METHOD:
- visitor.visitAnnotationOnMethod((AbstractMethodDeclaration)up().get(), this, (Annotation)get());
+ visitor.visitAnnotationOnMethod((AbstractMethodDeclaration) up().get(), this, (Annotation) get());
break;
case ARGUMENT:
visitor.visitAnnotationOnMethodArgument(
- (Argument)parent.get(),
- (AbstractMethodDeclaration)parent.directUp().get(),
- this, (Annotation)get());
+ (Argument) parent.get(),
+ (AbstractMethodDeclaration) parent.directUp().get(),
+ this, (Annotation) get());
break;
case LOCAL:
- visitor.visitAnnotationOnLocal((LocalDeclaration)parent.get(), this, (Annotation)get());
+ visitor.visitAnnotationOnLocal((LocalDeclaration) parent.get(), this, (Annotation) get());
break;
default:
throw new AssertionError("Annotation not expected as child of a " + up().getKind());
}
break;
case STATEMENT:
- visitor.visitStatement(this, (Statement)get());
+ visitor.visitStatement(this, (Statement) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitStatement(this, (Statement)get());
+ visitor.endVisitStatement(this, (Statement) get());
break;
default:
throw new AssertionError("Unexpected kind during node traversal: " + getKind());
diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java
index e6528178..683465c9 100644
--- a/src/core/lombok/eclipse/TransformEclipseAST.java
+++ b/src/core/lombok/eclipse/TransformEclipseAST.java
@@ -106,7 +106,7 @@ public class TransformEclipseAST {
EclipseAST existing = null;
if (astCacheField != null) {
try {
- existing = (EclipseAST)astCacheField.get(ast);
+ existing = (EclipseAST) astCacheField.get(ast);
} catch (Exception e) {
// existing remains null
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 8326e1d0..6617d21a 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-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
@@ -48,6 +48,7 @@ import lombok.core.TypeResolver;
import lombok.core.configuration.NullCheckExceptionType;
import lombok.core.debug.ProblemReporter;
import lombok.core.handlers.HandlerUtil;
+import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAST;
import lombok.eclipse.EclipseNode;
import lombok.experimental.Accessors;
@@ -143,7 +144,7 @@ public class EclipseHandlerUtil {
return getGeneratedBy(node) != null;
}
- public static ASTNode setGeneratedBy(ASTNode node, ASTNode source) {
+ public static <T extends ASTNode> T setGeneratedBy(T node, ASTNode source) {
ASTNode_generatedBy.set(node, source);
return node;
}
@@ -166,6 +167,7 @@ public class EclipseHandlerUtil {
}
public static boolean isFieldDeprecated(EclipseNode fieldNode) {
+ if (!(fieldNode.get() instanceof FieldDeclaration)) return false;
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
if ((field.modifiers & ClassFileConstants.AccDeprecated) != 0) {
return true;
@@ -195,16 +197,36 @@ public class EclipseHandlerUtil {
TypeResolver resolver = new TypeResolver(node.getImportList());
return resolver.typeMatches(node, type.getName(), typeName);
+ }
+
+ /**
+ * Checks if the given TypeReference node is likely to be a reference to the provided class.
+ *
+ * @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type.
+ * @param node A Lombok AST node. Any node in the appropriate compilation unit will do (used to get access to import statements).
+ * @param typeRef A type reference to check.
+ */
+ public static boolean typeMatches(String type, EclipseNode node, TypeReference typeRef) {
+ if (typeRef == null || typeRef.getTypeName() == null || typeRef.getTypeName().length == 0) return false;
+ String lastPartA = new String(typeRef.getTypeName()[typeRef.getTypeName().length -1]);
+ int lastIndex = type.lastIndexOf('.');
+ String lastPartB = lastIndex == -1 ? type : type.substring(lastIndex + 1);
+ if (!lastPartA.equals(lastPartB)) return false;
+ String typeName = toQualifiedName(typeRef.getTypeName());
+ TypeResolver resolver = new TypeResolver(node.getImportList());
+ return resolver.typeMatches(node, type, typeName);
}
public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(EclipseNode typeNode, EclipseNode errorNode) {
List<String> disallowed = null;
for (EclipseNode child : typeNode.down()) {
- for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
+ if (child.getKind() != Kind.ANNOTATION) continue;
+ for (String annType : INVALID_ON_BUILDERS) {
if (annotationTypeMatches(annType, child)) {
if (disallowed == null) disallowed = new ArrayList<String>();
- disallowed.add(annType.getSimpleName());
+ int lastIndex = annType.lastIndexOf('.');
+ disallowed.add(lastIndex == -1 ? annType : annType.substring(lastIndex + 1));
}
}
}
@@ -298,6 +320,10 @@ public class EclipseHandlerUtil {
return new SingleTypeReference(typeName, p);
}
+ public static TypeReference[] copyTypes(TypeReference[] refs) {
+ return copyTypes(refs, null);
+ }
+
/**
* Convenience method that creates a new array and copies each TypeReference in the source array via
* {@link #copyType(TypeReference, ASTNode)}.
@@ -312,6 +338,10 @@ public class EclipseHandlerUtil {
return outs;
}
+ public static TypeReference copyType(TypeReference ref) {
+ return copyType(ref, null);
+ }
+
/**
* You can't share TypeReference objects or subtle errors start happening.
* Unfortunately the TypeReference type hierarchy is complicated and there's no clone
@@ -336,22 +366,23 @@ public class EclipseHandlerUtil {
}
}
}
+
TypeReference typeRef = new ParameterizedQualifiedTypeReference(iRef.tokens, args, iRef.dimensions(), copy(iRef.sourcePositions));
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof ArrayQualifiedTypeReference) {
ArrayQualifiedTypeReference iRef = (ArrayQualifiedTypeReference) ref;
TypeReference typeRef = new ArrayQualifiedTypeReference(iRef.tokens, iRef.dimensions(), copy(iRef.sourcePositions));
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof QualifiedTypeReference) {
QualifiedTypeReference iRef = (QualifiedTypeReference) ref;
TypeReference typeRef = new QualifiedTypeReference(iRef.tokens, copy(iRef.sourcePositions));
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
@@ -368,14 +399,14 @@ public class EclipseHandlerUtil {
}
TypeReference typeRef = new ParameterizedSingleTypeReference(iRef.token, args, iRef.dimensions(), (long)iRef.sourceStart << 32 | iRef.sourceEnd);
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
if (ref instanceof ArrayTypeReference) {
ArrayTypeReference iRef = (ArrayTypeReference) ref;
TypeReference typeRef = new ArrayTypeReference(iRef.token, iRef.dimensions(), (long)iRef.sourceStart << 32 | iRef.sourceEnd);
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
@@ -386,14 +417,14 @@ public class EclipseHandlerUtil {
wildcard.sourceStart = original.sourceStart;
wildcard.sourceEnd = original.sourceEnd;
if (original.bound != null) wildcard.bound = copyType(original.bound, source);
- setGeneratedBy(wildcard, source);
+ if (source != null) setGeneratedBy(wildcard, source);
return wildcard;
}
if (ref instanceof SingleTypeReference) {
SingleTypeReference iRef = (SingleTypeReference) ref;
TypeReference typeRef = new SingleTypeReference(iRef.token, (long)iRef.sourceStart << 32 | iRef.sourceEnd);
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
return typeRef;
}
@@ -401,19 +432,17 @@ public class EclipseHandlerUtil {
}
public static Annotation[] copyAnnotations(ASTNode source, Annotation[]... allAnnotations) {
- boolean allNull = true;
-
- List<Annotation> result = new ArrayList<Annotation>();
+ List<Annotation> result = null;
for (Annotation[] annotations : allAnnotations) {
if (annotations != null) {
- allNull = false;
for (Annotation annotation : annotations) {
+ if (result == null) result = new ArrayList<Annotation>();
result.add(copyAnnotation(annotation, source));
}
}
}
- if (allNull) return null;
- return result.toArray(new Annotation[0]);
+
+ return result == null ? null : result.toArray(new Annotation[0]);
}
public static boolean hasAnnotation(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) {
@@ -434,6 +463,42 @@ public class EclipseHandlerUtil {
}
}
+ public static boolean hasAnnotation(String type, EclipseNode node) {
+ if (node == null) return false;
+ if (type == null) return false;
+ switch (node.getKind()) {
+ case ARGUMENT:
+ case FIELD:
+ case LOCAL:
+ case TYPE:
+ case METHOD:
+ for (EclipseNode child : node.down()) {
+ if (annotationTypeMatches(type, child)) return true;
+ }
+ // intentional fallthrough
+ default:
+ return false;
+ }
+ }
+
+ public static EclipseNode findAnnotation(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) {
+ if (node == null) return null;
+ if (type == null) return null;
+ switch (node.getKind()) {
+ case ARGUMENT:
+ case FIELD:
+ case LOCAL:
+ case TYPE:
+ case METHOD:
+ for (EclipseNode child : node.down()) {
+ if (annotationTypeMatches(type, child)) return child;
+ }
+ // intentional fallthrough
+ default:
+ return null;
+ }
+ }
+
/**
* Checks if the provided annotation type is likely to be the intended type for the given annotation node.
*
@@ -441,11 +506,25 @@ public class EclipseHandlerUtil {
*/
public static boolean annotationTypeMatches(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) {
if (node.getKind() != Kind.ANNOTATION) return false;
- return typeMatches(type, node, ((Annotation)node.get()).type);
+ return typeMatches(type, node, ((Annotation) node.get()).type);
+ }
+
+ /**
+ * Checks if the provided annotation type is likely to be the intended type for the given annotation node.
+ *
+ * This is a guess, but a decent one.
+ */
+ public static boolean annotationTypeMatches(String type, EclipseNode node) {
+ if (node.getKind() != Kind.ANNOTATION) return false;
+ return typeMatches(type, node, ((Annotation) node.get()).type);
+ }
+
+ public static TypeReference cloneSelfType(EclipseNode context) {
+ return cloneSelfType(context, null);
}
public static TypeReference cloneSelfType(EclipseNode context, ASTNode source) {
- int pS = source.sourceStart, pE = source.sourceEnd;
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
long p = (long)pS << 32 | pE;
EclipseNode type = context;
TypeReference result = null;
@@ -457,7 +536,7 @@ public class EclipseHandlerUtil {
int idx = 0;
for (TypeParameter param : typeDecl.typeParameters) {
TypeReference typeRef = new SingleTypeReference(param.name, (long)param.sourceStart << 32 | param.sourceEnd);
- setGeneratedBy(typeRef, source);
+ if (source != null) setGeneratedBy(typeRef, source);
refs[idx++] = typeRef;
}
result = new ParameterizedSingleTypeReference(typeDecl.name, refs, 0, p);
@@ -465,7 +544,7 @@ public class EclipseHandlerUtil {
result = new SingleTypeReference(((TypeDeclaration)type.get()).name, p);
}
}
- if (result != null) setGeneratedBy(result, source);
+ if (result != null && source != null) setGeneratedBy(result, source);
return result;
}
@@ -668,47 +747,39 @@ public class EclipseHandlerUtil {
*/
public static <A extends java.lang.annotation.Annotation> AnnotationValues<A>
createAnnotation(Class<A> type, final EclipseNode annotationNode) {
+
final Annotation annotation = (Annotation) annotationNode.get();
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
- final MemberValuePair[] pairs = annotation.memberValuePairs();
- for (Method m : type.getDeclaredMethods()) {
- if (!Modifier.isPublic(m.getModifiers())) continue;
- String name = m.getName();
+ MemberValuePair[] memberValuePairs = annotation.memberValuePairs();
+
+ if (memberValuePairs != null) for (final MemberValuePair pair : memberValuePairs) {
List<String> raws = new ArrayList<String>();
List<Object> expressionValues = new ArrayList<Object>();
List<Object> guesses = new ArrayList<Object>();
- Expression fullExpression = null;
Expression[] expressions = null;
- if (pairs != null) for (MemberValuePair pair : pairs) {
- char[] n = pair.name;
- String mName = n == null ? "value" : new String(pair.name);
- if (mName.equals(name)) fullExpression = pair.value;
+ char[] n = pair.name;
+ String mName = (n == null || n.length == 0) ? "value" : new String(pair.name);
+ final Expression rhs = pair.value;
+ if (rhs instanceof ArrayInitializer) {
+ expressions = ((ArrayInitializer)rhs).expressions;
+ } else if (rhs != null) {
+ expressions = new Expression[] { rhs };
}
-
- boolean isExplicit = fullExpression != null;
-
- if (isExplicit) {
- if (fullExpression instanceof ArrayInitializer) {
- expressions = ((ArrayInitializer)fullExpression).expressions;
- } else expressions = new Expression[] { fullExpression };
- if (expressions != null) for (Expression ex : expressions) {
- StringBuffer sb = new StringBuffer();
- ex.print(0, sb);
- raws.add(sb.toString());
- expressionValues.add(ex);
- guesses.add(calculateValue(ex));
- }
+ if (expressions != null) for (Expression ex : expressions) {
+ StringBuffer sb = new StringBuffer();
+ ex.print(0, sb);
+ raws.add(sb.toString());
+ expressionValues.add(ex);
+ guesses.add(calculateValue(ex));
}
- final Expression fullExpr = fullExpression;
final Expression[] exprs = expressions;
-
- values.put(name, new AnnotationValue(annotationNode, raws, expressionValues, guesses, isExplicit) {
+ values.put(mName, new AnnotationValue(annotationNode, raws, expressionValues, guesses, true) {
@Override public void setError(String message, int valueIdx) {
Expression ex;
- if (valueIdx == -1) ex = fullExpr;
+ if (valueIdx == -1) ex = rhs;
else ex = exprs != null ? exprs[valueIdx] : null;
if (ex == null) ex = annotation;
@@ -721,7 +792,7 @@ public class EclipseHandlerUtil {
@Override public void setWarning(String message, int valueIdx) {
Expression ex;
- if (valueIdx == -1) ex = fullExpr;
+ if (valueIdx == -1) ex = rhs;
else ex = exprs != null ? exprs[valueIdx] : null;
if (ex == null) ex = annotation;
@@ -734,6 +805,21 @@ public class EclipseHandlerUtil {
});
}
+ for (Method m : type.getDeclaredMethods()) {
+ if (!Modifier.isPublic(m.getModifiers())) continue;
+ String name = m.getName();
+ if (!values.containsKey(name)) {
+ values.put(name, new AnnotationValue(annotationNode, new ArrayList<String>(), new ArrayList<Object>(), new ArrayList<Object>(), false) {
+ @Override public void setError(String message, int valueIdx) {
+ annotationNode.addError(message);
+ }
+ @Override public void setWarning(String message, int valueIdx) {
+ annotationNode.addWarning(message);
+ }
+ });
+ }
+ }
+
return new AnnotationValues<A>(type, values, annotationNode);
}
@@ -811,7 +897,7 @@ public class EclipseHandlerUtil {
// Check if the class has a @Getter annotation.
- if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) {
+ if (!hasGetterAnnotation && HandleGetter.fieldQualifiesForGetterGeneration(field)) {
//Check if the class has @Getter or @Data annotation.
EclipseNode containingType = field.up();
@@ -865,7 +951,7 @@ public class EclipseHandlerUtil {
}
static Expression createFieldAccessor(EclipseNode field, FieldAccess fieldAccess, ASTNode source) {
- int pS = source.sourceStart, pE = source.sourceEnd;
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
long p = (long)pS << 32 | pE;
boolean lookForGetter = lookForGetter(field, fieldAccess);
@@ -881,14 +967,17 @@ public class EclipseHandlerUtil {
ref.receiver = new SingleNameReference(((TypeDeclaration)containerNode.get()).name, p);
} else {
Expression smallRef = new FieldReference(field.getName().toCharArray(), p);
- setGeneratedBy(smallRef, source);
+ if (source != null) setGeneratedBy(smallRef, source);
return smallRef;
}
} else {
ref.receiver = new ThisReference(pS, pE);
}
- setGeneratedBy(ref, source);
- setGeneratedBy(ref.receiver, source);
+
+ if (source != null) {
+ setGeneratedBy(ref, source);
+ setGeneratedBy(ref.receiver, source);
+ }
return ref;
}
@@ -1145,9 +1234,8 @@ public class EclipseHandlerUtil {
if (params < minArgs || params > maxArgs) continue;
}
- if (def.annotations != null) for (Annotation anno : def.annotations) {
- if (typeMatches(Tolerate.class, node, anno.type)) continue top;
- }
+
+ if (isTolerate(node, def)) continue top;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
@@ -1158,6 +1246,13 @@ public class EclipseHandlerUtil {
return MemberExistsResult.NOT_EXISTS;
}
+ public static boolean isTolerate(EclipseNode node, AbstractMethodDeclaration def) {
+ if (def.annotations != null) for (Annotation anno : def.annotations) {
+ if (typeMatches(Tolerate.class, node, anno.type)) return true;
+ }
+ return false;
+ }
+
/**
* Checks if there is a (non-default) constructor. In case of multiple constructors (overloading), only
* the first constructor decides if EXISTS_BY_USER or EXISTS_BY_LOMBOK is returned.
@@ -1171,13 +1266,11 @@ public class EclipseHandlerUtil {
if (node != null && node.get() instanceof TypeDeclaration) {
TypeDeclaration typeDecl = (TypeDeclaration)node.get();
- if (typeDecl.methods != null) top: for (AbstractMethodDeclaration def : typeDecl.methods) {
+ if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) {
if (def instanceof ConstructorDeclaration) {
if ((def.bits & ASTNode.IsDefaultConstructor) != 0) continue;
- if (def.annotations != null) for (Annotation anno : def.annotations) {
- if (typeMatches(Tolerate.class, node, anno.type)) continue top;
- }
+ if (isTolerate(node, def)) continue;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
@@ -1191,8 +1284,9 @@ public class EclipseHandlerUtil {
* Inserts a field into an existing type. The type must represent a {@code TypeDeclaration}.
* The field carries the &#64;{@link SuppressWarnings}("all") annotation.
*/
- public static EclipseNode injectFieldSuppressWarnings(EclipseNode type, FieldDeclaration field) {
- field.annotations = createSuppressWarningsAll(field, field.annotations);
+ public static EclipseNode injectFieldAndMarkGenerated(EclipseNode type, FieldDeclaration field) {
+ field.annotations = addSuppressWarningsAll(type, field, field.annotations);
+ field.annotations = addGenerated(type, field, field.annotations);
return injectField(type, field);
}
@@ -1237,7 +1331,8 @@ public class EclipseHandlerUtil {
* Inserts a method into an existing type. The type must represent a {@code TypeDeclaration}.
*/
public static EclipseNode injectMethod(EclipseNode type, AbstractMethodDeclaration method) {
- method.annotations = createSuppressWarningsAll(method, method.annotations);
+ method.annotations = addSuppressWarningsAll(type, method, method.annotations);
+ method.annotations = addGenerated(type, method, method.annotations);
TypeDeclaration parent = (TypeDeclaration) type.get();
if (parent.methods == null) {
@@ -1279,9 +1374,10 @@ public class EclipseHandlerUtil {
* @param type New type (class, interface, etc) to inject.
*/
public static EclipseNode injectType(final EclipseNode typeNode, final TypeDeclaration type) {
- type.annotations = createSuppressWarningsAll(type, type.annotations);
+ type.annotations = addSuppressWarningsAll(typeNode, type, type.annotations);
+ type.annotations = addGenerated(typeNode, type, type.annotations);
TypeDeclaration parent = (TypeDeclaration) typeNode.get();
-
+
if (parent.memberTypes == null) {
parent.memberTypes = new TypeDeclaration[] { type };
} else {
@@ -1295,32 +1391,82 @@ public class EclipseHandlerUtil {
}
private static final char[] ALL = "all".toCharArray();
+ private static final char[] JUSTIFICATION = "justification".toCharArray();
+ private static final char[] GENERATED_CODE = "generated code".toCharArray();
+ private static final char[] LOMBOK = "lombok".toCharArray();
+ private static final char[][] JAVAX_ANNOTATION_GENERATED = Eclipse.fromQualifiedName("javax.annotation.Generated");
+ private static final char[][] LOMBOK_GENERATED = Eclipse.fromQualifiedName("lombok.Generated");
+ private static final char[][] EDU_UMD_CS_FINDBUGS_ANNOTATIONS_SUPPRESSFBWARNINGS = Eclipse.fromQualifiedName("edu.umd.cs.findbugs.annotations.SuppressFBWarnings");
+
+ public static Annotation[] addSuppressWarningsAll(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
+ Annotation[] anns = addAnnotation(source, originalAnnotationArray, TypeConstants.JAVA_LANG_SUPPRESSWARNINGS, new StringLiteral(ALL, 0, 0, 0));
+
+ if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_FINDBUGS_SUPPRESSWARNINGS_ANNOTATIONS))) {
+ MemberValuePair mvp = new MemberValuePair(JUSTIFICATION, 0, 0, new StringLiteral(GENERATED_CODE, 0, 0, 0));
+ anns = addAnnotation(source, anns, EDU_UMD_CS_FINDBUGS_ANNOTATIONS_SUPPRESSFBWARNINGS, mvp);
+ }
+
+ return anns;
+ }
+
+ public static Annotation[] addGenerated(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
+ Annotation[] result = originalAnnotationArray;
+ if (HandlerUtil.shouldAddGenerated(node)) {
+ result = addAnnotation(source, result, JAVAX_ANNOTATION_GENERATED, new StringLiteral(LOMBOK, 0, 0, 0));
+ }
+ if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_LOMBOK_GENERATED_ANNOTATIONS))) {
+ result = addAnnotation(source, result, LOMBOK_GENERATED, null);
+ }
+ return result;
+ }
- public static Annotation[] createSuppressWarningsAll(ASTNode source, Annotation[] originalAnnotationArray) {
+ private static Annotation[] addAnnotation(ASTNode source, Annotation[] originalAnnotationArray, char[][] annotationTypeFqn, ASTNode arg) {
+ char[] simpleName = annotationTypeFqn[annotationTypeFqn.length - 1];
+
if (originalAnnotationArray != null) for (Annotation ann : originalAnnotationArray) {
- char[] lastToken = null;
-
if (ann.type instanceof QualifiedTypeReference) {
char[][] t = ((QualifiedTypeReference) ann.type).tokens;
- lastToken = t[t.length - 1];
- } else if (ann.type instanceof SingleTypeReference) {
- lastToken = ((SingleTypeReference) ann.type).token;
+ if (Arrays.deepEquals(t, annotationTypeFqn)) return originalAnnotationArray;
}
- if (lastToken != null && new String(lastToken).equals("SuppressWarnings")) return originalAnnotationArray;
+ if (ann.type instanceof SingleTypeReference) {
+ char[] lastToken = ((SingleTypeReference) ann.type).token;
+ if (Arrays.equals(lastToken, simpleName)) return originalAnnotationArray;
+ }
}
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
- long[] poss = new long[3];
+ long[] poss = new long[annotationTypeFqn.length];
Arrays.fill(poss, p);
- QualifiedTypeReference suppressWarningsType = new QualifiedTypeReference(TypeConstants.JAVA_LANG_SUPPRESSWARNINGS, poss);
- setGeneratedBy(suppressWarningsType, source);
- SingleMemberAnnotation ann = new SingleMemberAnnotation(suppressWarningsType, pS);
- ann.declarationSourceEnd = pE;
- ann.memberValue = new StringLiteral(ALL, pS, pE, 0);
+ QualifiedTypeReference qualifiedType = new QualifiedTypeReference(annotationTypeFqn, poss);
+ setGeneratedBy(qualifiedType, source);
+ Annotation ann;
+ if (arg instanceof Expression) {
+ SingleMemberAnnotation sma = new SingleMemberAnnotation(qualifiedType, pS);
+ sma.declarationSourceEnd = pE;
+ arg.sourceStart = pS;
+ arg.sourceEnd = pE;
+ sma.memberValue = (Expression) arg;
+ setGeneratedBy(sma.memberValue, source);
+ ann = sma;
+ } else if (arg instanceof MemberValuePair) {
+ NormalAnnotation na = new NormalAnnotation(qualifiedType, pS);
+ na.declarationSourceEnd = pE;
+ arg.sourceStart = pS;
+ arg.sourceEnd = pE;
+ na.memberValuePairs = new MemberValuePair[] {(MemberValuePair) arg};
+ setGeneratedBy(na.memberValuePairs[0], source);
+ setGeneratedBy(na.memberValuePairs[0].value, source);
+ na.memberValuePairs[0].value.sourceStart = pS;
+ na.memberValuePairs[0].value.sourceEnd = pE;
+ ann = na;
+ } else {
+ MarkerAnnotation ma = new MarkerAnnotation(qualifiedType, pS);
+ ma.declarationSourceEnd = pE;
+ ann = ma;
+ }
setGeneratedBy(ann, source);
- setGeneratedBy(ann.memberValue, source);
if (originalAnnotationArray == null) return new Annotation[] { ann };
Annotation[] newAnnotationArray = new Annotation[originalAnnotationArray.length + 1];
System.arraycopy(originalAnnotationArray, 0, newAnnotationArray, 0, originalAnnotationArray.length);
@@ -1489,7 +1635,7 @@ public class EclipseHandlerUtil {
* with eclipse versions before 3.7.
*/
public static IntLiteral makeIntLiteral(char[] token, ASTNode source) {
- int pS = source.sourceStart, pE = source.sourceEnd;
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
IntLiteral result;
try {
if (intLiteralConstructor != null) {
@@ -1504,7 +1650,8 @@ public class EclipseHandlerUtil {
} catch (InstantiationException e) {
throw Lombok.sneakyThrow(e);
}
- setGeneratedBy(result, source);
+
+ if (source != null) setGeneratedBy(result, source);
return result;
}
@@ -1535,6 +1682,14 @@ public class EclipseHandlerUtil {
return true;
}
+ public static void addError(String errorName, EclipseNode node) {
+ if (node.getLatestJavaSpecSupported() < 8) {
+ node.addError("The correct format is " + errorName + "_={@SomeAnnotation, @SomeOtherAnnotation})");
+ } else {
+ node.addError("The correct format is " + errorName + "=@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ }
+ }
+
public static List<Annotation> unboxAndRemoveAnnotationParameter(Annotation annotation, String annotationName, String errorName, EclipseNode errorNode) {
if ("value".equals(annotationName)) {
// We can't unbox this, because SingleMemberAnnotation REQUIRES a value, and this method
@@ -1557,51 +1712,70 @@ public class EclipseHandlerUtil {
char[] nameAsCharArray = annotationName.toCharArray();
+ top:
for (int i = 0; i < pairs.length; i++) {
- if (pairs[i].name == null || !Arrays.equals(nameAsCharArray, pairs[i].name)) continue;
+ boolean allowRaw;
+ char[] name = pairs[i].name;
+ if (name == null) continue;
+ if (name.length < nameAsCharArray.length) continue;
+ for (int j = 0; j < nameAsCharArray.length; j++) {
+ if (name[j] != nameAsCharArray[j]) continue top;
+ }
+ allowRaw = name.length > nameAsCharArray.length;
+ for (int j = nameAsCharArray.length; j < name.length; j++) {
+ if (name[j] != '_') continue top;
+ }
+ // If we're still here it's the targeted annotation param.
Expression value = pairs[i].value;
MemberValuePair[] newPairs = new MemberValuePair[pairs.length - 1];
if (i > 0) System.arraycopy(pairs, 0, newPairs, 0, i);
if (i < pairs.length - 1) System.arraycopy(pairs, i + 1, newPairs, i, pairs.length - i - 1);
normalAnnotation.memberValuePairs = newPairs;
- // We have now removed the annotation parameter and stored '@__({... annotations ...})',
- // which we must now unbox.
- if (!(value instanceof Annotation)) {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
- return Collections.emptyList();
- }
-
- Annotation atDummyIdentifier = (Annotation) value;
- if (!(atDummyIdentifier.type instanceof SingleTypeReference) ||
- !isAllValidOnXCharacters(((SingleTypeReference) atDummyIdentifier.type).token)) {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
- return Collections.emptyList();
- }
-
- if (atDummyIdentifier instanceof MarkerAnnotation) {
- // It's @Getter(onMethod=@__). This is weird, but fine.
- return Collections.emptyList();
- }
+ // We have now removed the annotation parameter and stored the value,
+ // which we must now unbox. It's either annotations, or @__(annotations).
Expression content = null;
- if (atDummyIdentifier instanceof NormalAnnotation) {
- MemberValuePair[] mvps = ((NormalAnnotation) atDummyIdentifier).memberValuePairs;
- if (mvps == null || mvps.length == 0) {
- // It's @Getter(onMethod=@__()). This is weird, but fine.
+ if (value instanceof ArrayInitializer) {
+ if (!allowRaw) {
+ addError(errorName, errorNode);
return Collections.emptyList();
}
- if (mvps.length == 1 && Arrays.equals("value".toCharArray(), mvps[0].name)) {
- content = mvps[0].value;
+ content = value;
+ } else if (!(value instanceof Annotation)) {
+ addError(errorName, errorNode);
+ return Collections.emptyList();
+ } else {
+ Annotation atDummyIdentifier = (Annotation) value;
+ if (atDummyIdentifier.type instanceof SingleTypeReference && isAllValidOnXCharacters(((SingleTypeReference) atDummyIdentifier.type).token)) {
+ if (atDummyIdentifier instanceof MarkerAnnotation) {
+ return Collections.emptyList();
+ } else if (atDummyIdentifier instanceof NormalAnnotation) {
+ MemberValuePair[] mvps = ((NormalAnnotation) atDummyIdentifier).memberValuePairs;
+ if (mvps == null || mvps.length == 0) {
+ return Collections.emptyList();
+ }
+ if (mvps.length == 1 && Arrays.equals("value".toCharArray(), mvps[0].name)) {
+ content = mvps[0].value;
+ }
+ } else if (atDummyIdentifier instanceof SingleMemberAnnotation) {
+ content = ((SingleMemberAnnotation) atDummyIdentifier).memberValue;
+ } else {
+ addError(errorName, errorNode);
+ return Collections.emptyList();
+ }
+ } else {
+ if (allowRaw) {
+ content = atDummyIdentifier;
+ } else {
+ addError(errorName, errorNode);
+ return Collections.emptyList();
+ }
}
}
- if (atDummyIdentifier instanceof SingleMemberAnnotation) {
- content = ((SingleMemberAnnotation) atDummyIdentifier).memberValue;
- }
-
if (content == null) {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ addError(errorName, errorNode);
return Collections.emptyList();
}
@@ -1613,13 +1787,13 @@ public class EclipseHandlerUtil {
if (expressions != null) for (Expression ex : expressions) {
if (ex instanceof Annotation) result.add((Annotation) ex);
else {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ addError(errorName, errorNode);
return Collections.emptyList();
}
}
return result;
} else {
- errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ addError(errorName, errorNode);
return Collections.emptyList();
}
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
new file mode 100644
index 00000000..bc779dab
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers;
+
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.SpiLoadUtil;
+import lombok.core.TypeLibrary;
+import lombok.eclipse.EclipseNode;
+
+public class EclipseSingularsRecipes {
+ private static final EclipseSingularsRecipes INSTANCE = new EclipseSingularsRecipes();
+ private final Map<String, EclipseSingularizer> singularizers = new HashMap<String, EclipseSingularizer>();
+ private final TypeLibrary singularizableTypes = new TypeLibrary();
+
+ private EclipseSingularsRecipes() {
+ try {
+ loadAll(singularizableTypes, singularizers);
+ singularizableTypes.lock();
+ } catch (IOException e) {
+ System.err.println("Lombok's @Singularizable feature is broken due to misconfigured SPI files: " + e);
+ }
+ }
+
+ private static void loadAll(TypeLibrary library, Map<String, EclipseSingularizer> map) throws IOException {
+ for (EclipseSingularizer handler : SpiLoadUtil.findServices(EclipseSingularizer.class, EclipseSingularizer.class.getClassLoader())) {
+ for (String type : handler.getSupportedTypes()) {
+ EclipseSingularizer existingSingularizer = map.get(type);
+ if (existingSingularizer != null) {
+ EclipseSingularizer toKeep = existingSingularizer.getClass().getName().compareTo(handler.getClass().getName()) > 0 ? handler : existingSingularizer;
+ System.err.println("Multiple singularizers found for type " + type + "; the alphabetically first class is used: " + toKeep.getClass().getName());
+ map.put(type, toKeep);
+ } else {
+ map.put(type, handler);
+ library.addType(type);
+ }
+ }
+ }
+ }
+
+ public static EclipseSingularsRecipes get() {
+ return INSTANCE;
+ }
+
+ public String toQualified(String typeReference) {
+ return singularizableTypes.toQualified(typeReference);
+ }
+
+ public EclipseSingularizer getSingularizer(String fqn) {
+ return singularizers.get(fqn);
+ }
+
+ public static final class SingularData {
+ private final EclipseNode annotation;
+ private final char[] singularName;
+ private final char[] pluralName;
+ private final List<TypeReference> typeArgs;
+ private final String targetFqn;
+ private final EclipseSingularizer singularizer;
+ private final ASTNode source;
+
+ public SingularData(EclipseNode annotation, char[] singularName, char[] pluralName, List<TypeReference> typeArgs, String targetFqn, EclipseSingularizer singularizer, ASTNode source) {
+ this.annotation = annotation;
+ this.singularName = singularName;
+ this.pluralName = pluralName;
+ this.typeArgs = typeArgs;
+ this.targetFqn = targetFqn;
+ this.singularizer = singularizer;
+ this.source = source;
+ }
+
+ public void setGeneratedByRecursive(ASTNode target) {
+ SetGeneratedByVisitor visitor = new SetGeneratedByVisitor(source);
+
+ if (target instanceof AbstractMethodDeclaration) {
+ ((AbstractMethodDeclaration) target).traverse(visitor, (ClassScope) null);
+ } else if (target instanceof FieldDeclaration) {
+ ((FieldDeclaration) target).traverse(visitor, (MethodScope) null);
+ } else {
+ target.traverse(visitor, null);
+ }
+ }
+
+ public ASTNode getSource() {
+ return source;
+ }
+
+ public EclipseNode getAnnotation() {
+ return annotation;
+ }
+
+ public char[] getSingularName() {
+ return singularName;
+ }
+
+ public char[] getPluralName() {
+ return pluralName;
+ }
+
+ public List<TypeReference> getTypeArgs() {
+ return typeArgs;
+ }
+
+ public String getTargetFqn() {
+ return targetFqn;
+ }
+
+ public EclipseSingularizer getSingularizer() {
+ return singularizer;
+ }
+
+ public String getTargetSimpleType() {
+ int idx = targetFqn.lastIndexOf(".");
+ return idx == -1 ? targetFqn : targetFqn.substring(idx + 1);
+ }
+ }
+
+ public static abstract class EclipseSingularizer {
+ protected static final long[] NULL_POSS = {0L};
+ public abstract LombokImmutableList<String> getSupportedTypes();
+
+ /** Checks if any of the to-be-generated nodes (fields, methods) already exist. If so, errors on these (singulars don't support manually writing some of it, and returns true). */
+ public boolean checkForAlreadyExistingNodesAndGenerateError(EclipseNode builderType, SingularData data) {
+ for (EclipseNode child : builderType.down()) {
+ switch (child.getKind()) {
+ case FIELD: {
+ FieldDeclaration fd = (FieldDeclaration) child.get();
+ char[] name = fd.name;
+ if (name == null) continue;
+ if (getGeneratedBy(fd) != null) continue;
+ for (char[] fieldToBeGenerated : listFieldsToBeGenerated(data, builderType)) {
+ if (!Arrays.equals(name, fieldToBeGenerated)) continue;
+ child.addError("Manually adding a field that @Singular @Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use @Singular.");
+ return true;
+ }
+ break;
+ }
+ case METHOD: {
+ AbstractMethodDeclaration method = (AbstractMethodDeclaration) child.get();
+ char[] name = method.selector;
+ if (name == null) continue;
+ if (getGeneratedBy(method) != null) continue;
+ for (char[] methodToBeGenerated : listMethodsToBeGenerated(data, builderType)) {
+ if (!Arrays.equals(name, methodToBeGenerated)) continue;
+ child.addError("Manually adding a method that @Singular @Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use @Singular.");
+ return true;
+ }
+ break;
+ }}
+ }
+
+ return false;
+ }
+
+ public List<char[]> listFieldsToBeGenerated(SingularData data, EclipseNode builderType) {
+ return Collections.singletonList(data.pluralName);
+ }
+
+ public List<char[]> listMethodsToBeGenerated(SingularData data, EclipseNode builderType) {
+ char[] p = data.pluralName;
+ char[] s = data.singularName;
+ if (Arrays.equals(p, s)) return Collections.singletonList(p);
+ return Arrays.asList(p, s);
+ }
+
+ public abstract List<EclipseNode> generateFields(SingularData data, EclipseNode builderType);
+ public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain);
+ public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName);
+
+ public boolean requiresCleaning() {
+ try {
+ return !getClass().getMethod("appendCleaningCode", SingularData.class, EclipseNode.class, List.class).getDeclaringClass().equals(EclipseSingularizer.class);
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ public void appendCleaningCode(SingularData data, EclipseNode builderType, List<Statement> statements) {
+ }
+
+ // -- Utility methods --
+
+ /**
+ * Adds the requested number of type arguments to the provided type, copying each argument in {@code typeArgs}. If typeArgs is too long, the extra elements are ignored.
+ * If {@code typeArgs} is null or too short, {@code java.lang.Object} will be substituted for each missing type argument.
+ *
+ * @param count The number of type arguments requested.
+ * @param addExtends If {@code true}, all bounds are either '? extends X' or just '?'. If false, the reverse is applied, and '? extends Foo' is converted to Foo, '?' to Object, etc.
+ * @param node Some node in the same AST. Just used to obtain makers and contexts and such.
+ * @param type The type to add generics to.
+ * @param typeArgs the list of type args to clone.
+ * @param source The source annotation that is the root cause of this code generation.
+ */
+ protected TypeReference addTypeArgs(int count, boolean addExtends, EclipseNode node, TypeReference type, List<TypeReference> typeArgs) {
+ TypeReference[] clonedAndFixedArgs = createTypeArgs(count, addExtends, node, typeArgs);
+ if (type instanceof SingleTypeReference) {
+ type = new ParameterizedSingleTypeReference(((SingleTypeReference) type).token, clonedAndFixedArgs, 0, 0L);
+ } else if (type instanceof QualifiedTypeReference) {
+ QualifiedTypeReference qtr = (QualifiedTypeReference) type;
+ TypeReference[][] trs = new TypeReference[qtr.tokens.length][];
+ trs[qtr.tokens.length - 1] = clonedAndFixedArgs;
+ type = new ParameterizedQualifiedTypeReference(((QualifiedTypeReference) type).tokens, trs, 0, NULL_POSS);
+ } else {
+ node.addError("Don't know how to clone-and-parameterize type: " + type);
+ }
+
+ return type;
+ }
+
+ protected TypeReference[] createTypeArgs(int count, boolean addExtends, EclipseNode node, List<TypeReference> typeArgs) {
+ if (count < 0) throw new IllegalArgumentException("count is negative");
+ if (count == 0) return null;
+ List<TypeReference> arguments = new ArrayList<TypeReference>();
+
+ if (typeArgs != null) for (TypeReference orig : typeArgs) {
+ Wildcard wildcard = orig instanceof Wildcard ? (Wildcard) orig : null;
+ if (!addExtends) {
+ if (wildcard != null && (wildcard.kind == Wildcard.UNBOUND || wildcard.kind == Wildcard.SUPER)) {
+ arguments.add(new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS));
+ } else if (wildcard != null && wildcard.kind == Wildcard.EXTENDS) {
+ try {
+ arguments.add(copyType(wildcard.bound));
+ } catch (Exception e) {
+ arguments.add(new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS));
+ }
+ } else {
+ arguments.add(copyType(orig));
+ }
+ } else {
+ if (wildcard != null && (wildcard.kind == Wildcard.UNBOUND || wildcard.kind == Wildcard.SUPER)) {
+ Wildcard w = new Wildcard(Wildcard.UNBOUND);
+ arguments.add(w);
+ } else if (wildcard != null && wildcard.kind == Wildcard.EXTENDS) {
+ arguments.add(copyType(orig));
+ } else {
+ Wildcard w = new Wildcard(Wildcard.EXTENDS);
+ w.bound = copyType(orig);
+ arguments.add(w);
+ }
+ }
+ if (--count == 0) break;
+ }
+
+ while (count-- > 0) {
+ if (addExtends) {
+ arguments.add(new Wildcard(Wildcard.UNBOUND));
+ } else {
+ arguments.add(new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS));
+ }
+ }
+
+ if (arguments.isEmpty()) return null;
+ return arguments.toArray(new TypeReference[arguments.size()]);
+ }
+
+ private static final char[] SIZE_TEXT = new char[] {'s', 'i', 'z', 'e'};
+
+ /** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */
+ protected Expression getSize(EclipseNode builderType, char[] name, boolean nullGuard) {
+ MessageSend invoke = new MessageSend();
+ ThisReference thisRef = new ThisReference(0, 0);
+ FieldReference thisDotName = new FieldReference(name, 0L);
+ thisDotName.receiver = thisRef;
+ invoke.receiver = thisDotName;
+ invoke.selector = SIZE_TEXT;
+ if (!nullGuard) return invoke;
+
+ ThisReference cdnThisRef = new ThisReference(0, 0);
+ FieldReference cdnThisDotName = new FieldReference(name, 0L);
+ cdnThisDotName.receiver = cdnThisRef;
+ NullLiteral nullLiteral = new NullLiteral(0, 0);
+ EqualExpression isNull = new EqualExpression(cdnThisDotName, nullLiteral, OperatorIds.EQUAL_EQUAL);
+ IntLiteral zeroLiteral = makeIntLiteral(new char[] {'0'}, null);
+ ConditionalExpression conditional = new ConditionalExpression(isNull, zeroLiteral, invoke);
+ return conditional;
+ }
+
+ protected TypeReference cloneParamType(int index, List<TypeReference> typeArgs, EclipseNode builderType) {
+ if (typeArgs != null && typeArgs.size() > index) {
+ TypeReference originalType = typeArgs.get(index);
+ if (originalType instanceof Wildcard) {
+ Wildcard wOriginalType = (Wildcard) originalType;
+ if (wOriginalType.kind == Wildcard.EXTENDS) {
+ try {
+ return copyType(wOriginalType.bound);
+ } catch (Exception e) {
+ // fallthrough
+ }
+ }
+ } else {
+ return copyType(originalType);
+ }
+ }
+
+ return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS);
+ }
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 522501f6..d4cdc654 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-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
@@ -32,53 +32,132 @@ import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.mangosdk.spi.ProviderFor;
import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Builder.ObtainVia;
import lombok.ConfigurationKeys;
+import lombok.Singular;
import lombok.core.AST.Kind;
+import lombok.core.handlers.HandlerUtil;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
import lombok.eclipse.handlers.HandleConstructor.SkipIfConstructorExists;
-import lombok.experimental.Builder;
import lombok.experimental.NonFinal;
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
- @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
+ private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray();
+ private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray();
+
+ private static final boolean toBoolean(Object expr, boolean defaultValue) {
+ if (expr == null) return defaultValue;
+ if (expr instanceof FalseLiteral) return false;
+ if (expr instanceof TrueLiteral) return true;
+ return ((Boolean) expr).booleanValue();
+ }
+
+ private static class BuilderFieldData {
+ TypeReference type;
+ char[] rawName;
+ char[] name;
+ char[] nameOfDefaultProvider;
+ char[] nameOfSetFlag;
+ SingularData singularData;
+ ObtainVia obtainVia;
+ EclipseNode obtainViaNode;
+ EclipseNode originalFieldNode;
+ List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
+ }
+
+ private static boolean equals(String a, char[] b) {
+ if (a.length() != b.length) return false;
+ for (int i = 0; i < b.length; i++) {
+ if (a.charAt(i) != b[i]) return false;
+ }
+ return true;
+ }
+
+ private static boolean equals(String a, char[][] b) {
+ if (a == null || a.isEmpty()) return b.length == 0;
+ String[] aParts = a.split("\\.");
+ if (aParts.length != b.length) return false;
+ for (int i = 0; i < b.length; i++) {
+ if (!equals(aParts[i], b[i])) return false;
+ }
+ return true;
+ }
+
+ private static final char[] DEFAULT_PREFIX = {'$', 'd', 'e', 'f', 'a', 'u', 'l', 't', '$'};
+ private static final char[] SET_PREFIX = {'$', 's', 'e', 't'};
+
+ private static final char[] prefixWith(char[] prefix, char[] name) {
+ char[] out = new char[prefix.length + name.length];
+ System.arraycopy(prefix, 0, out, 0, prefix.length);
+ System.arraycopy(name, 0, out, prefix.length, name.length);
+ return out;
+ }
+
+ @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
Builder builderInstance = annotation.getInstance();
+
+ // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them.
+ boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true);
+ boolean chain = toBoolean(annotation.getActualExpression("chain"), true);
+
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
+ String toBuilderMethodName = "toBuilder";
+ boolean toBuilder = builderInstance.toBuilder();
+ List<char[]> typeArgsForToBuilder = null;
if (builderMethodName == null) builderMethodName = "builder";
if (buildMethodName == null) builderMethodName = "build";
@@ -92,36 +171,64 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
EclipseNode parent = annotationNode.up();
- List<TypeReference> typesOfParameters = new ArrayList<TypeReference>();
- List<char[]> namesOfParameters = new ArrayList<char[]>();
+ List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
TypeReference returnType;
TypeParameter[] typeParams;
TypeReference[] thrownExceptions;
char[] nameOfStaticBuilderMethod;
EclipseNode tdParent;
- AbstractMethodDeclaration fillParametersFrom = null;
+ EclipseNode fillParametersFrom = parent.get() instanceof AbstractMethodDeclaration ? parent : null;
+ boolean addCleaning = false;
+ boolean isStatic = true;
if (parent.get() instanceof TypeDeclaration) {
tdParent = parent;
TypeDeclaration td = (TypeDeclaration) tdParent.get();
- List<EclipseNode> fields = new ArrayList<EclipseNode>();
- @SuppressWarnings("deprecation")
- boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
- for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
+ List<EclipseNode> allFields = new ArrayList<EclipseNode>();
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent));
+ for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) {
FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
- // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
- // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
- // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
- if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
- namesOfParameters.add(removePrefixFromField(fieldNode));
- typesOfParameters.add(fd.type);
- fields.add(fieldNode);
+ EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode);
+ boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode));
+
+ BuilderFieldData bfd = new BuilderFieldData();
+ bfd.rawName = fieldNode.getName().toCharArray();
+ bfd.name = removePrefixFromField(fieldNode);
+ bfd.type = fd.type;
+ bfd.singularData = getSingularData(fieldNode, ast);
+ bfd.originalFieldNode = fieldNode;
+
+ if (bfd.singularData != null && isDefault != null) {
+ isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
+ isDefault = null;
+ }
+
+ if (fd.initialization == null && isDefault != null) {
+ isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
+ isDefault = null;
+ }
+
+ if (fd.initialization != null && isDefault == null) {
+ if (isFinal) continue;
+ fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
+ }
+
+ if (isDefault != null) {
+ bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name);
+ bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX);
+
+ MethodDeclaration md = generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ addObtainVia(bfd, fieldNode);
+ builderFields.add(bfd);
+ allFields.add(fieldNode);
}
- new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, fields, null, SkipIfConstructorExists.I_AM_BUILDER, null,
- Collections.<Annotation>emptyList(), annotationNode);
+ handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER,
+ Collections.<Annotation>emptyList(), annotationNode);
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
typeParams = td.typeParameters;
@@ -137,7 +244,6 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
tdParent = parent.up();
TypeDeclaration td = (TypeDeclaration) tdParent.get();
- fillParametersFrom = cd;
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
typeParams = td.typeParameters;
thrownExceptions = cd.thrownExceptions;
@@ -146,11 +252,79 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
} else if (parent.get() instanceof MethodDeclaration) {
MethodDeclaration md = (MethodDeclaration) parent.get();
tdParent = parent.up();
- if (!md.isStatic()) {
- annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
- return;
+ isStatic = md.isStatic();
+
+ if (toBuilder) {
+ final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
+ char[] token;
+ char[][] pkg = null;
+ if (md.returnType.dimensions() > 0) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (md.returnType instanceof SingleTypeReference) {
+ token = ((SingleTypeReference) md.returnType).token;
+ } else if (md.returnType instanceof QualifiedTypeReference) {
+ pkg = ((QualifiedTypeReference) md.returnType).tokens;
+ token = pkg[pkg.length];
+ char[][] pkg_ = new char[pkg.length - 1][];
+ System.arraycopy(pkg, 0, pkg_, 0, pkg_.length);
+ pkg = pkg_;
+ } else {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (pkg != null && !equals(parent.getPackageDeclaration(), pkg)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (tdParent == null || !equals(tdParent.getName(), token)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ TypeParameter[] tpOnType = ((TypeDeclaration) tdParent.get()).typeParameters;
+ TypeParameter[] tpOnMethod = md.typeParameters;
+ TypeReference[][] tpOnRet_ = null;
+ if (md.returnType instanceof ParameterizedSingleTypeReference) {
+ tpOnRet_ = new TypeReference[1][];
+ tpOnRet_[0] = ((ParameterizedSingleTypeReference) md.returnType).typeArguments;
+ } else if (md.returnType instanceof ParameterizedQualifiedTypeReference) {
+ tpOnRet_ = ((ParameterizedQualifiedTypeReference) md.returnType).typeArguments;
+ }
+
+ if (tpOnRet_ != null) for (int i = 0; i < tpOnRet_.length - 1; i++) {
+ if (tpOnRet_[i] != null && tpOnRet_[i].length > 0) {
+ annotationNode.addError("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate.");
+ return;
+ }
+ }
+ TypeReference[] tpOnRet = tpOnRet_ == null ? null : tpOnRet_[tpOnRet_.length - 1];
+ typeArgsForToBuilder = new ArrayList<char[]>();
+
+ // Every typearg on this method needs to be found in the return type, but the reverse is not true.
+ // We also need to 'map' them.
+
+
+ if (tpOnMethod != null) for (TypeParameter onMethod : tpOnMethod) {
+ int pos = -1;
+ if (tpOnRet != null) for (int i = 0; i < tpOnRet.length; i++) {
+ if (tpOnRet[i].getClass() != SingleTypeReference.class) continue;
+ if (!Arrays.equals(((SingleTypeReference) tpOnRet[i]).token, onMethod.name)) continue;
+ pos = i;
+ }
+ if (pos == -1 || tpOnType == null || tpOnType.length <= pos) {
+ annotationNode.addError("@Builder(toBuilder=true) requires that each type parameter on the static method is part of the typeargs of the return value. Type parameter " + new String(onMethod.name) + " is not part of the return type.");
+ return;
+ }
+
+ typeArgsForToBuilder.add(tpOnType[pos].name);
+ }
}
- fillParametersFrom = md;
+
returnType = copyType(md.returnType, ast);
typeParams = md.typeParameters;
thrownExceptions = md.thrownExceptions;
@@ -185,159 +359,373 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
builderClassName = new String(token) + "Builder";
}
} else {
- annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
+ annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
return;
}
if (fillParametersFrom != null) {
- if (fillParametersFrom.arguments != null) for (Argument a : fillParametersFrom.arguments) {
- namesOfParameters.add(a.name);
- typesOfParameters.add(a.type);
+ for (EclipseNode param : fillParametersFrom.down()) {
+ if (param.getKind() != Kind.ARGUMENT) continue;
+ BuilderFieldData bfd = new BuilderFieldData();
+ Argument arg = (Argument) param.get();
+ bfd.rawName = arg.name;
+ bfd.name = arg.name;
+ bfd.type = arg.type;
+ bfd.singularData = getSingularData(param, ast);
+ bfd.originalFieldNode = param;
+ addObtainVia(bfd, param);
+ builderFields.add(bfd);
}
}
EclipseNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
- builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast);
} else {
+ TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get();
+ if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) {
+ annotationNode.addError("Existing Builder must be a static inner class.");
+ return;
+ } else if (!isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) != 0) {
+ annotationNode.addError("Existing Builder must be a non-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;
+ }
+ }
+ }
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ if (bfd.singularData.getSingularizer().requiresCleaning()) {
+ addCleaning = true;
+ break;
+ }
+ }
+ if (bfd.obtainVia != null) {
+ if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) {
+ bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\").");
+ return;
+ }
+ if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) {
+ bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set.");
+ return;
+ }
+ }
}
- List<EclipseNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
- List<AbstractMethodDeclaration> newMethods = new ArrayList<AbstractMethodDeclaration>();
- for (EclipseNode fieldNode : fieldNodes) {
- MethodDeclaration newMethod = makeSetterMethodForBuilder(builderType, fieldNode, annotationNode, builderInstance.fluent(), builderInstance.chain());
- if (newMethod != null) newMethods.add(newMethod);
+
+ generateBuilderFields(builderType, builderFields, ast);
+ if (addCleaning) {
+ FieldDeclaration cleanDecl = new FieldDeclaration(CLEAN_FIELD_NAME, 0, -1);
+ cleanDecl.declarationSourceEnd = -1;
+ cleanDecl.modifiers = ClassFileConstants.AccPrivate;
+ cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
+ injectFieldAndMarkGenerated(builderType, cleanDecl);
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
ConstructorDeclaration cd = HandleConstructor.createConstructor(
- AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), null,
- annotationNode, Collections.<Annotation>emptyList());
+ AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false,
+ annotationNode, Collections.<Annotation>emptyList());
if (cd != null) injectMethod(builderType, cd);
}
- for (AbstractMethodDeclaration newMethod : newMethods) injectMethod(builderType, newMethod);
+ for (BuilderFieldData bfd : builderFields) {
+ makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain);
+ }
+
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- MethodDeclaration md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, ast, thrownExceptions);
+ MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
if (md != null) injectMethod(builderType, md);
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ List<EclipseNode> fieldNodes = new ArrayList<EclipseNode>();
+ for (BuilderFieldData bfd : builderFields) {
+ fieldNodes.addAll(bfd.createdFields);
+ }
MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
if (md != null) injectMethod(builderType, md);
}
+ if (addCleaning) {
+ MethodDeclaration cleanMethod = generateCleanMethod(builderFields, builderType, ast);
+ if (cleanMethod != null) injectMethod(builderType, cleanMethod);
+ }
+
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
- MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams, ast);
+ MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+
+ if (toBuilder) switch (methodExists(toBuilderMethodName, tdParent, 0)) {
+ case EXISTS_BY_USER:
+ annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+ break;
+ case NOT_EXISTS:
+ TypeParameter[] tps = typeParams;
+ if (typeArgsForToBuilder != null) {
+ tps = new TypeParameter[typeArgsForToBuilder.size()];
+ for (int i = 0; i < tps.length; i++) {
+ tps[i] = new TypeParameter();
+ tps[i].name = typeArgsForToBuilder.get(i);
+ }
+ }
+ MethodDeclaration md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast);
+
if (md != null) injectMethod(tdParent, md);
}
}
- public MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
+ 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);
- out.selector = builderMethodName.toCharArray();
- out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
+ out.selector = methodName.toCharArray();
+ out.modifiers = ClassFileConstants.AccPublic;
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
- out.typeParameters = copyTypeParams(typeParams, source);
AllocationExpression invoke = new AllocationExpression();
invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
- out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)};
+
+ Expression receiver = invoke;
+ for (BuilderFieldData bfd : builderFields) {
+ char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray();
+ MessageSend ms = new MessageSend();
+ if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+ char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray();
+ FieldReference fr = new FieldReference(fieldName, 0);
+ fr.receiver = new ThisReference(0, 0);
+ ms.arguments = new Expression[] {fr};
+ } else {
+ String obtainName = bfd.obtainVia.method();
+ boolean obtainIsStatic = bfd.obtainVia.isStatic();
+ MessageSend obtainExpr = new MessageSend();
+ obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0);
+ obtainExpr.selector = obtainName.toCharArray();
+ if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)};
+ ms.arguments = new Expression[] {obtainExpr};
+ }
+ ms.receiver = receiver;
+ ms.selector = setterName;
+ receiver = ms;
+ }
+
+ out.statements = new Statement[] {new ReturnStatement(receiver, pS, pE)};
out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
+
}
- public MethodDeclaration generateBuildMethod(String name, char[] staticName, TypeReference returnType, List<char[]> fieldNames, EclipseNode type, ASTNode source, TypeReference[] thrownExceptions) {
- int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long) pS << 32 | pE;
+ private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) {
+ List<Statement> statements = new ArrayList<Statement>();
- MethodDeclaration out = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements);
+ }
+ }
- out.modifiers = ClassFileConstants.AccPublic;
- TypeDeclaration typeDecl = (TypeDeclaration) type.get();
- out.selector = name.toCharArray();
- out.thrownExceptions = copyTypes(thrownExceptions, source);
+ FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0);
+ thisUnclean.receiver = new ThisReference(0, 0);
+ statements.add(new Assignment(thisUnclean, new FalseLiteral(0, 0), 0));
+ MethodDeclaration decl = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ decl.selector = CLEAN_METHOD_NAME;
+ decl.modifiers = ClassFileConstants.AccPrivate;
+ decl.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ decl.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ decl.statements = statements.toArray(new Statement[0]);
+ decl.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return decl;
+ }
+
+ public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
- out.returnType = returnType;
+ List<Statement> statements = new ArrayList<Statement>();
+
+ if (addCleaning) {
+ FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0);
+ thisUnclean.receiver = new ThisReference(0, 0);
+ Expression notClean = new UnaryExpression(thisUnclean, OperatorIds.NOT);
+ MessageSend invokeClean = new MessageSend();
+ invokeClean.selector = CLEAN_METHOD_NAME;
+ statements.add(new IfStatement(notClean, invokeClean, 0, 0));
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name);
+ }
+ }
+
+ List<Expression> args = new ArrayList<Expression>();
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.nameOfSetFlag != null) {
+ MessageSend inv = new MessageSend();
+ inv.sourceStart = source.sourceStart;
+ inv.sourceEnd = source.sourceEnd;
+ inv.receiver = new SingleNameReference(((TypeDeclaration) tdParent.get()).name, 0L);
+ inv.selector = bfd.nameOfDefaultProvider;
+ inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters);
+
+ args.add(new ConditionalExpression(
+ new SingleNameReference(bfd.nameOfSetFlag, 0L),
+ new SingleNameReference(bfd.name, 0L),
+ inv));
+ } else {
+ args.add(new SingleNameReference(bfd.name, 0L));
+ }
+ }
- List<Expression> assigns = new ArrayList<Expression>();
- for (char[] fieldName : fieldNames) {
- SingleNameReference nameRef = new SingleNameReference(fieldName, p);
- assigns.add(nameRef);
+ if (addCleaning) {
+ FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0);
+ thisUnclean.receiver = new ThisReference(0, 0);
+ statements.add(new Assignment(thisUnclean, new TrueLiteral(0, 0), 0));
}
- Statement statement;
+ out.modifiers = ClassFileConstants.AccPublic;
+ out.selector = name.toCharArray();
+ out.thrownExceptions = copyTypes(thrownExceptions);
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = returnType;
if (staticName == null) {
AllocationExpression allocationStatement = new AllocationExpression();
- allocationStatement.type = copyType(out.returnType, source);
- allocationStatement.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
- statement = new ReturnStatement(allocationStatement, (int)(p >> 32), (int)p);
+ allocationStatement.type = copyType(out.returnType);
+ allocationStatement.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]);
+ statements.add(new ReturnStatement(allocationStatement, 0, 0));
} else {
MessageSend invoke = new MessageSend();
invoke.selector = staticName;
- invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), p);
- TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters;
- if (tps != null) {
- TypeReference[] trs = new TypeReference[tps.length];
- for (int i = 0; i < trs.length; i++) {
- trs[i] = new SingleTypeReference(tps[i].name, p);
- }
- invoke.typeArguments = trs;
- }
- invoke.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
+ if (isStatic)
+ invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0);
+ else
+ invoke.receiver = new QualifiedThisReference(new SingleTypeReference(type.up().getName().toCharArray(), 0) , 0, 0);
+
+ invoke.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters);
+ invoke.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]);
if (returnType instanceof SingleTypeReference && Arrays.equals(TypeConstants.VOID, ((SingleTypeReference) returnType).token)) {
- statement = invoke;
+ statements.add(invoke);
} else {
- statement = new ReturnStatement(invoke, (int)(p >> 32), (int)p);
+ statements.add(new ReturnStatement(invoke, 0, 0));
}
}
+ out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[statements.size()]);
+ out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return out;
+ }
+
+ private TypeReference[] typeParameterNames(TypeParameter[] typeParameters) {
+ if (typeParameters == null) return null;
- out.statements = new Statement[] { statement };
+ TypeReference[] trs = new TypeReference[typeParameters.length];
+ for (int i = 0; i < trs.length; i++) {
+ trs[i] = new SingleTypeReference(typeParameters[i].name, 0);
+ }
+ return trs;
+ }
+
+ public MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult);
+ out.typeParameters = copyTypeParams(typeParameters, source);
+ out.selector = methodName;
+ out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ out.returnType = copyType(fd.type, source);
+ out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)};
+ fd.initialization = null;
- out.traverse(new SetGeneratedByVisitor(source), typeDecl.scope);
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope);
return out;
}
- public List<EclipseNode> addFieldsToBuilder(EclipseNode builderType, List<char[]> namesOfParameters, List<TypeReference> typesOfParameters, ASTNode source) {
- int len = namesOfParameters.size();
- TypeDeclaration td = (TypeDeclaration) builderType.get();
- FieldDeclaration[] existing = td.fields;
- if (existing == null) existing = new FieldDeclaration[0];
-
- List<EclipseNode> out = new ArrayList<EclipseNode>();
-
- top:
- for (int i = len - 1; i >= 0; i--) {
- char[] name = namesOfParameters.get(i);
- for (FieldDeclaration exists : existing) {
- if (Arrays.equals(exists.name, name)) {
- out.add(builderType.getNodeFor(exists));
- continue top;
- }
- }
- TypeReference fieldReference = copyType(typesOfParameters.get(i), source);
- FieldDeclaration newField = new FieldDeclaration(name, 0, 0);
- newField.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
- newField.modifiers = ClassFileConstants.AccPrivate;
- newField.type = fieldReference;
- out.add(injectField(builderType, newField));
- }
+ public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
- Collections.reverse(out);
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ out.selector = builderMethodName.toCharArray();
+ out.modifiers = ClassFileConstants.AccPublic;
+ if (isStatic) out.modifiers |= ClassFileConstants.AccStatic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+ out.typeParameters = copyTypeParams(typeParams, source);
+ AllocationExpression invoke = new AllocationExpression();
+ invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+ out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)};
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
return out;
}
+ public void generateBuilderFields(EclipseNode builderType, List<BuilderFieldData> builderFields, ASTNode source) {
+ List<EclipseNode> existing = new ArrayList<EclipseNode>();
+ for (EclipseNode child : builderType.down()) {
+ if (child.getKind() == Kind.FIELD) existing.add(child);
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType));
+ } else {
+ EclipseNode field = null, setFlag = null;
+ for (EclipseNode exists : existing) {
+ char[] n = ((FieldDeclaration) exists.get()).name;
+ if (Arrays.equals(n, bfd.name)) field = exists;
+ if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) setFlag = exists;
+ }
+
+ if (field == null) {
+ FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0);
+ fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fd.modifiers = ClassFileConstants.AccPrivate;
+ fd.type = copyType(bfd.type);
+ fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
+ field = injectFieldAndMarkGenerated(builderType, fd);
+ }
+ if (setFlag == null && bfd.nameOfSetFlag != null) {
+ FieldDeclaration fd = new FieldDeclaration(bfd.nameOfSetFlag, 0, 0);
+ fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fd.modifiers = ClassFileConstants.AccPrivate;
+ fd.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
+ fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
+ injectFieldAndMarkGenerated(builderType, fd);
+ }
+ bfd.createdFields.add(field);
+ }
+ }
+ }
+
private static final AbstractMethodDeclaration[] EMPTY = {};
- public MethodDeclaration makeSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, EclipseNode sourceNode, boolean fluent, boolean chain) {
+ public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain) {
+ boolean deprecate = isFieldDeprecated(bfd.originalFieldNode);
+ if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
+ makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain);
+ } else {
+ bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, fluent, chain);
+ }
+ }
+
+ private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain) {
TypeDeclaration td = (TypeDeclaration) builderType.get();
AbstractMethodDeclaration[] existing = td.methods;
if (existing == null) existing = EMPTY;
@@ -348,14 +736,14 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
for (int i = 0; i < len; i++) {
if (!(existing[i] instanceof MethodDeclaration)) continue;
char[] existingName = existing[i].selector;
- if (Arrays.equals(name, existingName)) return null;
+ if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) return;
}
- boolean isBoolean = isBoolean(fd.type);
- String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean);
+ String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
- return HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
- sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, chain, ClassFileConstants.AccPublic,
+ sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ injectMethod(builderType, setter);
}
public EclipseNode findInnerClass(EclipseNode parent, String name) {
@@ -368,14 +756,84 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return null;
}
- public EclipseNode makeBuilderClass(EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) {
+ public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) {
TypeDeclaration parent = (TypeDeclaration) tdParent.get();
TypeDeclaration builder = new TypeDeclaration(parent.compilationResult);
builder.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
- builder.modifiers |= ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
+ builder.modifiers |= ClassFileConstants.AccPublic;
+ if (isStatic) builder.modifiers |= ClassFileConstants.AccStatic;
builder.typeParameters = copyTypeParams(typeParams, source);
builder.name = builderClassName.toCharArray();
builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
return injectType(tdParent, builder);
}
+
+ private void addObtainVia(BuilderFieldData bfd, EclipseNode node) {
+ for (EclipseNode child : node.down()) {
+ if (!annotationTypeMatches(ObtainVia.class, child)) continue;
+ AnnotationValues<ObtainVia> ann = createAnnotation(ObtainVia.class, child);
+ bfd.obtainVia = ann.getInstance();
+ bfd.obtainViaNode = child;
+ return;
+ }
+ }
+
+ /**
+ * Returns the explicitly requested singular annotation on this node (field
+ * or parameter), or null if there's no {@code @Singular} annotation on it.
+ *
+ * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation.
+ */
+ private SingularData getSingularData(EclipseNode node, ASTNode source) {
+ for (EclipseNode child : node.down()) {
+ if (!annotationTypeMatches(Singular.class, child)) continue;
+ char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name;
+ AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
+ String explicitSingular = ann.getInstance().value();
+ if (explicitSingular.isEmpty()) {
+ if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
+ node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ explicitSingular = new String(pluralName);
+ } else {
+ explicitSingular = autoSingularize(new String(pluralName));
+ if (explicitSingular == null) {
+ node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
+ explicitSingular = new String(pluralName);
+ }
+ }
+ }
+ char[] singularName = explicitSingular.toCharArray();
+
+ TypeReference type = ((AbstractVariableDeclaration) node.get()).type;
+ TypeReference[] typeArgs = null;
+ String typeName;
+ if (type instanceof ParameterizedSingleTypeReference) {
+ typeArgs = ((ParameterizedSingleTypeReference) type).typeArguments;
+ typeName = new String(((ParameterizedSingleTypeReference) type).token);
+ } else if (type instanceof ParameterizedQualifiedTypeReference) {
+ TypeReference[][] tr = ((ParameterizedQualifiedTypeReference) type).typeArguments;
+ if (tr != null) typeArgs = tr[tr.length - 1];
+ char[][] tokens = ((ParameterizedQualifiedTypeReference) type).tokens;
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < tokens.length; i++) {
+ if (i > 0) sb.append(".");
+ sb.append(tokens[i]);
+ }
+ typeName = sb.toString();
+ } else {
+ typeName = type.toString();
+ }
+
+ String targetFqn = EclipseSingularsRecipes.get().toQualified(typeName);
+ EclipseSingularizer singularizer = EclipseSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + typeName + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source);
+ }
+
+ return null;
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
new file mode 100644
index 00000000..be2b986d
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017-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
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers;
+
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.Builder;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+
+@ProviderFor(EclipseAnnotationHandler.class)
+@HandlerPriority(-1025) //HandleBuilder's level, minus one.
+public class HandleBuilderDefault extends EclipseAnnotationHandler<Builder.Default> {
+ @Override public void handle(AnnotationValues<Builder.Default> annotation, Annotation ast, EclipseNode annotationNode) {
+ EclipseNode annotatedField = annotationNode.up();
+ if (annotatedField.getKind() != Kind.FIELD) return;
+ EclipseNode classWithAnnotatedField = annotatedField.up();
+ if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)) {
+ annotationNode.addWarning("@Builder.Default requires @Builder on the class for it to mean anything.");
+ }
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java
index b72d000f..62e2c18c 100644
--- a/src/core/lombok/eclipse/handlers/HandleConstructor.java
+++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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
@@ -29,10 +29,12 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.ConfigurationKeys;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
@@ -40,21 +42,28 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.experimental.Builder;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
+import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.CharLiteral;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.DoubleLiteral;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.FloatLiteral;
+import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
+import org.eclipse.jdt.internal.compiler.ast.LongLiteral;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
@@ -63,12 +72,16 @@ import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.mangosdk.spi.ProviderFor;
public class HandleConstructor {
@ProviderFor(EclipseAnnotationHandler.class)
public static class HandleNoArgsConstructor extends EclipseAnnotationHandler<NoArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<NoArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.NO_ARGS_CONSTRUCTOR_FLAG_USAGE, "@NoArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -78,16 +91,20 @@ public class HandleConstructor {
AccessLevel level = ann.access();
String staticName = ann.staticName();
if (level == AccessLevel.NONE) return;
- List<EclipseNode> fields = new ArrayList<EclipseNode>();
- List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode);
+ boolean force = ann.force();
+
+ List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList();
+ List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode);
- new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode);
+ handleConstructor.generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode);
}
}
@ProviderFor(EclipseAnnotationHandler.class)
public static class HandleRequiredArgsConstructor extends EclipseAnnotationHandler<RequiredArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE, "@RequiredArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -97,43 +114,51 @@ public class HandleConstructor {
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- Boolean suppressConstructorProperties = null;
if (annotation.isExplicit("suppressConstructorProperties")) {
- @SuppressWarnings("deprecation")
- boolean suppress = ann.suppressConstructorProperties();
- suppressConstructorProperties = suppress;
+ annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'.");
}
- List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode);
+ List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor", annotationNode);
- new HandleConstructor().generateConstructor(
- typeNode, level, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO,
- suppressConstructorProperties, onConstructor, annotationNode);
+ handleConstructor.generateConstructor(
+ typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO,
+ onConstructor, annotationNode);
}
}
private static List<EclipseNode> findRequiredFields(EclipseNode typeNode) {
+ return findFields(typeNode, true);
+ }
+
+ private static List<EclipseNode> findFinalFields(EclipseNode typeNode) {
+ return findFields(typeNode, false);
+ }
+
+ private static List<EclipseNode> findFields(EclipseNode typeNode, boolean nullMarked) {
List<EclipseNode> fields = new ArrayList<EclipseNode>();
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
if (!filterField(fieldDecl)) continue;
boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0;
- boolean isNonNull = findAnnotations(fieldDecl, NON_NULL_PATTERN).length != 0;
+ boolean isNonNull = nullMarked && findAnnotations(fieldDecl, NON_NULL_PATTERN).length != 0;
if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child);
}
return fields;
}
static List<EclipseNode> findAllFields(EclipseNode typeNode) {
+ return findAllFields(typeNode, false);
+ }
+
+ static List<EclipseNode> findAllFields(EclipseNode typeNode, boolean evenFinalInitialized) {
List<EclipseNode> fields = new ArrayList<EclipseNode>();
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
if (!filterField(fieldDecl)) continue;
- // Skip initialized final fields.
- if (((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) continue;
+ if (!evenFinalInitialized && ((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) continue;
fields.add(child);
}
@@ -142,6 +167,8 @@ public class HandleConstructor {
@ProviderFor(EclipseAnnotationHandler.class)
public static class HandleAllArgsConstructor extends EclipseAnnotationHandler<AllArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<AllArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.ALL_ARGS_CONSTRUCTOR_FLAG_USAGE, "@AllArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -151,18 +178,15 @@ public class HandleConstructor {
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- Boolean suppressConstructorProperties = null;
if (annotation.isExplicit("suppressConstructorProperties")) {
- @SuppressWarnings("deprecation")
- boolean suppress = ann.suppressConstructorProperties();
- suppressConstructorProperties = suppress;
+ annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'.");
}
- List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode);
+ List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor", annotationNode);
- new HandleConstructor().generateConstructor(
- typeNode, level, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO,
- suppressConstructorProperties, onConstructor, annotationNode);
+ handleConstructor.generateConstructor(
+ typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO,
+ onConstructor, annotationNode);
}
}
@@ -184,14 +208,14 @@ public class HandleConstructor {
EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode) {
- generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
+ generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode);
}
public void generateAllArgsConstructor(
EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode) {
- generateConstructor(typeNode, level, findAllFields(typeNode), staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
+ generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode);
}
public enum SkipIfConstructorExists {
@@ -199,8 +223,8 @@ public class HandleConstructor {
}
public void generateConstructor(
- EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists,
- Boolean suppressConstructorProperties, List<Annotation> onConstructor, EclipseNode sourceNode) {
+ EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
+ List<Annotation> onConstructor, EclipseNode sourceNode) {
ASTNode source = sourceNode.get();
boolean staticConstrRequired = staticName != null && !staticName.equals("");
@@ -210,8 +234,8 @@ public class HandleConstructor {
for (EclipseNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
boolean skipGeneration = (annotationTypeMatches(NoArgsConstructor.class, child) ||
- annotationTypeMatches(AllArgsConstructor.class, child) ||
- annotationTypeMatches(RequiredArgsConstructor.class, child));
+ annotationTypeMatches(AllArgsConstructor.class, child) ||
+ annotationTypeMatches(RequiredArgsConstructor.class, child));
if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
skipGeneration = annotationTypeMatches(Builder.class, child);
@@ -224,8 +248,8 @@ public class HandleConstructor {
// the 'staticName' parameter of the @XArgsConstructor you've stuck on your type.
// We should warn that we're ignoring @Data's 'staticConstructor' param.
typeNode.addWarning(
- "Ignoring static constructor name: explicit @XxxArgsConstructor annotation present; its `staticName` parameter will be used.",
- source.sourceStart, source.sourceEnd);
+ "Ignoring static constructor name: explicit @XxxArgsConstructor annotation present; its `staticName` parameter will be used.",
+ source.sourceStart, source.sourceEnd);
}
return;
}
@@ -234,11 +258,11 @@ public class HandleConstructor {
}
ConstructorDeclaration constr = createConstructor(
- staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields,
- suppressConstructorProperties, sourceNode, onConstructor);
+ staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault,
+ sourceNode, onConstructor);
injectMethod(typeNode, constr);
if (staticConstrRequired) {
- MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, fields, source);
+ MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.<EclipseNode>emptyList() : fields, source);
injectMethod(typeNode, staticConstr);
}
}
@@ -248,7 +272,7 @@ public class HandleConstructor {
if (fields.isEmpty()) return null;
int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
long[] poss = new long[3];
Arrays.fill(poss, p);
QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(JAVA_BEANS_CONSTRUCTORPROPERTIES, poss);
@@ -275,28 +299,28 @@ public class HandleConstructor {
return new Annotation[] { ann };
}
- public static ConstructorDeclaration createConstructor(
- AccessLevel level, EclipseNode type, Collection<EclipseNode> fields,
- Boolean suppressConstructorProperties, EclipseNode sourceNode, List<Annotation> onConstructor) {
+ @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(
+ AccessLevel level, EclipseNode type, Collection<EclipseNode> fields, boolean allToDefault,
+ EclipseNode sourceNode, List<Annotation> onConstructor) {
ASTNode source = sourceNode.get();
- TypeDeclaration typeDeclaration = ((TypeDeclaration)type.get());
- long p = (long)source.sourceStart << 32 | source.sourceEnd;
+ TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get());
+ long p = (long) source.sourceStart << 32 | source.sourceEnd;
- boolean isEnum = (((TypeDeclaration)type.get()).modifiers & ClassFileConstants.AccEnum) != 0;
+ boolean isEnum = (((TypeDeclaration) type.get()).modifiers & ClassFileConstants.AccEnum) != 0;
if (isEnum) level = AccessLevel.PRIVATE;
- if (suppressConstructorProperties == null) {
- if (fields.isEmpty()) {
- suppressConstructorProperties = false;
- } else {
- suppressConstructorProperties = Boolean.TRUE.equals(type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
- }
+ boolean addConstructorProperties;
+ if (fields.isEmpty()) {
+ addConstructorProperties = false;
+ } else {
+ Boolean v = type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES);
+ addConstructorProperties = v != null ? v.booleanValue() :
+ Boolean.FALSE.equals(type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
}
- ConstructorDeclaration constructor = new ConstructorDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
constructor.modifiers = toEclipseModifier(level);
constructor.selector = typeDeclaration.name;
@@ -319,23 +343,27 @@ public class HandleConstructor {
char[] rawName = field.name;
char[] fieldName = removePrefixFromField(fieldNode);
FieldReference thisX = new FieldReference(rawName, p);
- thisX.receiver = new ThisReference((int)(p >> 32), (int)p);
+ int s = (int) (p >> 32);
+ int e = (int) p;
+ thisX.receiver = new ThisReference(s, e);
+
+ Expression assignmentExpr = allToDefault ? getDefaultExpr(field.type, s, e) : new SingleNameReference(fieldName, p);
- SingleNameReference assignmentNameRef = new SingleNameReference(fieldName, p);
- Assignment assignment = new Assignment(thisX, assignmentNameRef, (int)p);
- assignment.sourceStart = (int)(p >> 32); assignment.sourceEnd = assignment.statementEnd = (int)(p >> 32);
+ Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p);
+ assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32);
assigns.add(assignment);
- long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd;
- Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL);
- Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
- Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
- if (nonNulls.length != 0) {
- Statement nullCheck = generateNullCheck(field, sourceNode);
- if (nullCheck != null) nullChecks.add(nullCheck);
+ if (!allToDefault) {
+ long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd;
+ Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL);
+ Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
+ Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
+ if (nonNulls.length != 0) {
+ Statement nullCheck = generateNullCheck(parameter, sourceNode);
+ if (nullCheck != null) nullChecks.add(nullCheck);
+ }
+ parameter.annotations = copyAnnotations(source, nonNulls, nullables);
+ params.add(parameter);
}
- Annotation[] copiedAnnotations = copyAnnotations(source, nonNulls, nullables);
- if (copiedAnnotations.length != 0) parameter.annotations = copiedAnnotations;
- params.add(parameter);
}
nullChecks.addAll(assigns);
@@ -344,20 +372,34 @@ public class HandleConstructor {
/* Generate annotations that must be put on the generated method, and attach them. */ {
Annotation[] constructorProperties = null;
- if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(type)) {
+ if (!allToDefault && addConstructorProperties && !isLocalType(type)) {
constructorProperties = createConstructorProperties(source, fields);
}
- Annotation[] copiedAnnotations = copyAnnotations(source,
- onConstructor.toArray(new Annotation[0]),
- constructorProperties);
- if (copiedAnnotations.length != 0) constructor.annotations = copiedAnnotations;
+ constructor.annotations = copyAnnotations(source,
+ onConstructor.toArray(new Annotation[0]),
+ constructorProperties);
}
constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
return constructor;
}
+ private static Expression getDefaultExpr(TypeReference type, int s, int e) {
+ boolean array = type instanceof ArrayTypeReference;
+ if (array) return new NullLiteral(s, e);
+ char[] lastToken = type.getLastToken();
+ if (Arrays.equals(TypeConstants.BOOLEAN, lastToken)) return new FalseLiteral(s, e);
+ if (Arrays.equals(TypeConstants.CHAR, lastToken)) return new CharLiteral(new char[] {'\'', '\\', '0', '\''}, s, e);
+ if (Arrays.equals(TypeConstants.BYTE, lastToken) || Arrays.equals(TypeConstants.SHORT, lastToken) ||
+ Arrays.equals(TypeConstants.INT, lastToken)) return IntLiteral.buildIntLiteral(new char[] {'0'}, s, e);
+ if (Arrays.equals(TypeConstants.LONG, lastToken)) return LongLiteral.buildLongLiteral(new char[] {'0', 'L'}, s, e);
+ if (Arrays.equals(TypeConstants.FLOAT, lastToken)) return new FloatLiteral(new char[] {'0', 'F'}, s, e);
+ if (Arrays.equals(TypeConstants.DOUBLE, lastToken)) return new DoubleLiteral(new char[] {'0', 'D'}, s, e);
+
+ return new NullLiteral(s, e);
+ }
+
public static boolean isLocalType(EclipseNode type) {
Kind kind = type.up().getKind();
if (kind == Kind.COMPILATION_UNIT) return false;
@@ -367,10 +409,9 @@ public class HandleConstructor {
public MethodDeclaration createStaticConstructor(AccessLevel level, String name, EclipseNode type, Collection<EclipseNode> fields, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
- MethodDeclaration constructor = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration constructor = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
constructor.modifiers = toEclipseModifier(level) | ClassFileConstants.AccStatic;
TypeDeclaration typeDecl = (TypeDeclaration) type.get();
@@ -378,7 +419,7 @@ public class HandleConstructor {
constructor.annotations = null;
constructor.selector = name.toCharArray();
constructor.thrownExceptions = null;
- constructor.typeParameters = copyTypeParams(((TypeDeclaration)type.get()).typeParameters, source);
+ constructor.typeParameters = copyTypeParams(((TypeDeclaration) type.get()).typeParameters, source);
constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
@@ -391,20 +432,18 @@ public class HandleConstructor {
for (EclipseNode fieldNode : fields) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
- long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd;
+ long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd;
SingleNameReference nameRef = new SingleNameReference(field.name, fieldPos);
assigns.add(nameRef);
Argument parameter = new Argument(field.name, fieldPos, copyType(field.type, source), Modifier.FINAL);
-
- Annotation[] copiedAnnotations = copyAnnotations(source, findAnnotations(field, NON_NULL_PATTERN), findAnnotations(field, NULLABLE_PATTERN));
- if (copiedAnnotations.length != 0) parameter.annotations = copiedAnnotations;
+ parameter.annotations = copyAnnotations(source, findAnnotations(field, NON_NULL_PATTERN), findAnnotations(field, NULLABLE_PATTERN));
params.add(parameter);
}
statement.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]);
- constructor.statements = new Statement[] { new ReturnStatement(statement, (int)(p >> 32), (int)p) };
+ constructor.statements = new Statement[] { new ReturnStatement(statement, (int) (p >> 32), (int)p) };
constructor.traverse(new SetGeneratedByVisitor(source), typeDecl.scope);
return constructor;
diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java
index 0ff65a47..025ceefd 100644
--- a/src/core/lombok/eclipse/handlers/HandleData.java
+++ b/src/core/lombok/eclipse/handlers/HandleData.java
@@ -43,6 +43,12 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleData extends EclipseAnnotationHandler<Data> {
+ private HandleGetter handleGetter = new HandleGetter();
+ private HandleSetter handleSetter = new HandleSetter();
+ private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode();
+ private HandleToString handleToString = new HandleToString();
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<Data> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data");
@@ -66,11 +72,11 @@ public class HandleData extends EclipseAnnotationHandler<Data> {
//for whatever reason, though you can find callers of that one by focusing on the class name itself
//and hitting 'find callers'.
- new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
- new HandleToString().generateToStringForType(typeNode, annotationNode);
- new HandleConstructor().generateRequiredArgsConstructor(
+ handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
+ handleSetter.generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
+ handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode);
+ handleToString.generateToStringForType(typeNode, annotationNode);
+ handleConstructor.generateRequiredArgsConstructor(
typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES,
Collections.<Annotation>emptyList(), annotationNode);
}
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index e1b02051..75339f7c 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -39,6 +39,7 @@ import lombok.EqualsAndHashCode;
import lombok.core.AST.Kind;
import lombok.core.handlers.HandlerUtil;
import lombok.core.AnnotationValues;
+import lombok.core.configuration.CallSuperType;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
@@ -66,6 +67,7 @@ import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
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;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
@@ -116,7 +118,10 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
return;
}
- generateMethods(typeNode, errorNode, null, null, null, false, FieldAccess.GETTER, new ArrayList<Annotation>());
+ Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
+ FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
+
+ generateMethods(typeNode, errorNode, null, null, null, false, access, new ArrayList<Annotation>());
}
@Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, EclipseNode annotationNode) {
@@ -127,7 +132,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
List<String> includes = Arrays.asList(ann.of());
EclipseNode typeNode = annotationNode.up();
- List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam=", annotationNode);
+ List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
checkForBogusFieldNames(typeNode, annotation);
Boolean callSuper = ann.callSuper();
@@ -185,8 +190,23 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
return;
}
- if (!isDirectDescendantOfObject && !callSuper && implicitCallSuper) {
- errorNode.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.");
+ if (implicitCallSuper && !isDirectDescendantOfObject) {
+ CallSuperType cst = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_CALL_SUPER);
+ if (cst == null) cst = CallSuperType.WARN;
+
+ switch (cst) {
+ default:
+ case WARN:
+ errorNode.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.");
+ callSuper = false;
+ break;
+ case SKIP:
+ callSuper = false;
+ break;
+ case CALL:
+ callSuper = true;
+ break;
+ }
}
List<EclipseNode> nodesForEquality = new ArrayList<EclipseNode>();
@@ -279,7 +299,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
/* final int PRIME = X; */ {
/* Without fields, PRIME isn't used, and that would trigger a 'local variable not used' warning. */
- if (!isEmpty || callSuper) {
+ if (!isEmpty) {
LocalDeclaration primeDecl = new LocalDeclaration(PRIME, pS, pE);
setGeneratedBy(primeDecl, source);
primeDecl.modifiers |= Modifier.FINAL;
@@ -291,26 +311,30 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
}
- /* int result = 1; */ {
- LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE);
+ /*int result = ... */{
+ LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE);
setGeneratedBy(resultDecl, source);
- resultDecl.initialization = makeIntLiteral("1".toCharArray(), source);
+ final Expression init;
+ if (callSuper) {
+ /* ... super.hashCode(); */
+ MessageSend callToSuper = new MessageSend();
+ setGeneratedBy(callToSuper, source);
+ callToSuper.sourceStart = pS; callToSuper.sourceEnd = pE;
+ callToSuper.receiver = new SuperReference(pS, pE);
+ setGeneratedBy(callToSuper.receiver, source);
+ callToSuper.selector = "hashCode".toCharArray();
+ init = callToSuper;
+ } else {
+ /* ... 1; */
+ init = makeIntLiteral("1".toCharArray(), source);
+ }
+ resultDecl.initialization = init;
resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
resultDecl.type.sourceStart = pS; resultDecl.type.sourceEnd = pE;
setGeneratedBy(resultDecl.type, source);
statements.add(resultDecl);
}
- if (callSuper) {
- MessageSend callToSuper = new MessageSend();
- setGeneratedBy(callToSuper, source);
- callToSuper.sourceStart = pS; callToSuper.sourceEnd = pE;
- callToSuper.receiver = new SuperReference(pS, pE);
- setGeneratedBy(callToSuper.receiver, source);
- callToSuper.selector = "hashCode".toCharArray();
- statements.add(createResultCalculation(source, callToSuper));
- }
-
for (EclipseNode field : fields) {
TypeReference fType = getFieldType(field, fieldAccess);
char[] dollarFieldName = ("$" + field.getName()).toCharArray();
@@ -358,7 +382,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
statements.add(createResultCalculation(source, fieldAccessor));
} else /* objects */ {
/* final java.lang.Object $fieldName = this.fieldName; */
- /* $fieldName == null ? 0 : $fieldName.hashCode() */
+ /* $fieldName == null ? NULL_PRIME : $fieldName.hashCode() */
statements.add(createLocalDeclaration(source, dollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), fieldAccessor));
SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p);
@@ -375,8 +399,8 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
setGeneratedBy(nullLiteral, source);
EqualExpression objIsNull = new EqualExpression(copy2, nullLiteral, OperatorIds.EQUAL_EQUAL);
setGeneratedBy(objIsNull, source);
- IntLiteral int0 = makeIntLiteral("0".toCharArray(), source);
- ConditionalExpression nullOrHashCode = new ConditionalExpression(objIsNull, int0, hashCodeCall);
+ IntLiteral intMagic = makeIntLiteral(String.valueOf(HandlerUtil.primeForNull()).toCharArray(), source);
+ ConditionalExpression nullOrHashCode = new ConditionalExpression(objIsNull, intMagic, hashCodeCall);
nullOrHashCode.sourceStart = pS; nullOrHashCode.sourceEnd = pE;
setGeneratedBy(nullOrHashCode, source);
statements.add(createResultCalculation(source, nullOrHashCode));
@@ -442,17 +466,44 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
return assignment;
}
- public TypeReference createTypeReference(EclipseNode type, long p) {
+ /**
+ * @param type Type to 'copy' into a typeref
+ * @param p position
+ * @param addWildcards If false, all generics are cut off. If true, replaces all genericparams with a ?.
+ * @return
+ */
+ public TypeReference createTypeReference(EclipseNode type, long p, ASTNode source, boolean addWildcards) {
+ int pS = source.sourceStart; int pE = source.sourceEnd;
List<String> list = new ArrayList<String>();
+ List<Integer> genericsCount = addWildcards ? new ArrayList<Integer>() : null;
+
list.add(type.getName());
+ if (addWildcards) genericsCount.add(arraySizeOf(((TypeDeclaration) type.get()).typeParameters));
+ boolean staticContext = (((TypeDeclaration) type.get()).modifiers & Modifier.STATIC) != 0;
EclipseNode tNode = type.up();
+
while (tNode != null && tNode.getKind() == Kind.TYPE) {
list.add(tNode.getName());
+ if (addWildcards) genericsCount.add(staticContext ? 0 : arraySizeOf(((TypeDeclaration) tNode.get()).typeParameters));
+ if (!staticContext) staticContext = (((TypeDeclaration) tNode.get()).modifiers & Modifier.STATIC) != 0;
tNode = tNode.up();
}
Collections.reverse(list);
+ if (addWildcards) Collections.reverse(genericsCount);
+
+ if (list.size() == 1) {
+ if (!addWildcards || genericsCount.get(0) == 0) {
+ return new SingleTypeReference(list.get(0).toCharArray(), p);
+ } else {
+ return new ParameterizedSingleTypeReference(list.get(0).toCharArray(), wildcardify(pS, pE, source, genericsCount.get(0)), 0, p);
+ }
+ }
+
+ if (addWildcards) {
+ addWildcards = false;
+ for (int i : genericsCount) if (i > 0) addWildcards = true;
+ }
- if (list.size() == 1) return new SingleTypeReference(list.get(0).toCharArray(), p);
long[] ps = new long[list.size()];
char[][] tokens = new char[list.size()][];
for (int i = 0; i < list.size(); i++) {
@@ -460,13 +511,31 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
tokens[i] = list.get(i).toCharArray();
}
- return new QualifiedTypeReference(tokens, ps);
+ if (!addWildcards) return new QualifiedTypeReference(tokens, ps);
+ TypeReference[][] typeArgs2 = new TypeReference[tokens.length][];
+ for (int i = 0; i < tokens.length; i++) typeArgs2[i] = wildcardify(pS, pE, source, genericsCount.get(i));
+ return new ParameterizedQualifiedTypeReference(tokens, typeArgs2, 0, ps);
+ }
+
+ private TypeReference[] wildcardify(int pS, int pE, ASTNode source, int count) {
+ if (count == 0) return null;
+ TypeReference[] typeArgs = new TypeReference[count];
+ for (int i = 0; i < count; i++) {
+ typeArgs[i] = new Wildcard(Wildcard.UNBOUND);
+ typeArgs[i].sourceStart = pS; typeArgs[i].sourceEnd = pE;
+ setGeneratedBy(typeArgs[i], source);
+ }
+
+ return typeArgs;
+ }
+
+ private int arraySizeOf(Object[] arr) {
+ return arr == null ? 0 : arr.length;
}
public MethodDeclaration createEquals(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List<Annotation> onParam) {
int pS = source.sourceStart; int pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
- TypeDeclaration typeDecl = (TypeDeclaration)type.get();
MethodDeclaration method = new MethodDeclaration(
((CompilationUnitDeclaration) type.top().get()).compilationResult);
@@ -512,7 +581,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
SingleNameReference oRef = new SingleNameReference(new char[] { 'o' }, p);
setGeneratedBy(oRef, source);
- TypeReference typeReference = createTypeReference(type, p);
+ TypeReference typeReference = createTypeReference(type, p, source, false);
setGeneratedBy(typeReference, source);
InstanceOfExpression instanceOf = new InstanceOfExpression(oRef, typeReference);
@@ -535,33 +604,17 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
char[] otherName = "other".toCharArray();
- /* MyType<?> other = (MyType<?>) o; */ {
+ /* Outer.Inner.MyType<?> other = (Outer.Inner.MyType<?>) o; */ {
if (!fields.isEmpty() || needsCanEqual) {
LocalDeclaration other = new LocalDeclaration(otherName, pS, pE);
other.modifiers |= ClassFileConstants.AccFinal;
setGeneratedBy(other, source);
- char[] typeName = typeDecl.name;
- TypeReference targetType;
- if (typeDecl.typeParameters == null || typeDecl.typeParameters.length == 0) {
- targetType = new SingleTypeReference(typeName, p);
- setGeneratedBy(targetType, source);
- other.type = new SingleTypeReference(typeName, p);
- setGeneratedBy(other.type, source);
- } else {
- TypeReference[] typeArgs = new TypeReference[typeDecl.typeParameters.length];
- for (int i = 0; i < typeArgs.length; i++) {
- typeArgs[i] = new Wildcard(Wildcard.UNBOUND);
- typeArgs[i].sourceStart = pS; typeArgs[i].sourceEnd = pE;
- setGeneratedBy(typeArgs[i], source);
- }
- targetType = new ParameterizedSingleTypeReference(typeName, typeArgs, 0, p);
- setGeneratedBy(targetType, source);
- other.type = new ParameterizedSingleTypeReference(typeName, copyTypes(typeArgs, source), 0, p);
- setGeneratedBy(other.type, source);
- }
+ TypeReference targetType = createTypeReference(type, p, source, true);
+ setGeneratedBy(targetType, source);
+ other.type = createTypeReference(type, p, source, true);
+ setGeneratedBy(other.type, source);
NameReference oRef = new SingleNameReference(new char[] { 'o' }, p);
setGeneratedBy(oRef, source);
- other.annotations = createSuppressWarningsAll(source, null);
other.initialization = makeCastExpression(oRef, targetType, source);
statements.add(other);
}
@@ -645,7 +698,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
} else /* objects */ {
/* final java.lang.Object this$fieldName = this.fieldName; */
/* final java.lang.Object other$fieldName = other.fieldName; */
- /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false;; */
+ /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */
char[] thisDollarFieldName = ("this$" + field.getName()).toCharArray();
char[] otherDollarFieldName = ("other$" + field.getName()).toCharArray();
@@ -725,7 +778,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
public MethodDeclaration createCanEqual(EclipseNode type, ASTNode source, List<Annotation> onParam) {
- /* public boolean canEqual(final java.lang.Object other) {
+ /* protected boolean canEqual(final java.lang.Object other) {
* return other instanceof Outer.Inner.MyType;
* }
*/
@@ -757,7 +810,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
SingleNameReference otherRef = new SingleNameReference(otherName, p);
setGeneratedBy(otherRef, source);
- TypeReference typeReference = createTypeReference(type, p);
+ TypeReference typeReference = createTypeReference(type, p, source, false);
setGeneratedBy(typeReference, source);
InstanceOfExpression instanceOf = new InstanceOfExpression(otherRef, typeReference);
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
index 7d0702db..702713fe 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 The Project Lombok Authors.
+ * Copyright (C) 2012-2016 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
@@ -23,12 +23,17 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.Arrays;
+
import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
-import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseASTAdapter;
+import lombok.eclipse.EclipseASTVisitor;
import lombok.eclipse.EclipseNode;
import lombok.experimental.FieldDefaults;
import lombok.experimental.NonFinal;
@@ -37,16 +42,19 @@ import lombok.experimental.PackagePrivate;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.mangosdk.spi.ProviderFor;
/**
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
-@ProviderFor(EclipseAnnotationHandler.class)
+@ProviderFor(EclipseASTVisitor.class)
@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
-public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> {
+public class HandleFieldDefaults extends EclipseASTAdapter {
public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
if (hasAnnotation(FieldDefaults.class, typeNode)) {
@@ -89,43 +97,78 @@ public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults>
if (level != null && level != AccessLevel.NONE) {
if ((field.modifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected)) == 0) {
if (!hasAnnotation(PackagePrivate.class, fieldNode)) {
- field.modifiers |= EclipseHandlerUtil.toEclipseModifier(level);
+ if ((field.modifiers & ClassFileConstants.AccStatic) == 0) {
+ field.modifiers |= EclipseHandlerUtil.toEclipseModifier(level);
+ }
}
}
}
if (makeFinal && (field.modifiers & ClassFileConstants.AccFinal) == 0) {
if (!hasAnnotation(NonFinal.class, fieldNode)) {
- field.modifiers |= ClassFileConstants.AccFinal;
+ if ((field.modifiers & ClassFileConstants.AccStatic) == 0) {
+ field.modifiers |= ClassFileConstants.AccFinal;
+ }
}
}
fieldNode.rebuild();
}
- public void handle(AnnotationValues<FieldDefaults> annotation, Annotation ast, EclipseNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
-
- EclipseNode node = annotationNode.up();
- FieldDefaults instance = annotation.getInstance();
- AccessLevel level = instance.level();
- boolean makeFinal = instance.makeFinal();
+ private static final char[] FIELD_DEFAULTS = "FieldDefaults".toCharArray();
+
+ @Override public void visitType(EclipseNode typeNode, TypeDeclaration type) {
+ AnnotationValues<FieldDefaults> fieldDefaults = null;
+ EclipseNode source = typeNode;
- if (level == AccessLevel.NONE && !makeFinal) {
- annotationNode.addError("This does nothing; provide either level or makeFinal or both.");
- return;
+ boolean levelIsExplicit = false;
+ boolean makeFinalIsExplicit = false;
+ FieldDefaults fd = null;
+ for (EclipseNode jn : typeNode.down()) {
+ if (jn.getKind() != Kind.ANNOTATION) continue;
+ Annotation ann = (Annotation) jn.get();
+ TypeReference typeTree = ann.type;
+ if (typeTree == null) continue;
+ if (typeTree instanceof SingleTypeReference) {
+ char[] t = ((SingleTypeReference) typeTree).token;
+ if (!Arrays.equals(t, FIELD_DEFAULTS)) continue;
+ } else if (typeTree instanceof QualifiedTypeReference) {
+ char[][] t = ((QualifiedTypeReference) typeTree).tokens;
+ if (!Eclipse.nameEquals(t, "lombok.experimental.FieldDefaults")) continue;
+ } else {
+ continue;
+ }
+
+ if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue;
+
+ source = jn;
+ fieldDefaults = createAnnotation(FieldDefaults.class, jn);
+ levelIsExplicit = fieldDefaults.isExplicit("level");
+ makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal");
+
+ handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
+
+ fd = fieldDefaults.getInstance();
+ if (!levelIsExplicit && !makeFinalIsExplicit) {
+ jn.addError("This does nothing; provide either level or makeFinal or both.");
+ }
+
+ if (levelIsExplicit && fd.level() == AccessLevel.NONE) {
+ jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value.");
+ levelIsExplicit = false;
+ }
+ break;
}
- if (level == AccessLevel.PACKAGE) {
- annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
- }
+ if (fd == null && (type.modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0) return;
- if (!makeFinal && annotation.isExplicit("makeFinal")) {
- annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
- }
+ boolean defaultToPrivate = levelIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE));
+ boolean defaultToFinal = makeFinalIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE));
- if (node == null) return;
+ if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return;
+ AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null;
+ boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal;
- generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
+ generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java
index 031fff82..f417aca5 100644
--- a/src/core/lombok/eclipse/handlers/HandleGetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleGetter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 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
@@ -105,7 +105,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
return true;
}
- public boolean fieldQualifiesForGetterGeneration(EclipseNode field) {
+ public static boolean fieldQualifiesForGetterGeneration(EclipseNode field) {
if (field.getKind() != Kind.FIELD) return false;
FieldDeclaration fieldDecl = (FieldDeclaration) field.get();
return filterField(fieldDecl);
@@ -148,7 +148,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
if (node == null) return;
- List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod=", annotationNode);
+ List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod", annotationNode);
switch (node.getKind()) {
case FIELD:
@@ -183,6 +183,10 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
errorNode.addError("'lazy' requires the field to be private and final.");
return;
}
+ if ((field.modifiers & ClassFileConstants.AccTransient) != 0) {
+ errorNode.addError("'lazy' is not supported on transient fields.");
+ return;
+ }
if (field.initialization == null) {
errorNode.addError("'lazy' requires field initialization.");
return;
@@ -270,14 +274,12 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
- Annotation[] copiedAnnotations = copyAnnotations(source,
+ method.annotations = copyAnnotations(source,
onMethod.toArray(new Annotation[0]),
findAnnotations(field, NON_NULL_PATTERN),
findAnnotations(field, NULLABLE_PATTERN),
findDelegatesAndMarkAsHandled(fieldNode),
deprecated);
-
- if (copiedAnnotations.length != 0) method.annotations = copiedAnnotations;
}
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
diff --git a/src/core/lombok/eclipse/handlers/HandleHelper.java b/src/core/lombok/eclipse/handlers/HandleHelper.java
new file mode 100644
index 00000000..4f06bdfd
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleHelper.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015-2016 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers;
+
+import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Block;
+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.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.ConfigurationKeys;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.experimental.Helper;
+
+/**
+ * Handles the {@code lombok.Cleanup} annotation for eclipse.
+ */
+@ProviderFor(EclipseAnnotationHandler.class)
+public class HandleHelper extends EclipseAnnotationHandler<Helper> {
+ private Statement[] getStatementsFromAstNode(ASTNode node) {
+ if (node instanceof Block) return ((Block) node).statements;
+ if (node instanceof AbstractMethodDeclaration) return ((AbstractMethodDeclaration) node).statements;
+ if (node instanceof SwitchStatement) return ((SwitchStatement) node).statements;
+ return null;
+ }
+
+ private void setStatementsOfAstNode(ASTNode node, Statement[] statements) {
+ if (node instanceof Block) ((Block) node).statements = statements;
+ else if (node instanceof AbstractMethodDeclaration) ((AbstractMethodDeclaration) node).statements = statements;
+ else if (node instanceof SwitchStatement) ((SwitchStatement) node).statements = statements;
+ else throw new IllegalArgumentException("Can't set statements on node type: " + node.getClass());
+ }
+
+ @Override public void handle(AnnotationValues<Helper> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper");
+
+ EclipseNode annotatedType = annotationNode.up();
+ EclipseNode containingBlock = annotatedType == null ? null : annotatedType.directUp();
+ Statement[] origStatements = getStatementsFromAstNode(containingBlock == null ? null : containingBlock.get());
+
+ if (annotatedType == null || annotatedType.getKind() != Kind.TYPE || origStatements == null) {
+ annotationNode.addError("@Helper is legal only on method-local classes.");
+ return;
+ }
+
+ TypeDeclaration annotatedType_ = (TypeDeclaration) annotatedType.get();
+ int indexOfType = -1;
+ for (int i = 0; i < origStatements.length; i++) {
+ if (origStatements[i] == annotatedType_) {
+ indexOfType = i;
+ break;
+ }
+ }
+
+ final List<String> knownMethodNames = new ArrayList<String>();
+
+ for (AbstractMethodDeclaration methodOfHelper : annotatedType_.methods) {
+ if (!(methodOfHelper instanceof MethodDeclaration)) continue;
+ char[] name = methodOfHelper.selector;
+ if (name != null && name.length > 0 && name[0] != '<') knownMethodNames.add(new String(name));
+ }
+
+ Collections.sort(knownMethodNames);
+ final String[] knownMethodNames_ = knownMethodNames.toArray(new String[knownMethodNames.size()]);
+
+ final char[] helperName = new char[annotatedType_.name.length + 1];
+ final boolean[] helperUsed = new boolean[1];
+ helperName[0] = '$';
+ System.arraycopy(annotatedType_.name, 0, helperName, 1, helperName.length - 1);
+
+ ASTVisitor visitor = new ASTVisitor() {
+ @Override public boolean visit(MessageSend messageSend, BlockScope scope) {
+ if (messageSend.receiver instanceof ThisReference) {
+ if ((((ThisReference) messageSend.receiver).bits & ASTNode.IsImplicitThis) == 0) return true;
+ } else if (messageSend.receiver != null) return true;
+
+ char[] name = messageSend.selector;
+ if (name == null || name.length == 0 || name[0] == '<') return true;
+ String n = new String(name);
+ if (Arrays.binarySearch(knownMethodNames_, n) < 0) return true;
+ messageSend.receiver = new SingleNameReference(helperName, messageSend.nameSourcePosition);
+ helperUsed[0] = true;
+ return true;
+ }
+ };
+
+ for (int i = indexOfType + 1; i < origStatements.length; i++) {
+ origStatements[i].traverse(visitor, null);
+ }
+
+ if (!helperUsed[0]) {
+ annotationNode.addWarning("No methods of this helper class are ever used.");
+ return;
+ }
+
+ Statement[] newStatements = new Statement[origStatements.length + 1];
+ System.arraycopy(origStatements, 0, newStatements, 0, indexOfType + 1);
+ System.arraycopy(origStatements, indexOfType + 1, newStatements, indexOfType + 2, origStatements.length - indexOfType - 1);
+ LocalDeclaration decl = new LocalDeclaration(helperName, 0, 0);
+ decl.modifiers |= ClassFileConstants.AccFinal;
+ AllocationExpression alloc = new AllocationExpression();
+ alloc.type = new SingleTypeReference(annotatedType_.name, 0L);
+ decl.initialization = alloc;
+ decl.type = new SingleTypeReference(annotatedType_.name, 0L);
+ SetGeneratedByVisitor sgbvVisitor = new SetGeneratedByVisitor(annotationNode.get());
+ decl.traverse(sgbvVisitor, null);
+ newStatements[indexOfType + 1] = decl;
+ setStatementsOfAstNode(containingBlock.get(), newStatements);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleLog.java b/src/core/lombok/eclipse/handlers/HandleLog.java
index 830190a2..c49030d8 100644
--- a/src/core/lombok/eclipse/handlers/HandleLog.java
+++ b/src/core/lombok/eclipse/handlers/HandleLog.java
@@ -229,6 +229,17 @@ public class HandleLog {
}
}
+ /**
+ * Handles the {@link lombok.extern.jbosslog.JBossLog} annotation for Eclipse.
+ */
+ @ProviderFor(EclipseAnnotationHandler.class)
+ public static class HandleJBossLog extends EclipseAnnotationHandler<lombok.extern.jbosslog.JBossLog> {
+ @Override public void handle(AnnotationValues<lombok.extern.jbosslog.JBossLog> annotation, Annotation source, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JBOSSLOG_FLAG_USAGE, "@JBossLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log");
+ processAnnotation(LoggingFramework.JBOSSLOG, annotation, source, annotationNode, annotation.getInstance().topic());
+ }
+ }
+
enum LoggingFramework {
// private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(TargetType.class);
COMMONS("org.apache.commons.logging.Log", "org.apache.commons.logging.LogFactory", "getLog", "@CommonsLog"),
@@ -264,7 +275,9 @@ public class HandleLog {
// private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(TargetType.class);
XSLF4J("org.slf4j.ext.XLogger", "org.slf4j.ext.XLoggerFactory", "getXLogger", "@XSlf4j"),
-
+
+ // private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(TargetType.class);
+ JBOSSLOG("org.jboss.logging.Logger", "org.jboss.logging.Logger", "getLogger", "@JBossLog"),
;
private final String loggerTypeName;
diff --git a/src/core/lombok/eclipse/handlers/HandleNonNull.java b/src/core/lombok/eclipse/handlers/HandleNonNull.java
index d904de2f..d09993ed 100644
--- a/src/core/lombok/eclipse/handlers/HandleNonNull.java
+++ b/src/core/lombok/eclipse/handlers/HandleNonNull.java
@@ -91,7 +91,7 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> {
if (isGenerated(declaration)) return;
if (declaration.isAbstract()) {
- annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method.");
+ // This used to be a warning, but as @NonNull also has a documentary purpose, better to not warn about this. Since 1.16.7
return;
}
diff --git a/src/core/lombok/eclipse/handlers/HandlePrintAST.java b/src/core/lombok/eclipse/handlers/HandlePrintAST.java
index 0b61bc4d..234e29b8 100644
--- a/src/core/lombok/eclipse/handlers/HandlePrintAST.java
+++ b/src/core/lombok/eclipse/handlers/HandlePrintAST.java
@@ -59,7 +59,7 @@ public class HandlePrintAST extends EclipseAnnotationHandler<PrintAST> {
try {
stream.close();
} catch (Exception e) {
- Lombok.sneakyThrow(e);
+ throw Lombok.sneakyThrow(e);
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index c22af676..6a9a5123 100644
--- a/src/core/lombok/eclipse/handlers/HandleSetter.java
+++ b/src/core/lombok/eclipse/handlers/HandleSetter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -52,6 +52,7 @@ import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@@ -125,8 +126,8 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
AccessLevel level = annotation.getInstance().value();
if (level == AccessLevel.NONE || node == null) return;
- List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod=", annotationNode);
- List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam=", annotationNode);
+ List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod", annotationNode);
+ List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam", annotationNode);
switch (node.getKind()) {
case FIELD:
@@ -192,11 +193,11 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
}
}
- MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), fieldNode, setterName, shouldReturnThis, modifier, sourceNode, onMethod, onParam);
+ MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), false, fieldNode, setterName, null, shouldReturnThis, modifier, sourceNode, onMethod, onParam);
injectMethod(fieldNode.up(), method);
}
- static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) {
+ static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
ASTNode source = sourceNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
@@ -213,13 +214,10 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
shouldReturnThis = false;
}
Annotation[] deprecated = null;
- if (isFieldDeprecated(fieldNode)) {
+ if (isFieldDeprecated(fieldNode) || deprecate) {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
- Annotation[] copiedAnnotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
- if (copiedAnnotations.length != 0) {
- method.annotations = copiedAnnotations;
- }
+ method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
Argument param = new Argument(field.name, p, copyType(field.type, source), Modifier.FINAL);
param.sourceStart = pS; param.sourceEnd = pE;
method.arguments = new Argument[] { param };
@@ -246,15 +244,17 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
statements.add(assignment);
}
+ if (booleanFieldToSet != null) {
+ statements.add(new Assignment(new SingleNameReference(booleanFieldToSet, p), new TrueLiteral(pS, pE), pE));
+ }
+
if (shouldReturnThis) {
ThisReference thisRef = new ThisReference(pS, pE);
ReturnStatement returnThis = new ReturnStatement(thisRef, pS, pE);
statements.add(returnThis);
}
method.statements = statements.toArray(new Statement[0]);
-
- Annotation[] copiedAnnotationsParam = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
- if (copiedAnnotationsParam.length != 0) param.annotations = copiedAnnotationsParam;
+ param.annotations = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
return method;
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index a4ed254a..d8f4c569 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.java
@@ -94,7 +94,11 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
- generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER);
+
+ Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
+ FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
+
+ generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, access);
}
public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) {
diff --git a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
new file mode 100644
index 00000000..959c1d20
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers;
+
+import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.Arrays;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Clinit;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
+import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.ConfigurationKeys;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.core.AST.Kind;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.experimental.UtilityClass;
+
+/**
+ * Handles the {@code lombok.experimental.UtilityClass} annotation for eclipse.
+ */
+@HandlerPriority(-4096) //-2^12; to ensure @FieldDefaults picks up on the 'static' we set here.
+@ProviderFor(EclipseAnnotationHandler.class)
+public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> {
+ @Override public void handle(AnnotationValues<UtilityClass> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass");
+
+ EclipseNode typeNode = annotationNode.up();
+ if (!checkLegality(typeNode, annotationNode)) return;
+ changeModifiersAndGenerateConstructor(annotationNode.up(), annotationNode);
+ }
+
+ private static boolean checkLegality(EclipseNode typeNode, EclipseNode errorNode) {
+ TypeDeclaration typeDecl = null;
+ if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
+ int modifiers = typeDecl == null ? 0 : typeDecl.modifiers;
+ boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0;
+
+ if (typeDecl == null || notAClass) {
+ errorNode.addError("@UtilityClass is only supported on a class (can't be an interface, enum, or annotation).");
+ return false;
+ }
+
+ // It might be an inner class. This is okay, but only if it is / can be a static inner class. Thus, all of its parents have to be static inner classes until the top-level.
+ EclipseNode typeWalk = typeNode;
+ while (true) {
+ typeWalk = typeWalk.up();
+ switch (typeWalk.getKind()) {
+ case TYPE:
+ if ((((TypeDeclaration) typeWalk.get()).modifiers & (ClassFileConstants.AccStatic | ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0) continue;
+ if (typeWalk.up().getKind() == Kind.COMPILATION_UNIT) return true;
+ errorNode.addError("@UtilityClass automatically makes the class static, however, this class cannot be made static.");
+ return false;
+ case COMPILATION_UNIT:
+ return true;
+ default:
+ errorNode.addError("@UtilityClass cannot be placed on a method local or anonymous inner class, or any class nested in such a class.");
+ return false;
+ }
+ }
+ }
+
+ private void changeModifiersAndGenerateConstructor(EclipseNode typeNode, EclipseNode annotationNode) {
+ TypeDeclaration classDecl = (TypeDeclaration) typeNode.get();
+
+ boolean makeConstructor = true;
+
+ classDecl.modifiers |= ClassFileConstants.AccFinal;
+
+ boolean markStatic = true;
+ boolean requiresClInit = false;
+ boolean alreadyHasClinit = false;
+
+ if (typeNode.up().getKind() == Kind.COMPILATION_UNIT) markStatic = false;
+ if (markStatic && typeNode.up().getKind() == Kind.TYPE) {
+ TypeDeclaration typeDecl = (TypeDeclaration) typeNode.up().get();
+ if ((typeDecl.modifiers & ClassFileConstants.AccInterface) != 0) markStatic = false;
+ }
+
+ if (markStatic) classDecl.modifiers |= ClassFileConstants.AccStatic;
+
+ for (EclipseNode element : typeNode.down()) {
+ if (element.getKind() == Kind.FIELD) {
+ FieldDeclaration fieldDecl = (FieldDeclaration) element.get();
+ if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) == 0) {
+ requiresClInit = true;
+ fieldDecl.modifiers |= ClassFileConstants.AccStatic;
+ }
+ } else if (element.getKind() == Kind.METHOD) {
+ AbstractMethodDeclaration amd = (AbstractMethodDeclaration) element.get();
+ if (amd instanceof ConstructorDeclaration) {
+ ConstructorDeclaration constrDecl = (ConstructorDeclaration) element.get();
+ if (getGeneratedBy(constrDecl) == null && (constrDecl.bits & ASTNode.IsDefaultConstructor) == 0) {
+ element.addError("@UtilityClasses cannot have declared constructors.");
+ makeConstructor = false;
+ continue;
+ }
+ } else if (amd instanceof MethodDeclaration) {
+ amd.modifiers |= ClassFileConstants.AccStatic;
+ } else if (amd instanceof Clinit) {
+ alreadyHasClinit = true;
+ }
+ } else if (element.getKind() == Kind.TYPE) {
+ ((TypeDeclaration) element.get()).modifiers |= ClassFileConstants.AccStatic;
+ }
+ }
+
+ if (makeConstructor) createPrivateDefaultConstructor(typeNode, annotationNode);
+ if (requiresClInit && !alreadyHasClinit) classDecl.addClinit();
+ }
+
+ private static final char[][] JAVA_LANG_UNSUPPORTED_OPERATION_EXCEPTION = new char[][] {
+ TypeConstants.JAVA, TypeConstants.LANG, "UnsupportedOperationException".toCharArray()
+ };
+
+ private static final char[] UNSUPPORTED_MESSAGE = "This is a utility class and cannot be instantiated".toCharArray();
+
+ private void createPrivateDefaultConstructor(EclipseNode typeNode, EclipseNode sourceNode) {
+ ASTNode source = sourceNode.get();
+
+ TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get());
+ long p = (long) source.sourceStart << 32 | source.sourceEnd;
+
+ ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult);
+
+ constructor.modifiers = ClassFileConstants.AccPrivate;
+ constructor.selector = typeDeclaration.name;
+ constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.ImplicitSuper);
+ constructor.constructorCall.sourceStart = source.sourceStart;
+ constructor.constructorCall.sourceEnd = source.sourceEnd;
+ constructor.thrownExceptions = null;
+ constructor.typeParameters = null;
+ constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
+ constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
+ constructor.arguments = null;
+
+ AllocationExpression exception = new AllocationExpression();
+ setGeneratedBy(exception, source);
+ long[] ps = new long[JAVA_LANG_UNSUPPORTED_OPERATION_EXCEPTION.length];
+ Arrays.fill(ps, p);
+ exception.type = new QualifiedTypeReference(JAVA_LANG_UNSUPPORTED_OPERATION_EXCEPTION, ps);
+ setGeneratedBy(exception.type, source);
+ exception.arguments = new Expression[] {
+ new StringLiteral(UNSUPPORTED_MESSAGE, source.sourceStart, source.sourceEnd, 0)
+ };
+ setGeneratedBy(exception.arguments[0], source);
+ ThrowStatement throwStatement = new ThrowStatement(exception, source.sourceStart, source.sourceEnd);
+ setGeneratedBy(throwStatement, source);
+
+ constructor.statements = new Statement[] {throwStatement};
+
+ injectMethod(typeNode, constructor);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java
index d4ae417c..3742ac00 100644
--- a/src/core/lombok/eclipse/handlers/HandleVal.java
+++ b/src/core/lombok/eclipse/handlers/HandleVal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Project Lombok Authors.
+ * Copyright (C) 2010-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
@@ -21,19 +21,24 @@
*/
package lombok.eclipse.handlers;
-import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.core.handlers.HandlerUtil.handleFlagUsage;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches;
import lombok.ConfigurationKeys;
import lombok.val;
+import lombok.var;
import lombok.core.HandlerPriority;
import lombok.eclipse.DeferUntilPostDiet;
import lombok.eclipse.EclipseASTAdapter;
import lombok.eclipse.EclipseASTVisitor;
import lombok.eclipse.EclipseNode;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ForStatement;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.mangosdk.spi.ProviderFor;
/*
@@ -44,8 +49,13 @@ import org.mangosdk.spi.ProviderFor;
@HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated.
public class HandleVal extends EclipseASTAdapter {
@Override public void visitLocal(EclipseNode localNode, LocalDeclaration local) {
- if (!EclipseHandlerUtil.typeMatches(val.class, localNode, local.type)) return;
- handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val");
+ TypeReference type = local.type;
+ boolean isVal = typeMatches(val.class, localNode, type);
+ boolean isVar = typeMatches(var.class, localNode, type);
+ if (!(isVal || isVar)) return;
+
+ if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val");
+ if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var");
boolean variableOfForEach = false;
@@ -54,23 +64,37 @@ public class HandleVal extends EclipseASTAdapter {
variableOfForEach = fs.elementVariable == local;
}
+ String annotation = isVal ? "val" : "var";
if (local.initialization == null && !variableOfForEach) {
- localNode.addError("'val' on a local variable requires an initializer expression");
+ localNode.addError("'" + annotation + "' on a local variable requires an initializer expression");
return;
}
if (local.initialization instanceof ArrayInitializer) {
- localNode.addError("'val' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })");
+ localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })");
return;
}
- if (localNode.directUp().get() instanceof ForStatement) {
+ ASTNode parentRaw = localNode.directUp().get();
+
+ if (isVal && parentRaw instanceof ForStatement) {
localNode.addError("'val' is not allowed in old-style for loops");
return;
}
+ if (parentRaw instanceof ForStatement && ((ForStatement) parentRaw).initializations != null && ((ForStatement) parentRaw).initializations.length > 1) {
+ localNode.addError("'var' is not allowed in old-style for loops if there is more than 1 initializer");
+ return;
+ }
+
if (local.initialization != null && local.initialization.getClass().getName().equals("org.eclipse.jdt.internal.compiler.ast.LambdaExpression")) {
- localNode.addError("'val' is not allowed with lambda expressions.");
+ localNode.addError("'" + annotation + "' is not allowed with lambda expressions.");
+ return;
+ }
+
+ if(isVar && local.initialization instanceof NullLiteral) {
+ localNode.addError("variable initializer is 'null'");
+ return;
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleValue.java b/src/core/lombok/eclipse/handlers/HandleValue.java
index 79c11771..a61ca6c3 100644
--- a/src/core/lombok/eclipse/handlers/HandleValue.java
+++ b/src/core/lombok/eclipse/handlers/HandleValue.java
@@ -47,6 +47,12 @@ import org.mangosdk.spi.ProviderFor;
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier.
public class HandleValue extends EclipseAnnotationHandler<Value> {
+ private HandleFieldDefaults handleFieldDefaults = new HandleFieldDefaults();
+ private HandleGetter handleGetter = new HandleGetter();
+ private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode();
+ private HandleToString handleToString = new HandleToString();
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
public void handle(AnnotationValues<Value> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.VALUE_FLAG_USAGE, "@Value");
@@ -72,7 +78,7 @@ public class HandleValue extends EclipseAnnotationHandler<Value> {
}
}
- new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
+ handleFieldDefaults.generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
//Careful: Generate the public static constructor (if there is one) LAST, so that any attempt to
//'find callers' on the annotation node will find callers of the constructor, which is by far the
@@ -80,10 +86,10 @@ public class HandleValue extends EclipseAnnotationHandler<Value> {
//for whatever reason, though you can find callers of that one by focusing on the class name itself
//and hitting 'find callers'.
- new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
- new HandleToString().generateToStringForType(typeNode, annotationNode);
- new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES,
+ handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
+ handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode);
+ handleToString.generateToStringForType(typeNode, annotationNode);
+ handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES,
Collections.<Annotation>emptyList(), annotationNode);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java
index 8b038676..200ebde7 100644
--- a/src/core/lombok/eclipse/handlers/HandleWither.java
+++ b/src/core/lombok/eclipse/handlers/HandleWither.java
@@ -25,7 +25,6 @@ import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
-import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -57,6 +56,7 @@ import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.mangosdk.spi.ProviderFor;
@ProviderFor(EclipseAnnotationHandler.class)
@@ -127,8 +127,8 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
AccessLevel level = annotation.getInstance().value();
if (level == AccessLevel.NONE || node == null) return;
- List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod=", annotationNode);
- List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam=", annotationNode);
+ List<Annotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod", annotationNode);
+ List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam", annotationNode);
switch (node.getKind()) {
case FIELD:
@@ -163,6 +163,9 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
return;
}
+ EclipseNode typeNode = fieldNode.up();
+ boolean makeAbstract = typeNode != null && typeNode.getKind() == Kind.TYPE && (((TypeDeclaration) typeNode.get()).modifiers & ClassFileConstants.AccAbstract) != 0;
+
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
TypeReference fieldType = copyType(field.type, source);
boolean isBoolean = isBoolean(fieldType);
@@ -208,17 +211,18 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
int modifier = toEclipseModifier(level);
- MethodDeclaration method = createWither((TypeDeclaration) fieldNode.up().get(), fieldNode, witherName, modifier, sourceNode, onMethod, onParam);
+ MethodDeclaration method = createWither((TypeDeclaration) fieldNode.up().get(), fieldNode, witherName, modifier, sourceNode, onMethod, onParam, makeAbstract);
injectMethod(fieldNode.up(), method);
}
- public MethodDeclaration createWither(TypeDeclaration parent, EclipseNode fieldNode, String name, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) {
+ public MethodDeclaration createWither(TypeDeclaration parent, EclipseNode fieldNode, String name, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam, boolean makeAbstract ) {
ASTNode source = sourceNode.get();
if (name == null) return null;
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
MethodDeclaration method = new MethodDeclaration(parent.compilationResult);
+ if (makeAbstract) modifier = modifier | ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody;
method.modifiers = modifier;
method.returnType = cloneSelfType(fieldNode, source);
if (method.returnType == null) return null;
@@ -227,11 +231,8 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
if (isFieldDeprecated(fieldNode)) {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
- Annotation[] copiedAnnotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
- if (copiedAnnotations.length != 0) {
- method.annotations = copiedAnnotations;
- }
- Argument param = new Argument(field.name, p, copyType(field.type, source), Modifier.FINAL);
+ method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
+ Argument param = new Argument(field.name, p, copyType(field.type, source), ClassFileConstants.AccFinal);
param.sourceStart = pS; param.sourceEnd = pE;
method.arguments = new Argument[] { param };
method.selector = name.toCharArray();
@@ -240,51 +241,52 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> {
method.typeParameters = null;
method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
- List<Expression> args = new ArrayList<Expression>();
- for (EclipseNode child : fieldNode.up().down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration childDecl = (FieldDeclaration) child.get();
- // Skip fields that start with $
- if (childDecl.name != null && childDecl.name.length > 0 && childDecl.name[0] == '$') continue;
- long fieldFlags = childDecl.modifiers;
- // Skip static fields.
- if ((fieldFlags & ClassFileConstants.AccStatic) != 0) continue;
- // Skip initialized final fields.
- if (((fieldFlags & ClassFileConstants.AccFinal) != 0) && childDecl.initialization != null) continue;
- if (child.get() == fieldNode.get()) {
- args.add(new SingleNameReference(field.name, p));
- } else {
- args.add(createFieldAccessor(child, FieldAccess.ALWAYS_FIELD, source));
- }
- }
-
- AllocationExpression constructorCall = new AllocationExpression();
- constructorCall.arguments = args.toArray(new Expression[0]);
- constructorCall.type = cloneSelfType(fieldNode, source);
-
- Expression identityCheck = new EqualExpression(
- createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source),
- new SingleNameReference(field.name, p),
- OperatorIds.EQUAL_EQUAL);
- ThisReference thisRef = new ThisReference(pS, pE);
- Expression conditional = new ConditionalExpression(identityCheck, thisRef, constructorCall);
- Statement returnStatement = new ReturnStatement(conditional, pS, pE);
- method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
- method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
-
Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
- List<Statement> statements = new ArrayList<Statement>(5);
- if (nonNulls.length > 0) {
- Statement nullCheck = generateNullCheck(field, sourceNode);
- if (nullCheck != null) statements.add(nullCheck);
- }
- statements.add(returnStatement);
-
- method.statements = statements.toArray(new Statement[0]);
- Annotation[] copiedAnnotationsParam = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
- if (copiedAnnotationsParam.length != 0) param.annotations = copiedAnnotationsParam;
+ if (!makeAbstract) {
+ List<Expression> args = new ArrayList<Expression>();
+ for (EclipseNode child : fieldNode.up().down()) {
+ if (child.getKind() != Kind.FIELD) continue;
+ FieldDeclaration childDecl = (FieldDeclaration) child.get();
+ // Skip fields that start with $
+ if (childDecl.name != null && childDecl.name.length > 0 && childDecl.name[0] == '$') continue;
+ long fieldFlags = childDecl.modifiers;
+ // Skip static fields.
+ if ((fieldFlags & ClassFileConstants.AccStatic) != 0) continue;
+ // Skip initialized final fields.
+ if (((fieldFlags & ClassFileConstants.AccFinal) != 0) && childDecl.initialization != null) continue;
+ if (child.get() == fieldNode.get()) {
+ args.add(new SingleNameReference(field.name, p));
+ } else {
+ args.add(createFieldAccessor(child, FieldAccess.ALWAYS_FIELD, source));
+ }
+ }
+
+ AllocationExpression constructorCall = new AllocationExpression();
+ constructorCall.arguments = args.toArray(new Expression[0]);
+ constructorCall.type = cloneSelfType(fieldNode, source);
+
+ Expression identityCheck = new EqualExpression(
+ createFieldAccessor(fieldNode, FieldAccess.ALWAYS_FIELD, source),
+ new SingleNameReference(field.name, p),
+ OperatorIds.EQUAL_EQUAL);
+ ThisReference thisRef = new ThisReference(pS, pE);
+ Expression conditional = new ConditionalExpression(identityCheck, thisRef, constructorCall);
+ Statement returnStatement = new ReturnStatement(conditional, pS, pE);
+ method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
+ method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
+
+ List<Statement> statements = new ArrayList<Statement>(5);
+ if (nonNulls.length > 0) {
+ Statement nullCheck = generateNullCheck(field, sourceNode);
+ if (nullCheck != null) statements.add(nullCheck);
+ }
+ statements.add(returnStatement);
+
+ method.statements = statements.toArray(new Statement[0]);
+ }
+ param.annotations = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
method.traverse(new SetGeneratedByVisitor(source), parent.scope);
return method;
diff --git a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
index 7217a396..89964914 100644
--- a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
+++ b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2013 The Project Lombok Authors.
+ * Copyright (C) 2011-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,8 @@ package lombok.eclipse.handlers;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+import java.util.Arrays;
+
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
@@ -128,881 +130,816 @@ public final class SetGeneratedByVisitor extends ASTVisitor {
private static final long INT_TO_LONG_MASK = 0x00000000FFFFFFFFL;
private final ASTNode source;
- private final int newSourceStart;
- private final int newSourceEnd;
+ private final int sourceStart;
+ private final int sourceEnd;
+ private final long sourcePos;
public SetGeneratedByVisitor(ASTNode source) {
this.source = source;
- this.newSourceStart = this.source.sourceStart;
- this.newSourceEnd = this.source.sourceEnd;
+ this.sourceStart = this.source.sourceStart;
+ this.sourceEnd = this.source.sourceEnd;
+ this.sourcePos = (long)sourceStart << 32 | (sourceEnd & INT_TO_LONG_MASK);
}
- private void applyOffset(JavadocAllocationExpression node) {
- applyOffsetExpression(node);
- node.memberStart = newSourceStart;
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocAllocationExpression node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.memberStart = sourceStart;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
}
- private void applyOffset(JavadocMessageSend node) {
- applyOffsetMessageSend(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocMessageSend node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.nameSourcePosition = sourcePos;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
}
- private void applyOffset(JavadocSingleNameReference node) {
- applyOffsetExpression(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocSingleNameReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
}
- private void applyOffset(JavadocSingleTypeReference node) {
- applyOffsetExpression(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocSingleTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
}
- private void applyOffset(JavadocFieldReference node) {
- applyOffsetFieldReference(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(JavadocFieldReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.nameSourcePosition = sourcePos;
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
+ }
+
+ private void fixPositions(JavadocArrayQualifiedTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
+ }
+
+ private void fixPositions(JavadocQualifiedTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
+ node.tagSourceEnd = sourceEnd;
+ node.tagSourceStart = sourceStart;
+ }
+
+ private void fixPositions(Annotation node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
}
-
- private void applyOffset(JavadocArrayQualifiedTypeReference node) {
- applyOffsetQualifiedTypeReference(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+
+ private void fixPositions(ArrayTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.originalSourceEnd = sourceEnd;
}
- private void applyOffset(JavadocQualifiedTypeReference node) {
- applyOffsetQualifiedTypeReference(node);
- node.tagSourceEnd = newSourceEnd;
- node.tagSourceStart = newSourceStart;
+ private void fixPositions(AbstractMethodDeclaration node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.bodyEnd = sourceEnd;
+ node.bodyStart = sourceStart;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
}
- private void applyOffset(Annotation node) {
- applyOffsetExpression(node);
- node.declarationSourceEnd = newSourceEnd;
+ private void fixPositions(Javadoc node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.valuePositions = sourceStart;
}
- private void applyOffset(ArrayTypeReference node) {
- applyOffsetExpression(node);
- node.originalSourceEnd = newSourceEnd;
+ private void fixPositions(Initializer node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.declarationEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
+ node.endPart1Position = sourceEnd;
+ node.endPart2Position = sourceEnd;
+ node.bodyStart = sourceStart;
+ node.bodyEnd = sourceEnd;
}
-
- private void applyOffset(AbstractMethodDeclaration node) {
- applyOffsetASTNode(node);
- node.bodyEnd = newSourceEnd;
- node.bodyStart = newSourceStart;
- node.declarationSourceEnd = newSourceEnd;
- node.declarationSourceStart = newSourceStart;
- node.modifiersSourceStart = newSourceStart;
+
+ private void fixPositions(TypeDeclaration node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.bodyEnd = sourceEnd;
+ node.bodyStart = sourceStart;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
}
- private void applyOffset(Javadoc node) {
- applyOffsetASTNode(node);
- node.valuePositions = newSourceStart;
- for (int i = 0; i < node.inheritedPositions.length; i++) {
- node.inheritedPositions[i] = recalcSourcePosition(node.inheritedPositions[i]);
- }
+ private void fixPositions(ImportReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.declarationEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
}
-
- private void applyOffset(Initializer node) {
- applyOffsetFieldDeclaration(node);
- node.bodyStart = newSourceStart;
- node.bodyEnd = newSourceEnd;
- }
-
- private void applyOffset(TypeDeclaration node) {
- applyOffsetASTNode(node);
- node.bodyEnd = newSourceEnd;
- node.bodyStart = newSourceStart;
- node.declarationSourceEnd = newSourceEnd;
- node.declarationSourceStart = newSourceStart;
- node.modifiersSourceStart = newSourceStart;
- }
-
- private void applyOffset(ImportReference node) {
- applyOffsetASTNode(node);
- node.declarationEnd = newSourceEnd;
- node.declarationSourceEnd = newSourceEnd;
- node.declarationSourceStart = newSourceStart;
- for (int i = 0; i < node.sourcePositions.length; i++) {
- node.sourcePositions[i] = recalcSourcePosition(node.sourcePositions[i]);
- }
+
+ private void fixPositions(ASTNode node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
}
-
- private void applyOffsetASTNode(ASTNode node) {
- node.sourceEnd = newSourceEnd;
- node.sourceStart = newSourceStart;
+
+ private void fixPositions(SwitchStatement node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.blockStart = sourceStart;
}
-
- private void applyOffsetExpression(Expression node) {
- applyOffsetASTNode(node);
-// if (node.statementEnd != -1) {
- node.statementEnd = newSourceEnd;
-// }
+
+ private void fixPositions(Expression node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
}
-
- private void applyOffsetVariable(AbstractVariableDeclaration node) {
- applyOffsetASTNode(node);
- node.declarationEnd = newSourceEnd;
- node.declarationSourceEnd = newSourceEnd;
- node.declarationSourceStart = newSourceStart;
- node.modifiersSourceStart = newSourceStart;
+
+ private void fixPositions(AbstractVariableDeclaration node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.declarationEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
}
- private void applyOffsetFieldDeclaration(FieldDeclaration node) {
- applyOffsetVariable(node);
- node.endPart1Position = newSourceEnd;
- node.endPart2Position = newSourceEnd;
+ private void fixPositions(FieldDeclaration node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.declarationEnd = sourceEnd;
+ node.declarationSourceEnd = sourceEnd;
+ node.declarationSourceStart = sourceStart;
+ node.modifiersSourceStart = sourceStart;
+ node.endPart1Position = sourceEnd;
+ node.endPart2Position = sourceEnd;
}
- private void applyOffsetFieldReference(FieldReference node) {
- applyOffsetExpression(node);
- node.nameSourcePosition = recalcSourcePosition(node.nameSourcePosition);
+ private void fixPositions(FieldReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.nameSourcePosition = sourcePos;
}
- private void applyOffsetMessageSend(MessageSend node) {
- applyOffsetExpression(node);
- node.nameSourcePosition = recalcSourcePosition(node.nameSourcePosition);
+ private void fixPositions(MessageSend node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ node.nameSourcePosition = sourcePos;
}
- private void applyOffsetQualifiedNameReference(QualifiedNameReference node) {
- applyOffsetExpression(node);
- for (int i = 0; i < node.sourcePositions.length; i++) {
- node.sourcePositions[i] = recalcSourcePosition(node.sourcePositions[i]);
- }
+ private void fixPositions(QualifiedNameReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
}
- private void applyOffsetQualifiedTypeReference(QualifiedTypeReference node) {
- applyOffsetExpression(node);
- for (int i = 0; i < node.sourcePositions.length; i++) {
- node.sourcePositions[i] = recalcSourcePosition(node.sourcePositions[i]);
- }
- }
-
- /** See {@link FieldReference#nameSourcePosition} for explanation */
- private long recalcSourcePosition(long sourcePosition) {
-// long start = (sourcePosition >>> 32);
-// long end = (sourcePosition & 0x00000000FFFFFFFFL);
-// start = newSourceStart;
-// end = newSourceStart;
-// return ((start<<32)+end);
- return ((long)newSourceStart << 32) | (newSourceEnd & INT_TO_LONG_MASK);
+ private void fixPositions(QualifiedTypeReference node) {
+ node.sourceEnd = sourceEnd;
+ node.sourceStart = sourceStart;
+ node.statementEnd = sourceEnd;
+ if (node.sourcePositions == null || node.sourcePositions.length != node.tokens.length) node.sourcePositions = new long[node.tokens.length];
+ Arrays.fill(node.sourcePositions, sourcePos);
}
@Override public boolean visit(AllocationExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
-
+
@Override public boolean visit(AND_AND_Expression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(AnnotationMethodDeclaration node, ClassScope classScope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, classScope);
}
@Override public boolean visit(Argument node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Argument node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayAllocationExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayInitializer node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayQualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayQualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ArrayTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(AssertStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Assignment node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(BinaryExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Block node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(BreakStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CaseStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CastExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CharLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ClassLiteralAccess node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Clinit node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CompilationUnitDeclaration node, CompilationUnitScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(CompoundAssignment node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ConditionalExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ConstructorDeclaration node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ContinueStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(DoStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(DoubleLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(EmptyStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(EqualExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ExplicitConstructorCall node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ExtendedStringLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FalseLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FieldDeclaration node, MethodScope scope) {
- setGeneratedBy(node, source);
- applyOffsetFieldDeclaration(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FieldReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetFieldReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FieldReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetFieldReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(FloatLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ForeachStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ForStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(IfStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ImportReference node, CompilationUnitScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Initializer node, MethodScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(InstanceOfExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(IntLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Javadoc node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Javadoc node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocAllocationExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocAllocationExpression node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArgumentExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArgumentExpression node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArrayQualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArrayQualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArraySingleTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocArraySingleTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocFieldReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocFieldReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocImplicitTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocImplicitTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocMessageSend node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocMessageSend node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocQualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocQualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocReturnStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocReturnStatement node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocSingleNameReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocSingleNameReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocSingleTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(JavadocSingleTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(LabeledStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(LocalDeclaration node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(LongLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(MarkerAnnotation node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(MemberValuePair node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(MessageSend node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetMessageSend(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(MethodDeclaration node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(StringLiteralConcatenation node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(NormalAnnotation node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(NullLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(OR_OR_Expression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ParameterizedQualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ParameterizedQualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ParameterizedSingleTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ParameterizedSingleTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(PostfixExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(PrefixExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedAllocationExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedNameReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedNameReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedNameReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedSuperReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedSuperReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedThisReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedThisReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(QualifiedTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetQualifiedTypeReference(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ReturnStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleMemberAnnotation node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleNameReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleNameReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleTypeReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SingleTypeReference node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(StringLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SuperReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SwitchStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
- node.blockStart = newSourceStart;
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(SynchronizedStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ThisReference node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ThisReference node, ClassScope scope) {
- setGeneratedBy(node, source);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(ThrowStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TrueLiteral node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TryStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeDeclaration node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeDeclaration node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeDeclaration node, CompilationUnitScope scope) {
- setGeneratedBy(node, source);
- applyOffset(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeParameter node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(TypeParameter node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetVariable(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(UnaryExpression node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(WhileStatement node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetASTNode(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Wildcard node, BlockScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
@Override public boolean visit(Wildcard node, ClassScope scope) {
- setGeneratedBy(node, source);
- applyOffsetExpression(node);
+ fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
}
+
+// missing methods
+// public boolean visit(MarkerAnnotation node, ClassScope scope){ return true;}
+// public boolean visit(MemberValuePair node, ClassScope scope){ return true;}
+// public boolean visit(NormalAnnotation node, ClassScope scope){ return true;}
+// public boolean visit(SingleMemberAnnotation node, ClassScope scope){ return true;}
+
+// missing methods from later versions
+// public boolean visit(UnionTypeReference node, BlockScope scope){ return true;}
+// public boolean visit(UnionTypeReference node, ClassScope scope){ return true;}
+// public boolean visit(LambdaExpression node, BlockScope scope){ return true;}
+// public boolean visit(ReferenceExpression node, BlockScope scope){ return true;}
+// public boolean visit(IntersectionCastTypeReference node, ClassScope scope){ return true;}
+// public boolean visit(IntersectionCastTypeReference node, BlockScope scope){ return true;}
} \ No newline at end of file
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java
new file mode 100644
index 00000000..5956c01b
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaMapSingularizer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseGuavaMapSingularizer extends EclipseGuavaSingularizer {
+ // TODO cgcc.ImmutableMultimap, cgcc.ImmutableListMultimap, cgcc.ImmutableSetMultimap
+ // TODO cgcc.ImmutableClassToInstanceMap
+ // TODO cgcc.ImmutableRangeMap
+
+ private static final LombokImmutableList<String> SUFFIXES =
+ LombokImmutableList.of("key", "value");
+ private static final LombokImmutableList<String> SUPPORTED_TYPES = LombokImmutableList.of(
+ "com.google.common.collect.ImmutableMap",
+ "com.google.common.collect.ImmutableBiMap",
+ "com.google.common.collect.ImmutableSortedMap"
+ );
+
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return SUPPORTED_TYPES;
+ }
+
+ @Override protected LombokImmutableList<String> getArgumentSuffixes() {
+ return SUFFIXES;
+ }
+
+ @Override protected String getAddMethodName() {
+ return "put";
+ }
+
+ @Override protected String getAddAllTypeName() {
+ return "java.util.Map";
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java
new file mode 100644
index 00000000..326a9179
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSetListSingularizer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseGuavaSetListSingularizer extends EclipseGuavaSingularizer {
+ // TODO com.google.common.collect.ImmutableRangeSet
+ // TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset
+ private static final LombokImmutableList<String> SUFFIXES = LombokImmutableList.of("");
+ private static final LombokImmutableList<String> SUPPORTED_TYPES = LombokImmutableList.of(
+ "com.google.common.collect.ImmutableCollection",
+ "com.google.common.collect.ImmutableList",
+ "com.google.common.collect.ImmutableSet",
+ "com.google.common.collect.ImmutableSortedSet"
+ );
+
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return SUPPORTED_TYPES;
+ }
+
+ @Override protected LombokImmutableList<String> getArgumentSuffixes() {
+ return SUFFIXES;
+ }
+
+ @Override protected String getAddMethodName() {
+ return "add";
+ }
+
+ @Override protected String getAddAllTypeName() {
+ return "java.lang.Iterable";
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
new file mode 100644
index 00000000..8e925b3f
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import lombok.core.GuavaTypeMap;
+import lombok.core.LombokImmutableList;
+import lombok.core.handlers.HandlerUtil;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+import org.eclipse.jdt.internal.compiler.ast.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.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
+ protected String getSimpleTargetTypeName(SingularData data) {
+ return GuavaTypeMap.getGuavaTypeName(data.getTargetFqn());
+ }
+
+ protected char[] getBuilderMethodName(SingularData data) {
+ String simpleTypeName = getSimpleTargetTypeName(data);
+ if ("ImmutableSortedSet".equals(simpleTypeName) || "ImmutableSortedMap".equals(simpleTypeName)) return "naturalOrder".toCharArray();
+ return "builder".toCharArray();
+ }
+
+ protected char[][] makeGuavaTypeName(String simpleName, boolean addBuilder) {
+ char[][] tokenizedName = new char[addBuilder ? 6 : 5][];
+ tokenizedName[0] = new char[] {'c', 'o', 'm'};
+ tokenizedName[1] = new char[] {'g', 'o', 'o', 'g', 'l', 'e'};
+ tokenizedName[2] = new char[] {'c', 'o', 'm', 'm', 'o', 'n'};
+ tokenizedName[3] = new char[] {'c', 'o', 'l', 'l', 'e', 'c', 't'};
+ tokenizedName[4] = simpleName.toCharArray();
+ if (addBuilder) tokenizedName[5] = new char[] { 'B', 'u', 'i', 'l', 'd', 'e', 'r'};
+ return tokenizedName;
+ }
+
+ @Override public List<EclipseNode> generateFields(SingularData data, EclipseNode builderType) {
+ String simpleTypeName = getSimpleTargetTypeName(data);
+ char[][] tokenizedName = makeGuavaTypeName(simpleTypeName, true);
+ TypeReference type = new QualifiedTypeReference(tokenizedName, NULL_POSS);
+ type = addTypeArgs(getTypeArgumentsCount(), false, builderType, type, data.getTypeArgs());
+
+ FieldDeclaration buildField = new FieldDeclaration(data.getPluralName(), 0, -1);
+ buildField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ buildField.modifiers = ClassFileConstants.AccPrivate;
+ buildField.declarationSourceEnd = -1;
+ buildField.type = type;
+ data.setGeneratedByRecursive(buildField);
+ return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) {
+ TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateClearMethod(deprecate, returnType, returnStatement, data, builderType);
+ }
+
+ void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ Assignment a = new Assignment(thisDotField, new NullLiteral(0, 0), 0);
+ md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray();
+ md.statements = returnStatement != null ? new Statement[] {a, returnStatement} : new Statement[] {a};
+ md.returnType = returnType;
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ injectMethod(builderType, md);
+ }
+
+ void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ LombokImmutableList<String> suffixes = getArgumentSuffixes();
+ char[][] names = new char[suffixes.size()][];
+ for (int i = 0; i < suffixes.size(); i++) {
+ String s = suffixes.get(i);
+ char[] n = data.getSingularName();
+ names[i] = s.isEmpty() ? n : s.toCharArray();
+ }
+
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType));
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldDotAdd = new MessageSend();
+ thisDotFieldDotAdd.arguments = new Expression[suffixes.size()];
+ for (int i = 0; i < suffixes.size(); i++) {
+ thisDotFieldDotAdd.arguments[i] = new SingleNameReference(names[i], 0L);
+ }
+ thisDotFieldDotAdd.receiver = thisDotField;
+ thisDotFieldDotAdd.selector = getAddMethodName().toCharArray();
+ statements.add(thisDotFieldDotAdd);
+ if (returnStatement != null) statements.add(returnStatement);
+ md.statements = statements.toArray(new Statement[statements.size()]);
+ md.arguments = new Argument[suffixes.size()];
+ for (int i = 0; i < suffixes.size(); i++) {
+ TypeReference tr = cloneParamType(i, data.getTypeArgs(), builderType);
+ md.arguments[i] = new Argument(names[i], 0, tr, 0);
+ }
+ md.returnType = returnType;
+ md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(getAddMethodName(), new String(data.getSingularName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType));
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldDotAddAll = new MessageSend();
+ thisDotFieldDotAddAll.arguments = new Expression[] {new SingleNameReference(data.getPluralName(), 0L)};
+ thisDotFieldDotAddAll.receiver = thisDotField;
+ thisDotFieldDotAddAll.selector = (getAddMethodName() + "All").toCharArray();
+ statements.add(thisDotFieldDotAddAll);
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+
+ TypeReference paramType;
+ paramType = new QualifiedTypeReference(fromQualifiedName(getAddAllTypeName()), NULL_POSS);
+ paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs());
+ Argument param = new Argument(data.getPluralName(), 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(getAddMethodName() + "All", new String(data.getPluralName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ TypeReference varType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
+ String simpleTypeName = getSimpleTargetTypeName(data);
+ int agrumentsCount = getTypeArgumentsCount();
+ varType = addTypeArgs(agrumentsCount, false, builderType, varType, data.getTypeArgs());
+
+ MessageSend emptyInvoke; {
+ //ImmutableX.of()
+ emptyInvoke = new MessageSend();
+ emptyInvoke.selector = new char[] {'o', 'f'};
+ emptyInvoke.receiver = new QualifiedNameReference(makeGuavaTypeName(simpleTypeName, false), NULL_POSS, 0, 0);
+ emptyInvoke.typeArguments = createTypeArgs(agrumentsCount, false, builderType, data.getTypeArgs());
+ }
+
+ MessageSend invokeBuild; {
+ //this.pluralName.build();
+ invokeBuild = new MessageSend();
+ invokeBuild.selector = new char[] {'b', 'u', 'i', 'l', 'd'};
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ invokeBuild.receiver = thisDotField;
+ }
+
+ Expression isNull; {
+ //this.pluralName == null
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ isNull = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+ }
+
+ Expression init = new ConditionalExpression(isNull, emptyInvoke, invokeBuild);
+ LocalDeclaration varDefStat = new LocalDeclaration(data.getPluralName(), 0, 0);
+ varDefStat.type = varType;
+ varDefStat.initialization = init;
+ statements.add(varDefStat);
+ }
+
+ protected Statement createConstructBuilderVarIfNeeded(SingularData data, EclipseNode builderType) {
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ FieldReference thisDotField2 = new FieldReference(data.getPluralName(), 0L);
+ thisDotField2.receiver = new ThisReference(0, 0);
+ Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+
+ MessageSend createBuilderInvoke = new MessageSend();
+ char[][] tokenizedName = makeGuavaTypeName(getSimpleTargetTypeName(data), false);
+ createBuilderInvoke.receiver = new QualifiedNameReference(tokenizedName, NULL_POSS, 0, 0);
+ createBuilderInvoke.selector = getBuilderMethodName(data);
+ return new IfStatement(cond, new Assignment(thisDotField2, createBuilderInvoke, 0), 0, 0);
+ }
+
+ protected abstract LombokImmutableList<String> getArgumentSuffixes();
+ protected abstract String getAddMethodName();
+ protected abstract String getAddAllTypeName();
+
+ protected int getTypeArgumentsCount() {
+ return getArgumentSuffixes().size();
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java
new file mode 100644
index 00000000..4d25811b
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaTableSingularizer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseGuavaTableSingularizer extends EclipseGuavaSingularizer {
+ private static final LombokImmutableList<String> SUFFIXES =
+ LombokImmutableList.of("rowKey", "columnKey", "value");
+ private static final LombokImmutableList<String> SUPPORTED_TYPES =
+ LombokImmutableList.of("com.google.common.collect.ImmutableTable");
+
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return SUPPORTED_TYPES;
+ }
+
+ @Override protected LombokImmutableList<String> getArgumentSuffixes() {
+ return SUFFIXES;
+ }
+
+ @Override protected String getAddMethodName() {
+ return "put";
+ }
+
+ @Override protected String getAddAllTypeName() {
+ return "com.google.common.collect.Table";
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
new file mode 100644
index 00000000..cfa48eaf
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import static lombok.eclipse.Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import lombok.core.handlers.HandlerUtil;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.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.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingularizer {
+ @Override public List<char[]> listFieldsToBeGenerated(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ return super.listFieldsToBeGenerated(data, builderType);
+ }
+
+ @Override public List<char[]> listMethodsToBeGenerated(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listMethodsToBeGenerated(data, builderType);
+ }
+
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+
+ @Override public List<EclipseNode> generateFields(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.generateFields(data, builderType);
+ }
+
+ TypeReference type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs());
+
+ FieldDeclaration buildField = new FieldDeclaration(data.getPluralName(), 0, -1);
+ buildField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ buildField.modifiers = ClassFileConstants.AccPrivate;
+ buildField.declarationSourceEnd = -1;
+ buildField.type = type;
+ data.setGeneratedByRecursive(buildField);
+ return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, chain);
+ return;
+ }
+
+ TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateClearMethod(deprecate, returnType, returnStatement, data, builderType);
+ }
+
+ private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ FieldReference thisDotField2 = new FieldReference(data.getPluralName(), 0L);
+ thisDotField2.receiver = new ThisReference(0, 0);
+ md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray();
+ MessageSend clearMsg = new MessageSend();
+ clearMsg.receiver = thisDotField2;
+ clearMsg.selector = "clear".toCharArray();
+ Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsg, 0, 0);
+ md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement};
+ md.returnType = returnType;
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+ injectMethod(builderType, md);
+ }
+
+ void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType, false));
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldDotAdd = new MessageSend();
+ thisDotFieldDotAdd.arguments = new Expression[] {new SingleNameReference(data.getSingularName(), 0L)};
+ thisDotFieldDotAdd.receiver = thisDotField;
+ thisDotFieldDotAdd.selector = "add".toCharArray();
+ statements.add(thisDotFieldDotAdd);
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+ TypeReference paramType = cloneParamType(0, data.getTypeArgs(), builderType);
+ Argument param = new Argument(data.getSingularName(), 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType, false));
+
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldDotAddAll = new MessageSend();
+ thisDotFieldDotAddAll.arguments = new Expression[] {new SingleNameReference(data.getPluralName(), 0L)};
+ thisDotFieldDotAddAll.receiver = thisDotField;
+ thisDotFieldDotAddAll.selector = "addAll".toCharArray();
+ statements.add(thisDotFieldDotAddAll);
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+
+ TypeReference paramType = new QualifiedTypeReference(TypeConstants.JAVA_UTIL_COLLECTION, NULL_POSS);
+ paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs());
+ Argument param = new Argument(data.getPluralName(), 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
new file mode 100644
index 00000000..576115b0
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import static lombok.eclipse.handlers.EclipseHandlerUtil.makeIntLiteral;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.BreakStatement;
+import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.mangosdk.spi.ProviderFor;
+
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable");
+ }
+
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ return;
+ }
+
+ List<Statement> switchContents = new ArrayList<Statement>();
+
+ /* case 0: (empty) break; */ {
+ switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'0'}, null), 0, 0));
+ MessageSend invoke = new MessageSend();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ invoke.selector = "emptyList".toCharArray();
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0));
+ switchContents.add(new BreakStatement(null, 0, 0));
+ }
+
+ /* case 1: (singleton) break; */ {
+ switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'1'}, null), 0, 0));
+ FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotFieldGet0 = new MessageSend();
+ thisDotFieldGet0.receiver = thisDotField;
+ thisDotFieldGet0.selector = new char[] {'g', 'e', 't'};
+ thisDotFieldGet0.arguments = new Expression[] {makeIntLiteral(new char[] {'0'}, null)};
+
+ Expression[] args = new Expression[] {thisDotFieldGet0};
+ MessageSend invoke = new MessageSend();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ invoke.selector = "singletonList".toCharArray();
+ invoke.arguments = args;
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0));
+ switchContents.add(new BreakStatement(null, 0, 0));
+ }
+
+ /* default: Create by passing builder field to constructor. */ {
+ switchContents.add(new CaseStatement(null, 0, 0));
+
+ Expression argToUnmodifiable;
+ /* new j.u.ArrayList<Generics>(this.pluralName); */ {
+ FieldReference thisDotPluralName = new FieldReference(data.getPluralName(), 0L);
+ thisDotPluralName.receiver = new ThisReference(0, 0);
+ TypeReference targetTypeExpr = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs());
+ AllocationExpression constructorCall = new AllocationExpression();
+ constructorCall.type = targetTypeExpr;
+ constructorCall.arguments = new Expression[] {thisDotPluralName};
+ argToUnmodifiable = constructorCall;
+ }
+
+ /* pluralname = Collections.unmodifiableList(-newlist-); */ {
+ MessageSend unmodInvoke = new MessageSend();
+ unmodInvoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ unmodInvoke.selector = "unmodifiableList".toCharArray();
+ unmodInvoke.arguments = new Expression[] {argToUnmodifiable};
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), unmodInvoke, 0));
+ }
+ }
+
+ SwitchStatement switchStat = new SwitchStatement();
+ switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]);
+ switchStat.expression = getSize(builderType, data.getPluralName(), true);
+
+ TypeReference localShadowerType = new QualifiedTypeReference(Eclipse.fromQualifiedName(data.getTargetFqn()), NULL_POSS);
+ localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs());
+ LocalDeclaration varDefStat = new LocalDeclaration(data.getPluralName(), 0, 0);
+ varDefStat.type = localShadowerType;
+ statements.add(varDefStat);
+ statements.add(switchStat);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
new file mode 100644
index 00000000..9aac32a3
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.handlers.HandlerUtil;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Map", "java.util.SortedMap", "java.util.NavigableMap");
+ }
+
+ @Override public List<char[]> listFieldsToBeGenerated(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ char[] p = data.getPluralName();
+ int len = p.length;
+ char[] k = new char[len + 4];
+ char[] v = new char[len + 6];
+ System.arraycopy(p, 0, k, 0, len);
+ System.arraycopy(p, 0, v, 0, len);
+ k[len] = '$';
+ k[len + 1] = 'k';
+ k[len + 2] = 'e';
+ k[len + 3] = 'y';
+ v[len] = '$';
+ v[len + 1] = 'v';
+ v[len + 2] = 'a';
+ v[len + 3] = 'l';
+ v[len + 4] = 'u';
+ v[len + 5] = 'e';
+ return Arrays.asList(k, v);
+ }
+
+ @Override public List<char[]> listMethodsToBeGenerated(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listFieldsToBeGenerated(data, builderType);
+ } else {
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+ }
+
+ @Override public List<EclipseNode> generateFields(SingularData data, EclipseNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.generateFields(data, builderType);
+ }
+
+ char[] keyName = (new String(data.getPluralName()) + "$key").toCharArray();
+ char[] valueName = (new String(data.getPluralName()) + "$value").toCharArray();
+ FieldDeclaration buildKeyField; {
+ TypeReference type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs());
+ buildKeyField = new FieldDeclaration(keyName, 0, -1);
+ buildKeyField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ buildKeyField.modifiers = ClassFileConstants.AccPrivate;
+ buildKeyField.declarationSourceEnd = -1;
+ buildKeyField.type = type;
+ }
+ FieldDeclaration buildValueField; {
+ TypeReference type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ List<TypeReference> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.size() > 1) tArgs = Collections.singletonList(tArgs.get(1));
+ else tArgs = Collections.emptyList();
+ type = addTypeArgs(1, false, builderType, type, tArgs);
+ buildValueField = new FieldDeclaration(valueName, 0, -1);
+ buildValueField.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ buildValueField.modifiers = ClassFileConstants.AccPrivate;
+ buildValueField.declarationSourceEnd = -1;
+ buildValueField.type = type;
+ }
+ data.setGeneratedByRecursive(buildKeyField);
+ data.setGeneratedByRecursive(buildValueField);
+ EclipseNode keyFieldNode = injectFieldAndMarkGenerated(builderType, buildKeyField);
+ EclipseNode valueFieldNode = injectFieldAndMarkGenerated(builderType, buildValueField);
+ return Arrays.asList(keyFieldNode, valueFieldNode);
+ }
+
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, chain);
+ return;
+ }
+
+ TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ generateClearMethod(deprecate, returnType, returnStatement, data, builderType);
+ }
+
+ private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ String pN = new String(data.getPluralName());
+ char[] keyFieldName = (pN + "$key").toCharArray();
+ char[] valueFieldName = (pN + "$value").toCharArray();
+
+ FieldReference thisDotField = new FieldReference(keyFieldName, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ FieldReference thisDotField2 = new FieldReference(keyFieldName, 0L);
+ thisDotField2.receiver = new ThisReference(0, 0);
+ FieldReference thisDotField3 = new FieldReference(valueFieldName, 0L);
+ thisDotField3.receiver = new ThisReference(0, 0);
+ md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray();
+ MessageSend clearMsg1 = new MessageSend();
+ clearMsg1.receiver = thisDotField2;
+ clearMsg1.selector = "clear".toCharArray();
+ MessageSend clearMsg2 = new MessageSend();
+ clearMsg2.receiver = thisDotField3;
+ clearMsg2.selector = "clear".toCharArray();
+ Block clearMsgs = new Block(2);
+ clearMsgs.statements = new Statement[] {clearMsg1, clearMsg2};
+ Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsgs, 0, 0);
+ md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement};
+ md.returnType = returnType;
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ injectMethod(builderType, md);
+ }
+
+ private void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType, true));
+
+ String sN = new String(data.getSingularName());
+ String pN = new String(data.getPluralName());
+ char[] keyParamName = (sN + "Key").toCharArray();
+ char[] valueParamName = (sN + "Value").toCharArray();
+ char[] keyFieldName = (pN + "$key").toCharArray();
+ char[] valueFieldName = (pN + "$value").toCharArray();
+
+ /* this.pluralname$key.add(singularnameKey); */ {
+ FieldReference thisDotKeyField = new FieldReference(keyFieldName, 0L);
+ thisDotKeyField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotKeyFieldDotAdd = new MessageSend();
+ thisDotKeyFieldDotAdd.arguments = new Expression[] {new SingleNameReference(keyParamName, 0L)};
+ thisDotKeyFieldDotAdd.receiver = thisDotKeyField;
+ thisDotKeyFieldDotAdd.selector = "add".toCharArray();
+ statements.add(thisDotKeyFieldDotAdd);
+ }
+
+ /* this.pluralname$value.add(singularnameValue); */ {
+ FieldReference thisDotValueField = new FieldReference(valueFieldName, 0L);
+ thisDotValueField.receiver = new ThisReference(0, 0);
+ MessageSend thisDotValueFieldDotAdd = new MessageSend();
+ thisDotValueFieldDotAdd.arguments = new Expression[] {new SingleNameReference(valueParamName, 0L)};
+ thisDotValueFieldDotAdd.receiver = thisDotValueField;
+ thisDotValueFieldDotAdd.selector = "add".toCharArray();
+ statements.add(thisDotValueFieldDotAdd);
+ }
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+ TypeReference keyParamType = cloneParamType(0, data.getTypeArgs(), builderType);
+ Argument keyParam = new Argument(keyParamName, 0, keyParamType, 0);
+ TypeReference valueParamType = cloneParamType(1, data.getTypeArgs(), builderType);
+ Argument valueParam = new Argument(valueParamName, 0, valueParamType, 0);
+ md.arguments = new Argument[] {keyParam, valueParam};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("put", new String(data.getSingularName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ private void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult);
+ md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ md.modifiers = ClassFileConstants.AccPublic;
+
+ String pN = new String(data.getPluralName());
+ char[] keyFieldName = (pN + "$key").toCharArray();
+ char[] valueFieldName = (pN + "$value").toCharArray();
+
+ List<Statement> statements = new ArrayList<Statement>();
+ statements.add(createConstructBuilderVarIfNeeded(data, builderType, true));
+
+ char[] entryName = "$lombokEntry".toCharArray();
+
+ TypeReference forEachType = new QualifiedTypeReference(JAVA_UTIL_MAP_ENTRY, NULL_POSS);
+ forEachType = addTypeArgs(2, true, builderType, forEachType, data.getTypeArgs());
+
+ MessageSend keyArg = new MessageSend();
+ keyArg.receiver = new SingleNameReference(entryName, 0L);
+ keyArg.selector = "getKey".toCharArray();
+ MessageSend addKey = new MessageSend();
+ FieldReference thisDotKeyField = new FieldReference(keyFieldName, 0L);
+ thisDotKeyField.receiver = new ThisReference(0, 0);
+ addKey.receiver = thisDotKeyField;
+ addKey.selector = new char[] {'a', 'd', 'd'};
+ addKey.arguments = new Expression[] {keyArg};
+
+ MessageSend valueArg = new MessageSend();
+ valueArg.receiver = new SingleNameReference(entryName, 0L);
+ valueArg.selector = "getValue".toCharArray();
+ MessageSend addValue = new MessageSend();
+ FieldReference thisDotValueField = new FieldReference(valueFieldName, 0L);
+ thisDotValueField.receiver = new ThisReference(0, 0);
+ addValue.receiver = thisDotValueField;
+ addValue.selector = new char[] {'a', 'd', 'd'};
+ addValue.arguments = new Expression[] {valueArg};
+
+ LocalDeclaration elementVariable = new LocalDeclaration(entryName, 0, 0);
+ elementVariable.type = forEachType;
+ ForeachStatement forEach = new ForeachStatement(elementVariable, 0);
+ MessageSend invokeEntrySet = new MessageSend();
+ invokeEntrySet.selector = new char[] { 'e', 'n', 't', 'r', 'y', 'S', 'e', 't'};
+ invokeEntrySet.receiver = new SingleNameReference(data.getPluralName(), 0L);
+ forEach.collection = invokeEntrySet;
+ Block forEachContent = new Block(0);
+ forEachContent.statements = new Statement[] {addKey, addValue};
+ forEach.action = forEachContent;
+ statements.add(forEach);
+ if (returnStatement != null) statements.add(returnStatement);
+
+ md.statements = statements.toArray(new Statement[statements.size()]);
+
+ TypeReference paramType = new QualifiedTypeReference(JAVA_UTIL_MAP, NULL_POSS);
+ paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs());
+ Argument param = new Argument(data.getPluralName(), 0, paramType, 0);
+ md.arguments = new Argument[] {param};
+ md.returnType = returnType;
+ md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("putAll", new String(data.getPluralName())).toCharArray();
+ md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null;
+
+ data.setGeneratedByRecursive(md);
+ injectMethod(builderType, md);
+ }
+
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ return;
+ }
+
+ if (data.getTargetFqn().equals("java.util.Map")) {
+ statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap"));
+ } else {
+ statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap"));
+ }
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
new file mode 100644
index 00000000..2d16eae0
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import java.util.List;
+
+import lombok.core.LombokImmutableList;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(EclipseSingularizer.class)
+public class EclipseJavaUtilSetSingularizer extends EclipseJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet");
+ }
+
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ return;
+ }
+
+ if (data.getTargetFqn().equals("java.util.Set")) {
+ statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, false, "emptySet", "singleton", "LinkedHashSet"));
+ } else {
+ statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, false, true, false, true, "TreeSet"));
+ }
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
new file mode 100644
index 00000000..6661f4af
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers.singulars;
+
+import static lombok.eclipse.Eclipse.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
+import org.eclipse.jdt.internal.compiler.ast.Block;
+import org.eclipse.jdt.internal.compiler.ast.BreakStatement;
+import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
+import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
+import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.ForStatement;
+import org.eclipse.jdt.internal.compiler.ast.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
+import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
+import org.eclipse.jdt.internal.compiler.ast.PostfixExpression;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+
+import lombok.ConfigurationKeys;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+
+abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
+ protected static final char[][] JAVA_UTIL_ARRAYLIST = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'A', 'r', 'r', 'a', 'y', 'L', 'i', 's', 't'}
+ };
+
+ protected static final char[][] JAVA_UTIL_LIST = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'L', 'i', 's', 't'}
+ };
+
+ protected static final char[][] JAVA_UTIL_MAP = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'}
+ };
+
+ protected static final char[][] JAVA_UTIL_MAP_ENTRY = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'M', 'a', 'p'}, {'E', 'n', 't', 'r', 'y'}
+ };
+
+ protected static final char[][] JAVA_UTIL_COLLECTIONS = {
+ {'j', 'a', 'v', 'a'}, {'u', 't', 'i', 'l'}, {'C', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', 's'}
+ };
+
+ protected final EclipseSingularizer guavaListSetSingularizer = new EclipseGuavaSetListSingularizer();
+ protected final EclipseSingularizer guavaMapSingularizer = new EclipseGuavaMapSingularizer();
+
+ protected boolean useGuavaInstead(EclipseNode node) {
+ return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA));
+ }
+
+ protected List<Statement> createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType) {
+ List<Statement> switchContents = new ArrayList<Statement>();
+ char[] keyName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName();
+
+ if (emptyCollectionMethod != null) { // case 0: (empty); break;
+ switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'0'}, null), 0, 0));
+
+ /* pluralName = java.util.Collections.emptyCollectionMethod(); */ {
+ MessageSend invoke = new MessageSend();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ invoke.selector = emptyCollectionMethod.toCharArray();
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0));
+ }
+
+ switchContents.add(new BreakStatement(null, 0, 0));
+ }
+
+ if (singletonCollectionMethod != null) { // case 1: (singleton); break;
+ switchContents.add(new CaseStatement(makeIntLiteral(new char[] {'1'}, null), 0, 0));
+ /* !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0));
+ mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0)); */ {
+ FieldReference thisDotKey = new FieldReference(keyName, 0L);
+ thisDotKey.receiver = new ThisReference(0, 0);
+ MessageSend thisDotKeyGet0 = new MessageSend();
+ thisDotKeyGet0.receiver = thisDotKey;
+ thisDotKeyGet0.selector = new char[] {'g', 'e', 't'};
+ thisDotKeyGet0.arguments = new Expression[] {makeIntLiteral(new char[] {'0'}, null)};
+
+ Expression[] args;
+ if (mapMode) {
+ char[] valueName = (new String(data.getPluralName()) + "$value").toCharArray();
+ FieldReference thisDotValue = new FieldReference(valueName, 0L);
+ thisDotValue.receiver = new ThisReference(0, 0);
+ MessageSend thisDotValueGet0 = new MessageSend();
+ thisDotValueGet0.receiver = thisDotValue;
+ thisDotValueGet0.selector = new char[] {'g', 'e', 't'};
+ thisDotValueGet0.arguments = new Expression[] {makeIntLiteral(new char[] {'0'}, null)};
+ args = new Expression[] {thisDotKeyGet0, thisDotValueGet0};
+ } else {
+ args = new Expression[] {thisDotKeyGet0};
+ }
+
+ MessageSend invoke = new MessageSend();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ invoke.selector = singletonCollectionMethod.toCharArray();
+ invoke.arguments = args;
+ switchContents.add(new Assignment(new SingleNameReference(data.getPluralName(), 0), invoke, 0));
+ }
+ switchContents.add(new BreakStatement(null, 0, 0));
+ }
+
+ { // default:
+ switchContents.add(new CaseStatement(null, 0, 0));
+ switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType));
+ }
+
+ SwitchStatement switchStat = new SwitchStatement();
+ switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]);
+ switchStat.expression = getSize(builderType, keyName, true);
+
+ TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs());
+ LocalDeclaration varDefStat = new LocalDeclaration(data.getPluralName(), 0, 0);
+ varDefStat.type = localShadowerType;
+ return Arrays.asList(varDefStat, switchStat);
+ }
+
+ protected List<Statement> createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType) {
+ char[] varName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName();
+
+ Statement createStat; {
+ // pluralName = new java.util.TargetType(initialCap);
+ Expression[] constructorArgs = null;
+ if (addInitialCapacityArg) {
+ // this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE;
+ // lessThanCutOff = this.varName.size() < MAX_POWER_OF_2
+ Expression lessThanCutoff = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral("0x40000000".toCharArray(), null), OperatorIds.LESS);
+ FieldReference integerMaxValue = new FieldReference("MAX_VALUE".toCharArray(), 0L);
+ integerMaxValue.receiver = new QualifiedNameReference(TypeConstants.JAVA_LANG_INTEGER, NULL_POSS, 0, 0);
+ Expression sizeFormulaLeft = new BinaryExpression(makeIntLiteral(new char[] {'1'}, null), getSize(builderType, varName, nullGuard), OperatorIds.PLUS);
+ Expression sizeFormulaRightLeft = new BinaryExpression(getSize(builderType, varName, nullGuard), makeIntLiteral(new char[] {'3'}, null), OperatorIds.MINUS);
+ Expression sizeFormulaRight = new BinaryExpression(sizeFormulaRightLeft, makeIntLiteral(new char[] {'3'}, null), OperatorIds.DIVIDE);
+ Expression sizeFormula = new BinaryExpression(sizeFormulaLeft, sizeFormulaRight, OperatorIds.PLUS);
+ Expression cond = new ConditionalExpression(lessThanCutoff, sizeFormula, integerMaxValue);
+ constructorArgs = new Expression[] {cond};
+ }
+
+ TypeReference targetTypeRef = new QualifiedTypeReference(new char[][] {TypeConstants.JAVA, TypeConstants.UTIL, targetType.toCharArray()}, NULL_POSS);
+ targetTypeRef = addTypeArgs(mapMode ? 2 : 1, false, builderType, targetTypeRef, data.getTypeArgs());
+ AllocationExpression constructorCall = new AllocationExpression();
+ constructorCall.type = targetTypeRef;
+ constructorCall.arguments = constructorArgs;
+
+ if (defineVar) {
+ TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs());
+ LocalDeclaration localShadowerDecl = new LocalDeclaration(data.getPluralName(), 0, 0);
+ localShadowerDecl.type = localShadowerType;
+ localShadowerDecl.initialization = constructorCall;
+ createStat = localShadowerDecl;
+ } else {
+ createStat = new Assignment(new SingleNameReference(data.getPluralName(), 0L), constructorCall, 0);
+ }
+ }
+
+ Statement fillStat; {
+ if (mapMode) {
+ // for (int $i = 0; $i < this.pluralname$key.size(); i++) pluralname.put(this.pluralname$key.get($i), this.pluralname$value.get($i));
+ char[] iVar = new char[] {'$', 'i'};
+ MessageSend pluralnameDotPut = new MessageSend();
+ pluralnameDotPut.selector = new char[] {'p', 'u', 't'};
+ pluralnameDotPut.receiver = new SingleNameReference(data.getPluralName(), 0L);
+ FieldReference thisDotKey = new FieldReference(varName, 0L);
+ thisDotKey.receiver = new ThisReference(0, 0);
+ FieldReference thisDotValue = new FieldReference((new String(data.getPluralName()) + "$value").toCharArray(), 0L);
+ thisDotValue.receiver = new ThisReference(0, 0);
+ MessageSend keyArg = new MessageSend();
+ keyArg.receiver = thisDotKey;
+ keyArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)};
+ keyArg.selector = new char[] {'g', 'e', 't'};
+ MessageSend valueArg = new MessageSend();
+ valueArg.receiver = thisDotValue;
+ valueArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)};
+ valueArg.selector = new char[] {'g', 'e', 't'};
+ pluralnameDotPut.arguments = new Expression[] {keyArg, valueArg};
+
+ LocalDeclaration forInit = new LocalDeclaration(iVar, 0, 0);
+ forInit.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
+ forInit.initialization = makeIntLiteral(new char[] {'0'}, null);
+ Expression checkExpr = new BinaryExpression(new SingleNameReference(iVar, 0L), getSize(builderType, varName, nullGuard), OperatorIds.LESS);
+ Expression incrementExpr = new PostfixExpression(new SingleNameReference(iVar, 0L), IntLiteral.One, OperatorIds.PLUS, 0);
+ fillStat = new ForStatement(new Statement[] {forInit}, checkExpr, new Statement[] {incrementExpr}, pluralnameDotPut, true, 0, 0);
+ } else {
+ // pluralname.addAll(this.pluralname);
+ MessageSend pluralnameDotAddAll = new MessageSend();
+ pluralnameDotAddAll.selector = new char[] {'a', 'd', 'd', 'A', 'l', 'l'};
+ pluralnameDotAddAll.receiver = new SingleNameReference(data.getPluralName(), 0L);
+ FieldReference thisDotPluralname = new FieldReference(varName, 0L);
+ thisDotPluralname.receiver = new ThisReference(0, 0);
+ pluralnameDotAddAll.arguments = new Expression[] {thisDotPluralname};
+ fillStat = pluralnameDotAddAll;
+ }
+
+ if (nullGuard) {
+ FieldReference thisDotField = new FieldReference(varName, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL);
+ fillStat = new IfStatement(cond, fillStat, 0, 0);
+ }
+ }
+
+ Statement unmodifiableStat; {
+ // pluralname = Collections.unmodifiableInterfaceType(pluralname);
+ Expression arg = new SingleNameReference(data.getPluralName(), 0L);
+ MessageSend invoke = new MessageSend();
+ invoke.arguments = new Expression[] {arg};
+ invoke.selector = ("unmodifiable" + data.getTargetSimpleType()).toCharArray();
+ invoke.receiver = new QualifiedNameReference(JAVA_UTIL_COLLECTIONS, NULL_POSS, 0, 0);
+ unmodifiableStat = new Assignment(new SingleNameReference(data.getPluralName(), 0L), invoke, 0);
+ }
+
+ return Arrays.asList(createStat, fillStat, unmodifiableStat);
+ }
+
+ protected Statement createConstructBuilderVarIfNeeded(SingularData data, EclipseNode builderType, boolean mapMode) {
+ char[] v1Name, v2Name;
+ if (mapMode) {
+ String n = new String(data.getPluralName());
+ v1Name = (n + "$key").toCharArray();
+ v2Name = (n + "$value").toCharArray();
+ } else {
+ v1Name = data.getPluralName();
+ v2Name = null;
+ }
+
+ FieldReference thisDotField = new FieldReference(v1Name, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
+
+ thisDotField = new FieldReference(v1Name, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ TypeReference v1Type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ v1Type = addTypeArgs(1, false, builderType, v1Type, data.getTypeArgs());
+ AllocationExpression constructArrayList = new AllocationExpression();
+ constructArrayList.type = v1Type;
+ Assignment initV1 = new Assignment(thisDotField, constructArrayList, 0);
+ Statement thenPart;
+ if (mapMode) {
+ thisDotField = new FieldReference(v2Name, 0L);
+ thisDotField.receiver = new ThisReference(0, 0);
+ TypeReference v2Type = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
+ List<TypeReference> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.size() > 1) tArgs = Collections.singletonList(tArgs.get(1));
+ else tArgs = Collections.emptyList();
+ v2Type = addTypeArgs(1, false, builderType, v2Type, tArgs);
+ constructArrayList = new AllocationExpression();
+ constructArrayList.type = v2Type;
+ Assignment initV2 = new Assignment(thisDotField, constructArrayList, 0);
+ Block b = new Block(0);
+ b.statements = new Statement[] {initV1, initV2};
+ thenPart = b;
+ } else {
+ thenPart = initV1;
+ }
+
+ return new IfStatement(cond, thenPart, 0, 0);
+ }
+}
diff --git a/src/core/lombok/experimental/Accessors.java b/src/core/lombok/experimental/Accessors.java
index c2a0ca16..dc9ae4b0 100644
--- a/src/core/lombok/experimental/Accessors.java
+++ b/src/core/lombok/experimental/Accessors.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 The Project Lombok Authors.
+ * Copyright (C) 2012-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* A container for settings for the generation of getters and setters.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Accessors.html">the project lombok features page for &#64;Accessors</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Accessors">the project lombok features page for &#64;Accessors</a>.
* <p>
* Using this annotation does nothing by itself; an annotation that makes lombok generate getters and setters,
* such as {@link lombok.Setter} or {@link lombok.Data} is also required.
@@ -38,15 +38,19 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
public @interface Accessors {
/**
- * If true, accessors will be named after the field and not include a <code>get</code> or <code>set</code>
- * prefix. If true and <code>chain</code> is omitted, <code>chain</code> defaults to <code>true</code>.
+ * If true, accessors will be named after the field and not include a {@code get} or {@code set}
+ * prefix. If true and {@code chain} is omitted, {@code chain} defaults to {@code true}.
* <strong>default: false</strong>
+ *
+ * @return Whether or not to make fluent methods (named {@code fieldName()}, not for example {@code setFieldName}).
*/
boolean fluent() default false;
/**
- * If true, setters return <code>this</code> instead of <code>void</code>.
- * <strong>default: false</strong>, unless <code>fluent=true</code>, then <strong>default: true</code>
+ * If true, setters return {@code this} instead of {@code void}.
+ * <strong>default: false</strong>, unless {@code fluent=true}, then <strong>default: true</strong>
+ *
+ * @return Whether or not setters should return themselves (chaining) or {@code void} (no chaining).
*/
boolean chain() default false;
@@ -55,6 +59,8 @@ public @interface Accessors {
* Note that a prefix only counts if the next character is NOT a lowercase character or the last
* letter of the prefix is not a letter (for instance an underscore). If multiple fields
* all turn into the same name when the prefix is stripped, an error will be generated.
+ *
+ * @return If you are in the habit of prefixing your fields (for example, you name them {@code fFieldName}, specify such prefixes here).
*/
String[] prefix() default {};
}
diff --git a/src/core/lombok/experimental/Delegate.java b/src/core/lombok/experimental/Delegate.java
index 806d5871..cc844526 100644
--- a/src/core/lombok/experimental/Delegate.java
+++ b/src/core/lombok/experimental/Delegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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,7 +40,7 @@ import java.lang.annotation.Target;
* that exist in {@link Object}, the {@code canEqual(Object)} method, and any methods that appear in types
* that are listed in the {@code excludes} property.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Delegate.html">the project lombok features page for &#64;Delegate</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Delegate">the project lombok features page for &#64;Delegate</a>.
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@@ -52,6 +52,8 @@ public @interface Delegate {
* type listed here is used only to determine which delegate methods to generate.
*
* NB: All methods in {@code Object}, as well as {@code canEqual(Object other)} will never be delegated.
+ *
+ * @return For each method (not already in {@code java.lang.Object}) in these types, generate a delegate method.
*/
Class<?>[] types() default {};
@@ -59,6 +61,8 @@ public @interface Delegate {
* Each method in any of the types listed here (include supertypes) will <em>not</em> be delegated.
*
* NB: All methods in {@code Object}, as well as {@code canEqual(Object other)} will never be delegated.
+ *
+ * @return For each method (not already in {@code java.lang.Object}) in these types, skip generating a delegate method (overrides {@code types()}).
*/
Class<?>[] excludes() default {};
}
diff --git a/src/core/lombok/experimental/ExtensionMethod.java b/src/core/lombok/experimental/ExtensionMethod.java
index 7de8a136..af38f6ed 100644
--- a/src/core/lombok/experimental/ExtensionMethod.java
+++ b/src/core/lombok/experimental/ExtensionMethod.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 The Project Lombok Authors.
+ * Copyright (C) 2012-2017 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
@@ -31,8 +31,8 @@ import java.lang.annotation.*;
* otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as
* if they were instance methods on the extended type.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/ExtensionMethod.html">the project lombok features page for &#64;ExtensionMethod</a>.
- * <p>
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/ExtensionMethod">the project lombok features page for &#64;ExtensionMethod</a>.
+ * <br>
* <p>
* Before:
*
@@ -60,12 +60,14 @@ import java.lang.annotation.*;
@Target(TYPE)
@Retention(SOURCE)
public @interface ExtensionMethod {
- /** All types whose static methods will be exposed as extension methods. */
+ /** @return All types whose static methods will be exposed as extension methods. */
Class<?>[] value();
/**
* If {@code true}, an applicable extension method is used (if found) even if the method call already was compilable (this is the default).
* If {@code false}, an extension method is only used if the method call is not also defined by the type itself.
+ *
+ * @return Whether or not to override already existing methods with the extension.
*/
boolean suppressBaseMethods() default true;
}
diff --git a/src/core/lombok/experimental/FieldDefaults.java b/src/core/lombok/experimental/FieldDefaults.java
index 1c621f3c..5c387bb6 100644
--- a/src/core/lombok/experimental/FieldDefaults.java
+++ b/src/core/lombok/experimental/FieldDefaults.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 The Project Lombok Authors.
+ * Copyright (C) 2012-2017 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
@@ -31,11 +31,11 @@ import lombok.AccessLevel;
/**
* Adds modifiers to each field in the type with this annotation.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/FieldDefaults.html">the project lombok features page for &#64;FieldDefaults</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/FieldDefaults">the project lombok features page for &#64;FieldDefaults</a>.
* <p>
- * If {@code makeFinal} is {@code true}, then each field that is not annotated with {@code @NonFinal} will have the {@code final} modifier added.
+ * If {@code makeFinal} is {@code true}, then each (instance) field that is not annotated with {@code @NonFinal} will have the {@code final} modifier added.
* <p>
- * If {@code level} is set, then each field that is package private (i.e. no access modifier) and does not have the {@code @PackagePrivate} annotation will
+ * If {@code level} is set, then each (instance) field that is package private (i.e. no access modifier) and does not have the {@code @PackagePrivate} annotation will
* have the appropriate access level modifier added.
*/
@Target(ElementType.TYPE)
diff --git a/src/core/lombok/experimental/Value.java b/src/core/lombok/experimental/Helper.java
index b7700bb5..34745cbe 100644
--- a/src/core/lombok/experimental/Value.java
+++ b/src/core/lombok/experimental/Helper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 The Project Lombok Authors.
+ * Copyright (C) 2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -27,34 +27,9 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Generates a lot of code which fits with a class that is a representation of an immutable entity.
- * <p>
- * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
- * <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
- *
- * @see lombok.Getter
- * @see Wither
- * @see lombok.RequiredArgsConstructor
- * @see lombok.ToString
- * @see lombok.EqualsAndHashCode
- * @see lombok.Data
- * @deprecated {@link lombok.Value} has been promoted to the main package, so use that one instead.
+ * Use on a method local class to indicate that all methods inside should be exposed to the rest of
+ * the method as if they were helper methods.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
-@Deprecated
-public @interface Value {
- /**
- * If you specify a static constructor name, then the generated constructor will be private, and
- * instead a static factory method is created that other classes can use to create instances.
- * We suggest the name: "of", like so:
- *
- * <pre>
- * public @Data(staticConstructor = "of") class Point { final int x, y; }
- * </pre>
- *
- * Default: No static constructor, instead the normal constructor is public.
- */
- String staticConstructor() default "";
-}
+public @interface Helper {}
diff --git a/src/core/lombok/experimental/NonFinal.java b/src/core/lombok/experimental/NonFinal.java
index 0c31dd2a..12a45d22 100644
--- a/src/core/lombok/experimental/NonFinal.java
+++ b/src/core/lombok/experimental/NonFinal.java
@@ -28,7 +28,7 @@ import java.lang.annotation.Target;
/**
* Used to indicate the explicit intention for the annotated entity to <em>not</em> be {@code final}.
- * Currently used by {@code FieldDefaults} to avoid having it make a field final.
+ * Currently used by {@code FieldDefaults} and {@code Value} to avoid having it make a field final.
*/
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
diff --git a/src/core/lombok/experimental/PackagePrivate.java b/src/core/lombok/experimental/PackagePrivate.java
index bfe5638b..42002818 100644
--- a/src/core/lombok/experimental/PackagePrivate.java
+++ b/src/core/lombok/experimental/PackagePrivate.java
@@ -28,7 +28,7 @@ import java.lang.annotation.Target;
/**
* Used to indicate the explicit intention for the annotated entity to have the <em>package private</em> access level.
- * Currently used by {@code FieldDefaults} to avoid having it make a field one of {@code public}, {@code protected}, or {@code private}.
+ * Currently used by {@code FieldDefaults} and {@code Value} to avoid having it make a field one of {@code public}, {@code protected}, or {@code private}.
*/
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.SOURCE)
diff --git a/src/core/lombok/experimental/UtilityClass.java b/src/core/lombok/experimental/UtilityClass.java
new file mode 100644
index 00000000..80f3eee9
--- /dev/null
+++ b/src/core/lombok/experimental/UtilityClass.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.experimental;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation to create utility classes.
+ *
+ * If a class is annotated with {@code @UtilityClass}, the following things happen to it:<ul>
+ * <li>It is marked final.</li>
+ * <li>If any constructors are declared in it, an error is generated. Otherwise, a private no-args constructor is generated; it throws a {@code UnsupportedOperationException}.</li>
+ * <li>All methods, inner classes, and fields in the class are marked static.</li>
+ * </ul>
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface UtilityClass {
+}
diff --git a/src/core/lombok/experimental/Wither.java b/src/core/lombok/experimental/Wither.java
index fe113bb0..3df546d0 100644
--- a/src/core/lombok/experimental/Wither.java
+++ b/src/core/lombok/experimental/Wither.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 The Project Lombok Authors.
+ * Copyright (C) 2012-2017 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
@@ -31,7 +31,7 @@ import lombok.AccessLevel;
/**
* Put on any field to make lombok build a 'wither' - a withX method which produces a clone of this object (except for 1 field which gets a new value).
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Wither.html">the project lombok features page for &#64;Wither</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Wither">the project lombok features page for &#64;Wither</a>.
* <p>
* Even though it is not listed, this annotation also has the {@code onParam} and {@code onMethod} parameter. See the full documentation for more details.
* <p>
@@ -56,16 +56,32 @@ import lombok.AccessLevel;
public @interface Wither {
/**
* If you want your wither to be non-public, you can specify an alternate access level here.
+ *
+ * @return The method will be generated with this access modifier.
*/
AccessLevel value() default AccessLevel.PUBLIC;
/**
- * Any annotations listed here are put on the generated method. The syntax for this feature is: {@code @Setter(onMethod=@__({@AnnotationsGoHere}))}
+ * Any annotations listed here are put on the generated method.
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @Wither(onMethod=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @Wither(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}.
+ *
+ * @return List of annotations to apply to the generated method.
*/
AnyAnnotation[] onMethod() default {};
/**
- * Any annotations listed here are put on the generated method's parameter. The syntax for this feature is: {@code @Setter(onParam=@__({@AnnotationsGoHere}))}
+ * Any annotations listed here are put on the generated method's parameter.
+ * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br>
+ * up to JDK7:<br>
+ * {@code @Wither(onParam=@__({@AnnotationsGoHere}))}<br>
+ * from JDK8:<br>
+ * {@code @Wither(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}.
+ *
+ * @return List of annotations to apply to the generated parameter in the method.
*/
AnyAnnotation[] onParam() default {};
diff --git a/src/core/lombok/experimental/package-info.java b/src/core/lombok/experimental/package-info.java
index 776f2c27..22b23a52 100644
--- a/src/core/lombok/experimental/package-info.java
+++ b/src/core/lombok/experimental/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,6 +28,6 @@
* to the official feature documentation.
*
* @see lombok
- * @see <a href="http://projectlombok.org/features/experimental/index.html">Lombok features (experimental)</a>
+ * @see <a href="https://projectlombok.org/features/experimental/all">Lombok features (experimental)</a>
*/
package lombok.experimental;
diff --git a/src/core/lombok/experimental/var.java b/src/core/lombok/experimental/var.java
new file mode 100644
index 00000000..71cc141a
--- /dev/null
+++ b/src/core/lombok/experimental/var.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.experimental;
+
+/**
+ * like val but not final
+ *
+ * @deprecated {@code var} has been promoted to the main package; use {@link lombok.var} instead.
+ */
+@Deprecated
+public @interface var {
+}
diff --git a/src/core/lombok/extern/apachecommons/CommonsLog.java b/src/core/lombok/extern/apachecommons/CommonsLog.java
index 45345098..04d5ef93 100644
--- a/src/core/lombok/extern/apachecommons/CommonsLog.java
+++ b/src/core/lombok/extern/apachecommons/CommonsLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
@@ -46,21 +46,20 @@ import java.lang.annotation.Target;
* }
* </pre>
*
- * This annotation is valid for classes and enumerations.<br />
+ * This annotation is valid for classes and enumerations.<br>
*
- * @see org.apache.commons.logging.Log org.apache.commons.logging.Log
- * @see org.apache.commons.logging.LogFactory#getLog(java.lang.Class) org.apache.commons.logging.LogFactory.getLog(Class target)
+ * @see <a href="https://commons.apache.org/proper/commons-logging/apidocs/org/apache/commons/logging/Log.html">org.apache.commons.logging.Log</a>
+ * @see <a href="https://commons.apache.org/proper/commons-logging/apidocs/org/apache/commons/logging/LogFactory.html#getLog(java.lang.Class)">org.apache.commons.logging.LogFactory#getLog(java.lang.Class)</a>
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface CommonsLog {
- /**
- * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed.
- */
+ /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */
String topic() default "";
}
diff --git a/src/core/lombok/extern/java/Log.java b/src/core/lombok/extern/java/Log.java
index bac2742e..553b7c4a 100644
--- a/src/core/lombok/extern/java/Log.java
+++ b/src/core/lombok/extern/java/Log.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
@@ -46,20 +46,19 @@ import java.lang.annotation.Target;
* }
* </pre>
*
- * This annotation is valid for classes and enumerations.<br />
- * @see java.util.logging.Logger java.util.logging.Logger
- * @see java.util.logging.Logger#getLogger(java.lang.String) java.util.logging.Logger.getLogger(String name)
+ * This annotation is valid for classes and enumerations.<br>
+ * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html">java.util.logging.Logger</a>
+ * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#getLogger-java.lang.String-">java.util.logging.Logger#getLogger(java.lang.String)</a>
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Log {
- /**
- * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed.
- */
+ /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */
String topic() default "";
}
diff --git a/src/core/lombok/extern/jbosslog/JBossLog.java b/src/core/lombok/extern/jbosslog/JBossLog.java
new file mode 100644
index 00000000..ea520aea
--- /dev/null
+++ b/src/core/lombok/extern/jbosslog/JBossLog.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.extern.jbosslog;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Causes lombok to generate a logger field.
+ * <p>
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
+ * <p>
+ * Example:
+ * <pre>
+ * &#64;JBossLog
+ * public class LogExample {
+ * }
+ * </pre>
+ *
+ * will generate:
+ *
+ * <pre>
+ * public class LogExample {
+ * private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
+ * }
+ * </pre>
+ *
+ * This annotation is valid for classes and enumerations.<br>
+ * @see <a href="https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html">org.jboss.logging.Logger</a>
+ * @see <a href="https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html#getLogger(java.lang.Class)">org.jboss.logging.Logger#getLogger(java.lang.Class)</a>
+ * @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
+ * @see lombok.extern.java.Log &#64;Log
+ * @see lombok.extern.log4j.Log4j &#64;Log4j
+ * @see lombok.extern.log4j.Log4j2 &#64;Log4j2
+ * @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
+ * */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface JBossLog {
+ /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */
+ String topic() default "";
+}
diff --git a/src/core/lombok/extern/log4j/Log4j.java b/src/core/lombok/extern/log4j/Log4j.java
index 9490acb8..ee719407 100644
--- a/src/core/lombok/extern/log4j/Log4j.java
+++ b/src/core/lombok/extern/log4j/Log4j.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
@@ -46,21 +46,20 @@ import java.lang.annotation.Target;
* }
* </pre>
*
- * This annotation is valid for classes and enumerations.<br />
+ * This annotation is valid for classes and enumerations.<br>
*
- * @see org.apache.log4j.Logger org.apache.log4j.Logger
- * @see org.apache.log4j.Logger#getLogger(java.lang.Class) org.apache.log4j.Logger.getLogger(Class target)
+ * @see <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html">org.apache.log4j.Logger</a>
+ * @see <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html#getLogger(java.lang.Class)">org.apache.log4j.Logger#getLogger(java.lang.Class)</a>
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Log4j {
- /**
- * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed.
- */
+ /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */
String topic() default "";
}
diff --git a/src/core/lombok/extern/log4j/Log4j2.java b/src/core/lombok/extern/log4j/Log4j2.java
index 43125e6b..4a5b166c 100644
--- a/src/core/lombok/extern/log4j/Log4j2.java
+++ b/src/core/lombok/extern/log4j/Log4j2.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Project Lombok Authors.
+ * Copyright (C) 2013-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
@@ -42,25 +42,24 @@ import java.lang.annotation.Target;
*
* <pre>
* public class LogExample {
- * private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.Logger.getLogger(LogExample.class);
+ * private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
* }
* </pre>
*
- * This annotation is valid for classes and enumerations.<br />
+ * This annotation is valid for classes and enumerations.<br>
*
- * @see org.apache.logging.log4j.Logger org.apache.logging.log4j.Logger
- * @see org.apache.logging.log4j.LogManager#getLogger(java.lang.Class) org.apache.logging.log4j.LogManager.getLogger(Class target)
+ * @see <a href="https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html">org.apache.logging.log4j.Logger</a>
+ * @see <a href="https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogManager.html#getLogger-java.lang.Class-">org.apache.logging.log4j.LogManager#getLogger(java.lang.Class)</a>
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Log4j2 {
- /**
- * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed.
- */
+ /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */
String topic() default "";
}
diff --git a/src/core/lombok/extern/slf4j/Slf4j.java b/src/core/lombok/extern/slf4j/Slf4j.java
index 04df6498..24586d43 100644
--- a/src/core/lombok/extern/slf4j/Slf4j.java
+++ b/src/core/lombok/extern/slf4j/Slf4j.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
@@ -46,21 +46,20 @@ import java.lang.annotation.Target;
* }
* </pre>
*
- * This annotation is valid for classes and enumerations.<br />
- * @see org.slf4j.Logger org.slf4j.Logger
- * @see org.slf4j.LoggerFactory#getLogger(java.lang.Class) org.slf4j.LoggerFactory.getLogger(Class target)
+ * This annotation is valid for classes and enumerations.<br>
+ * @see <a href="https://www.slf4j.org/api/org/slf4j/Logger.html">org.slf4j.Logger</a>
+ * @see <a href="https://www.slf4j.org/api/org/slf4j/LoggerFactory.html#getLogger(java.lang.Class)">org.slf4j.LoggerFactory#getLogger(java.lang.Class)</a>
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
- * */
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
+ */
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Slf4j {
- /**
- * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed.
- */
+ /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */
String topic() default "";
}
diff --git a/src/core/lombok/extern/slf4j/XSlf4j.java b/src/core/lombok/extern/slf4j/XSlf4j.java
index 8a311c79..85a0fdd8 100644
--- a/src/core/lombok/extern/slf4j/XSlf4j.java
+++ b/src/core/lombok/extern/slf4j/XSlf4j.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 The Project Lombok Authors.
+ * Copyright (C) 2012-2017 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
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
@@ -46,20 +46,19 @@ import java.lang.annotation.Target;
* }
* </pre>
*
- * This annotation is valid for classes and enumerations.<br />
- * @see org.slf4j.ext.XLogger org.slf4j.ext.XLogger
- * @see org.slf4j.ext.XLoggerFactory#getLogger(java.lang.Class) org.slf4j.ext.XLoggerFactory.getXLogger(Class target)
+ * This annotation is valid for classes and enumerations.<br>
+ * @see <a href="https://www.slf4j.org/api/org/slf4j/ext/XLogger.html">org.slf4j.ext.XLogger</a>
+ * @see <a href="https://www.slf4j.org/api/org/slf4j/ext/XLoggerFactory.html#getXLogger(java.lang.Class)">org.slf4j.ext.XLoggerFactory#getLogger(java.lang.Class)</a>
* @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
* @see lombok.extern.java.Log &#64;Log
* @see lombok.extern.log4j.Log4j &#64;Log4j
* @see lombok.extern.log4j.Log4j2 &#64;Log4j2
* @see lombok.extern.slf4j.Slf4j &#64;Slf4j
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface XSlf4j {
- /**
- * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed.
- */
+ /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */
String topic() default "";
}
diff --git a/src/core/lombok/javac/CapturingDiagnosticListener.java b/src/core/lombok/javac/CapturingDiagnosticListener.java
index a0ac6adc..0e64ed8d 100644
--- a/src/core/lombok/javac/CapturingDiagnosticListener.java
+++ b/src/core/lombok/javac/CapturingDiagnosticListener.java
@@ -52,6 +52,10 @@ public class CapturingDiagnosticListener implements DiagnosticListener<JavaFileO
"^" + Pattern.quote(file.getAbsolutePath()) +
"\\s*:\\s*\\d+\\s*:\\s*(?:warning:\\s*)?(.*)$", Pattern.DOTALL).matcher(msg);
if (m.matches()) msg = m.group(1);
+ if (msg.equals("deprecated item is not annotated with @Deprecated")) {
+ // This is new in JDK9; prior to that you don't see this. We shall ignore these.
+ return;
+ }
messages.add(new CompilerMessage(d.getLineNumber(), d.getStartPosition(), d.getKind() == Kind.ERROR, msg));
}
diff --git a/src/core/lombok/javac/CompilerMessageSuppressor.java b/src/core/lombok/javac/CompilerMessageSuppressor.java
index a17e0c62..391ec64a 100644
--- a/src/core/lombok/javac/CompilerMessageSuppressor.java
+++ b/src/core/lombok/javac/CompilerMessageSuppressor.java
@@ -26,6 +26,7 @@ import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.LinkedList;
+import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -43,38 +44,40 @@ import com.sun.tools.javac.util.Log;
* then they will be generated AGAIN, this time with proper names and line numbers, at the end. Therefore, we want to suppress the logger.
*/
public final class CompilerMessageSuppressor {
+
private final Log log;
- private static final Field errWriterField, warnWriterField, noticeWriterField, dumpOnErrorField, promptOnErrorField, diagnosticListenerField;
+ private static final WriterField errWriterField, warnWriterField, noticeWriterField;
+ private static final Field dumpOnErrorField, promptOnErrorField, diagnosticListenerField;
private static final Field deferDiagnosticsField, deferredDiagnosticsField, diagnosticHandlerField;
private static final ConcurrentMap<Class<?>, Field> handlerDeferredFields = new ConcurrentHashMap<Class<?>, Field>();
private static final Field NULL_FIELD;
- private PrintWriter errWriter, warnWriter, noticeWriter;
private Boolean dumpOnError, promptOnError;
private DiagnosticListener<?> contextDiagnosticListener, logDiagnosticListener;
private final Context context;
- // If this is true, the fields changed. Better to print weird error messages than to fail outright.
- private static final boolean dontBother;
-
private static final ThreadLocal<Queue<?>> queueCache = new ThreadLocal<Queue<?>>();
+ enum Writers {
+ ERROR("errWriter", "ERROR"),
+ WARNING("warnWriter", "WARNING"),
+ NOTICE("noticeWriter", "NOTICE");
+
+ final String fieldName;
+ final String keyName;
+
+ Writers(String fieldName, String keyName) {
+ this.fieldName = fieldName;
+ this.keyName = keyName;
+ }
+ }
+
static {
- errWriterField = getDeclaredField(Log.class, "errWriter");
- warnWriterField = getDeclaredField(Log.class, "warnWriter");
- noticeWriterField = getDeclaredField(Log.class, "noticeWriter");
+ errWriterField = createWriterField(Writers.ERROR);
+ warnWriterField = createWriterField(Writers.WARNING);
+ noticeWriterField = createWriterField(Writers.NOTICE);
dumpOnErrorField = getDeclaredField(Log.class, "dumpOnError");
promptOnErrorField = getDeclaredField(Log.class, "promptOnError");
diagnosticListenerField = getDeclaredField(Log.class, "diagListener");
-
- dontBother =
- errWriterField == null ||
- warnWriterField == null ||
- noticeWriterField == null ||
- dumpOnErrorField == null ||
- promptOnErrorField == null ||
- diagnosticListenerField == null;
-
-
deferDiagnosticsField = getDeclaredField(Log.class, "deferDiagnostics");
deferredDiagnosticsField = getDeclaredField(Log.class, "deferredDiagnostics");
@@ -100,17 +103,13 @@ public final class CompilerMessageSuppressor {
this.context = context;
}
- public boolean disableLoggers() {
+ public void disableLoggers() {
contextDiagnosticListener = context.get(DiagnosticListener.class);
context.put(DiagnosticListener.class, (DiagnosticListener<?>) null);
- if (dontBother) return false;
- boolean dontBotherInstance = false;
-
- PrintWriter dummyWriter = new PrintWriter(new OutputStream() {
- @Override public void write(int b) throws IOException {
- // Do nothing on purpose
- }
- });
+
+ errWriterField.pauze(log);
+ warnWriterField.pauze(log);
+ noticeWriterField.pauze(log);
if (deferDiagnosticsField != null) try {
if (Boolean.TRUE.equals(deferDiagnosticsField.get(log))) {
@@ -130,50 +129,23 @@ public final class CompilerMessageSuppressor {
}
} catch (Exception e) {}
- if (!dontBotherInstance) try {
- errWriter = (PrintWriter) errWriterField.get(log);
- errWriterField.set(log, dummyWriter);
- } catch (Exception e) {
- dontBotherInstance = true;
- }
-
- if (!dontBotherInstance) try {
- warnWriter = (PrintWriter) warnWriterField.get(log);
- warnWriterField.set(log, dummyWriter);
- } catch (Exception e) {
- dontBotherInstance = true;
- }
-
- if (!dontBotherInstance) try {
- noticeWriter = (PrintWriter) noticeWriterField.get(log);
- noticeWriterField.set(log, dummyWriter);
- } catch (Exception e) {
- dontBotherInstance = true;
- }
-
- if (!dontBotherInstance) try {
+ if (dumpOnErrorField != null) try {
dumpOnError = (Boolean) dumpOnErrorField.get(log);
dumpOnErrorField.set(log, false);
} catch (Exception e) {
- dontBotherInstance = true;
}
- if (!dontBotherInstance) try {
+ if (promptOnErrorField != null) try {
promptOnError = (Boolean) promptOnErrorField.get(log);
promptOnErrorField.set(log, false);
} catch (Exception e) {
- dontBotherInstance = true;
}
- if (!dontBotherInstance) try {
+ if (diagnosticListenerField != null) try {
logDiagnosticListener = (DiagnosticListener<?>) diagnosticListenerField.get(log);
diagnosticListenerField.set(log, null);
} catch (Exception e) {
- dontBotherInstance = true;
}
-
- if (dontBotherInstance) enableLoggers();
- return !dontBotherInstance;
}
private static Field getDeferredField(Object handler) {
@@ -193,20 +165,9 @@ public final class CompilerMessageSuppressor {
contextDiagnosticListener = null;
}
- if (errWriter != null) try {
- errWriterField.set(log, errWriter);
- errWriter = null;
- } catch (Exception e) {}
-
- if (warnWriter != null) try {
- warnWriterField.set(log, warnWriter);
- warnWriter = null;
- } catch (Exception e) {}
-
- if (noticeWriter != null) try {
- noticeWriterField.set(log, noticeWriter);
- noticeWriter = null;
- } catch (Exception e) {}
+ errWriterField.resume(log);
+ warnWriterField.resume(log);
+ noticeWriterField.resume(log);
if (dumpOnError != null) try {
dumpOnErrorField.set(log, dumpOnError);
@@ -283,4 +244,107 @@ public final class CompilerMessageSuppressor {
// javac will contain rather a lot of messages, but this is a lot better than just crashing during compilation!
}
}
+
+ private static WriterField createWriterField(Writers w) {
+ // jdk9
+ try {
+ Field writers = getDeclaredField(Log.class, "writer");
+ if (writers != null) {
+ Class<?> kindsClass = Class.forName("com.sun.tools.javac.util.Log$WriterKind");
+ for (Object enumConstant : kindsClass.getEnumConstants()) {
+ if (enumConstant.toString().equals(w.keyName)) {
+ return new Java9WriterField(writers, enumConstant);
+ }
+ }
+ return WriterField.NONE;
+ }
+ } catch (Exception e) {
+ }
+
+ // jdk8
+ Field writerField = getDeclaredField(Log.class, w.fieldName);
+ if (writerField != null) return new Java8WriterField(writerField);
+
+ // other jdk
+ return WriterField.NONE;
+ }
+
+ interface WriterField {
+ final PrintWriter NO_WRITER = new PrintWriter(new OutputStream() {
+ @Override public void write(int b) throws IOException {
+ // Do nothing on purpose
+ }
+ });
+
+ final WriterField NONE = new WriterField() {
+ @Override public void pauze(Log log) {
+ // do nothing
+ }
+ @Override public void resume(Log log) {
+ // no nothing
+ }
+ };
+
+ void pauze(Log log);
+ void resume(Log log);
+ }
+
+ static class Java8WriterField implements WriterField {
+ private final Field field;
+ private PrintWriter writer;
+
+ public Java8WriterField(Field field) {
+ this.field = field;
+ }
+
+ @Override public void pauze(Log log) {
+ try {
+ writer = (PrintWriter) field.get(log);
+ field.set(log, NO_WRITER);
+ } catch (Exception e) {
+ }
+ }
+
+ @Override public void resume(Log log) {
+ if (writer != null) {
+ try {
+ field.set(log, writer);
+ } catch (Exception e) {
+ }
+ }
+ writer = null;
+ }
+ }
+
+
+ static class Java9WriterField implements WriterField {
+ private final Field field;
+ private final Object key;
+ private PrintWriter writer;
+
+ public Java9WriterField(Field field, Object key) {
+ this.field = field;
+ this.key = key;
+ }
+
+ @Override public void pauze(Log log) {
+ try {
+ @SuppressWarnings("unchecked") Map<Object,PrintWriter> map = (Map<Object,PrintWriter>)field.get(log);
+ writer = map.get(key);
+ map.put(key, NO_WRITER);
+ } catch (Exception e) {
+ }
+ }
+
+ @Override public void resume(Log log) {
+ if (writer != null) {
+ try {
+ @SuppressWarnings("unchecked") Map<Object,PrintWriter> map = (Map<Object,PrintWriter>)field.get(log);
+ map.put(key, writer);
+ } catch (Exception e) {
+ }
+ }
+ writer = null;
+ }
+ }
} \ No newline at end of file
diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java
index 30aeff73..3c61696b 100644
--- a/src/core/lombok/javac/HandlerLibrary.java
+++ b/src/core/lombok/javac/HandlerLibrary.java
@@ -44,6 +44,7 @@ import lombok.core.TypeResolver;
import lombok.core.configuration.ConfigurationKeysLoader;
import lombok.javac.handlers.JavacHandlerUtil;
+import com.sun.source.util.Trees;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
@@ -148,12 +149,12 @@ public class HandlerLibrary {
* then uses SPI discovery to load all annotation and visitor based handlers so that future calls
* to the handle methods will defer to these handlers.
*/
- public static HandlerLibrary load(Messager messager) {
+ public static HandlerLibrary load(Messager messager, Trees trees) {
HandlerLibrary library = new HandlerLibrary(messager);
try {
- loadAnnotationHandlers(library);
- loadVisitorHandlers(library);
+ loadAnnotationHandlers(library, trees);
+ loadVisitorHandlers(library, trees);
} catch (IOException e) {
System.err.println("Lombok isn't running due to misconfigured SPI files: " + e);
}
@@ -165,9 +166,10 @@ public class HandlerLibrary {
/** Uses SPI Discovery to find implementations of {@link JavacAnnotationHandler}. */
@SuppressWarnings({"rawtypes", "unchecked"})
- private static void loadAnnotationHandlers(HandlerLibrary lib) throws IOException {
+ private static void loadAnnotationHandlers(HandlerLibrary lib, Trees trees) throws IOException {
//No, that seemingly superfluous reference to JavacAnnotationHandler's classloader is not in fact superfluous!
for (JavacAnnotationHandler handler : SpiLoadUtil.findServices(JavacAnnotationHandler.class, JavacAnnotationHandler.class.getClassLoader())) {
+ handler.setTrees(trees);
Class<? extends Annotation> annotationClass = handler.getAnnotationHandledByThisHandler();
AnnotationHandlerContainer<?> container = new AnnotationHandlerContainer(handler, annotationClass);
String annotationClassName = container.annotationClass.getName().replace("$", ".");
@@ -179,9 +181,10 @@ public class HandlerLibrary {
}
/** Uses SPI Discovery to find implementations of {@link JavacASTVisitor}. */
- private static void loadVisitorHandlers(HandlerLibrary lib) throws IOException {
+ private static void loadVisitorHandlers(HandlerLibrary lib, Trees trees) throws IOException {
//No, that seemingly superfluous reference to JavacASTVisitor's classloader is not in fact superfluous!
for (JavacASTVisitor visitor : SpiLoadUtil.findServices(JavacASTVisitor.class, JavacASTVisitor.class.getClassLoader())) {
+ visitor.setTrees(trees);
lib.visitorHandlers.add(new VisitorContainer(visitor));
}
}
diff --git a/src/core/lombok/javac/Javac8BasedLombokOptions.java b/src/core/lombok/javac/Javac8BasedLombokOptions.java
index 3fdea890..9a662490 100644
--- a/src/core/lombok/javac/Javac8BasedLombokOptions.java
+++ b/src/core/lombok/javac/Javac8BasedLombokOptions.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Project Lombok Authors.
+ * Copyright (C) 2013-2017 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,7 +28,7 @@ import com.sun.tools.javac.util.Options;
public class Javac8BasedLombokOptions extends LombokOptions {
public static Javac8BasedLombokOptions replaceWithDelombokOptions(Context context) {
Options options = Options.instance(context);
- context.put(optionsKey, (Options)null);
+ context.put(optionsKey, (Options) null);
Javac8BasedLombokOptions result = new Javac8BasedLombokOptions(context);
result.putAll(options);
return result;
diff --git a/src/core/lombok/javac/Javac9BasedLombokOptions.java b/src/core/lombok/javac/Javac9BasedLombokOptions.java
new file mode 100644
index 00000000..e786346d
--- /dev/null
+++ b/src/core/lombok/javac/Javac9BasedLombokOptions.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac;
+
+import com.sun.tools.javac.main.Option;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Options;
+
+public class Javac9BasedLombokOptions extends LombokOptions {
+ public static Javac9BasedLombokOptions replaceWithDelombokOptions(Context context) {
+ Options options = Options.instance(context);
+ context.put(optionsKey, (Options) null);
+ Javac9BasedLombokOptions result = new Javac9BasedLombokOptions(context);
+ result.putAll(options);
+ return result;
+ }
+
+ private Javac9BasedLombokOptions(Context context) {
+ super(context);
+ }
+
+ @Override public void putJavacOption(String optionName, String value) {
+ if (optionName.equals("CLASSPATH")) optionName = "CLASS_PATH";
+ if (optionName.equals("SOURCEPATH")) optionName = "SOURCE_PATH";
+ if (optionName.equals("BOOTCLASSPATH")) optionName = "BOOT_CLASS_PATH";
+ String optionText = Option.valueOf(optionName).primaryName;
+ put(optionText, value);
+ }
+}
diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java
index 4e553063..12d919af 100644
--- a/src/core/lombok/javac/JavacAST.java
+++ b/src/core/lombok/javac/JavacAST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -22,52 +22,54 @@
package lombok.javac;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import javax.annotation.processing.Messager;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
+import com.sun.tools.javac.util.JCDiagnostic;
import lombok.core.AST;
-import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Source;
+import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.JCCatch;
-import com.sun.tools.javac.tree.JCTree.JCTry;
-import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
-import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
-import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
-import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
/**
* Wraps around javac's internal AST view to add useful features as well as the ability to visit parents from children,
* something javac's own AST system does not offer.
*/
public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
- private final Messager messager;
private final JavacElements elements;
private final JavacTreeMaker treeMaker;
private final Symtab symtab;
private final JavacTypes javacTypes;
private final Log log;
+ private final ErrorLog errorLogger;
private final Context context;
/**
@@ -78,11 +80,11 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
* @param top The compilation unit, which serves as the top level node in the tree to be built.
*/
public JavacAST(Messager messager, Context context, JCCompilationUnit top) {
- super(sourceName(top), packageDeclaration(top), new JavacImportList(top));
+ super(sourceName(top), PackageName.getPackageName(top), new JavacImportList(top), statementTypes());
setTop(buildCompilationUnit(top));
this.context = context;
- this.messager = messager;
this.log = Log.instance(context);
+ this.errorLogger = ErrorLog.create(messager, log);
this.elements = JavacElements.instance(context);
this.treeMaker = new JavacTreeMaker(TreeMaker.instance(context));
this.symtab = Symtab.instance(context);
@@ -103,10 +105,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return cu.sourcefile == null ? null : cu.sourcefile.toString();
}
- private static String packageDeclaration(JCCompilationUnit cu) {
- return (cu.pid instanceof JCFieldAccess || cu.pid instanceof JCIdent) ? cu.pid.toString() : null;
- }
-
public Context getContext() {
return context;
}
@@ -128,6 +126,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
String nm = Source.instance(context).name();
int underscoreIdx = nm.indexOf('_');
if (underscoreIdx > -1) return Integer.parseInt(nm.substring(underscoreIdx + 1));
+ // assume java9+
+ return Integer.parseInt(nm);
} catch (Exception ignore) {}
return 6;
}
@@ -189,7 +189,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
List<JavacNode> childNodes = new ArrayList<JavacNode>();
for (JCTree s : top.defs) {
if (s instanceof JCClassDecl) {
- addIfNotNull(childNodes, buildType((JCClassDecl)s));
+ addIfNotNull(childNodes, buildType((JCClassDecl) s));
} // else they are import statements, which we don't care about. Or Skip objects, whatever those are.
}
@@ -208,10 +208,10 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
* JCVariableDecl for fields
* JCBlock for (static) initializers
*/
- if (def instanceof JCMethodDecl) addIfNotNull(childNodes, buildMethod((JCMethodDecl)def));
- else if (def instanceof JCClassDecl) addIfNotNull(childNodes, buildType((JCClassDecl)def));
- else if (def instanceof JCVariableDecl) addIfNotNull(childNodes, buildField((JCVariableDecl)def));
- else if (def instanceof JCBlock) addIfNotNull(childNodes, buildInitializer((JCBlock)def));
+ if (def instanceof JCMethodDecl) addIfNotNull(childNodes, buildMethod((JCMethodDecl) def));
+ else if (def instanceof JCClassDecl) addIfNotNull(childNodes, buildType((JCClassDecl) def));
+ else if (def instanceof JCVariableDecl) addIfNotNull(childNodes, buildField((JCVariableDecl) def));
+ else if (def instanceof JCBlock) addIfNotNull(childNodes, buildInitializer((JCBlock) def));
}
return putInMap(new JavacNode(this, type, childNodes, Kind.TYPE));
@@ -297,7 +297,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
// @Foo int x, y; is handled in javac by putting the same annotation node on 2 JCVariableDecls.
return null;
}
-
return putInMap(new JavacNode(this, annotation, null, Kind.ANNOTATION));
}
@@ -315,12 +314,40 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
if (statement instanceof JCClassDecl) return buildType((JCClassDecl)statement);
if (statement instanceof JCVariableDecl) return buildLocalVar((JCVariableDecl)statement, Kind.LOCAL);
if (statement instanceof JCTry) return buildTry((JCTry) statement);
-
+ if (statement.getClass().getSimpleName().equals("JCLambda")) return buildLambda(statement);
if (setAndGetAsHandled(statement)) return null;
return drill(statement);
}
+ private JavacNode buildLambda(JCTree jcTree) {
+ return buildStatementOrExpression(getBody(jcTree));
+ }
+
+ private JCTree getBody(JCTree jcTree) {
+ try {
+ return (JCTree) getBodyMethod(jcTree.getClass()).invoke(jcTree);
+ } catch (Exception e) {
+ throw Javac.sneakyThrow(e);
+ }
+ }
+
+ private final static ConcurrentMap<Class<?>, Method> getBodyMethods = new ConcurrentHashMap<Class<?>, Method>();
+
+ private Method getBodyMethod(Class<?> c) {
+ Method m = getBodyMethods.get(c);
+ if (m != null) {
+ return m;
+ }
+ try {
+ m = c.getMethod("getBody");
+ } catch (NoSuchMethodException e) {
+ throw Javac.sneakyThrow(e);
+ }
+ getBodyMethods.putIfAbsent(c, m);
+ return getBodyMethods.get(c);
+ }
+
private JavacNode drill(JCTree statement) {
try {
List<JavacNode> childNodes = new ArrayList<JavacNode>();
@@ -336,9 +363,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
}
}
- /** For javac, both JCExpression and JCStatement are considered as valid children types. */
- @Override
- protected Collection<Class<? extends JCTree>> getStatementTypes() {
+ /* For javac, both JCExpression and JCStatement are considered as valid children types. */
+ private static Collection<Class<? extends JCTree>> statementTypes() {
Collection<Class<? extends JCTree>> collection = new ArrayList<Class<? extends JCTree>>(3);
collection.add(JCStatement.class);
collection.add(JCExpression.class);
@@ -376,22 +402,21 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
try {
switch (kind) {
case ERROR:
- increaseErrorCount(messager);
- boolean prev = log.multipleErrors;
- log.multipleErrors = true;
- try {
- log.error(pos, "proc.messager", message);
- } finally {
- log.multipleErrors = prev;
- }
+ errorLogger.error(pos, message);
+ break;
+ case MANDATORY_WARNING:
+ errorLogger.mandatoryWarning(pos, message);
break;
- default:
case WARNING:
- log.warning(pos, "proc.messager", message);
+ errorLogger.warning(pos, message);
+ break;
+ default:
+ case NOTE:
+ errorLogger.note(pos, message);
break;
}
} finally {
- if (oldSource != null) log.useSource(oldSource);
+ if (newSource != null) log.useSource(oldSource);
}
}
@@ -429,16 +454,183 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return oldL;
}
- private void increaseErrorCount(Messager m) {
- try {
- Field f = m.getClass().getDeclaredField("errorCount");
- f.setAccessible(true);
- if (f.getType() == int.class) {
- int val = ((Number)f.get(m)).intValue();
- f.set(m, val +1);
+ abstract static class ErrorLog {
+ final Log log;
+ private final Messager messager;
+ private final Field errorCount;
+ private final Field warningCount;
+
+ private ErrorLog(Log log, Messager messager, Field errorCount, Field warningCount) {
+ this.log = log;
+ this.messager = messager;
+ this.errorCount = errorCount;
+ this.warningCount = warningCount;
+ }
+
+ final void error(DiagnosticPosition pos, String message) {
+ increment(errorCount);
+ error1(pos, message);
+ }
+
+ final void warning(DiagnosticPosition pos, String message) {
+ increment(warningCount);
+ warning1(pos, message);
+ }
+
+ final void mandatoryWarning(DiagnosticPosition pos, String message) {
+ increment(warningCount);
+ mandatoryWarning1(pos, message);
+ }
+
+ abstract void error1(DiagnosticPosition pos, String message);
+ abstract void warning1(DiagnosticPosition pos, String message);
+ abstract void mandatoryWarning1(DiagnosticPosition pos, String message);
+ abstract void note(DiagnosticPosition pos, String message);
+
+ private void increment(Field field) {
+ if (field == null) return;
+ try {
+ int val = ((Number)field.get(messager)).intValue();
+ field.set(messager, val +1);
+ } catch (Throwable t) {
+ //Very unfortunate, but in most cases it still works fine, so we'll silently swallow it.
+ }
+ }
+
+ 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) {}
+ boolean hasMultipleErrors = false;
+ for (Field field : log.getClass().getFields()) {
+ if (field.getName().equals("multipleErrors")) {
+ hasMultipleErrors = true;
+ break;
+ }
+ }
+ 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) {}
+
+ return new Jdk9Plus(log, messager, errorCount, warningCount);
+ }
+ }
+
+ static class JdkBefore9 extends ErrorLog {
+ private JdkBefore9(Log log, Messager messager, Field errorCount) {
+ super(log, messager, errorCount, null);
+ }
+
+ @Override void error1(DiagnosticPosition pos, String message) {
+ boolean prev = log.multipleErrors;
+ log.multipleErrors = true;
+ try {
+ log.error(pos, "proc.messager", message);
+ } finally {
+ log.multipleErrors = prev;
+ }
+ }
+
+ @Override void warning1(DiagnosticPosition pos, String message) {
+ log.warning(pos, "proc.messager", message);
+ }
+
+ @Override void mandatoryWarning1(DiagnosticPosition pos, String message) {
+ log.mandatoryWarning(pos, "proc.messager", message);
+ }
+
+ @Override void note(DiagnosticPosition pos, String message) {
+ log.note(pos, "proc.messager", message);
+ }
+ }
+
+ static class Jdk9Plus extends ErrorLog {
+ private static final String PROC_MESSAGER = "proc.messager";
+ private Object multiple;
+ private Method errorMethod, warningMethod, mandatoryWarningMethod, noteMethod;
+ private Method errorKey, warningKey, noteKey;
+ private JCDiagnostic.Factory diags;
+
+ private Jdk9Plus(Log log, Messager messager, Field errorCount, Field warningCount) {
+ super(log, messager, errorCount, warningCount);
+
+ try {
+ final String jcd = "com.sun.tools.javac.util.JCDiagnostic";
+ Class<?> df = Class.forName(jcd + "$DiagnosticFlag");
+ for (Object constant : df.getEnumConstants()) {
+ if (constant.toString().equals("MULTIPLE")) this.multiple = constant;
+ }
+
+ Class<?> errorCls = Class.forName(jcd + "$Error");
+ Class<?> warningCls = Class.forName(jcd + "$Warning");
+ 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);
+
+ Field diagsField = lc.getSuperclass().getDeclaredField("diags");
+ diagsField.setAccessible(true);
+ 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);
+ } catch (Throwable t) {
+ //t.printStackTrace();
+ }
+ }
+
+ @Override void error1(DiagnosticPosition pos, String message) {
+ try {
+ Object error = this.errorKey.invoke(diags, PROC_MESSAGER, new Object[] { message });
+ errorMethod.invoke(log, multiple, pos, error);
+ } catch (Throwable t) {
+ //t.printStackTrace();
+ }
+ }
+
+ @Override
+ void warning1(DiagnosticPosition pos, String message) {
+ try {
+ Object warning = this.warningKey.invoke(diags, PROC_MESSAGER, new Object[] { message });
+ warningMethod.invoke(log, pos, warning);
+ } catch (Throwable t) {
+ //t.printStackTrace();
+ }
+ }
+
+ @Override
+ void mandatoryWarning1(DiagnosticPosition pos, String message) {
+ try {
+ Object warning = this.warningKey.invoke(diags, PROC_MESSAGER, new Object[] { message });
+ mandatoryWarningMethod.invoke(log, pos, warning);
+ } catch (Throwable t) {
+ //t.printStackTrace();
+ }
+ }
+
+ @Override
+ void note(DiagnosticPosition pos, String message) {
+ try {
+ Object note = this.noteKey.invoke(diags, PROC_MESSAGER, new Object[] { message });
+ noteMethod.invoke(log, pos, note);
+ } catch (Throwable t) {
+ //t.printStackTrace();
}
- } catch (Throwable t) {
- //Very unfortunate, but in most cases it still works fine, so we'll silently swallow it.
}
}
}
diff --git a/src/core/lombok/javac/JavacASTAdapter.java b/src/core/lombok/javac/JavacASTAdapter.java
index 5d120a77..6af53e3d 100644
--- a/src/core/lombok/javac/JavacASTAdapter.java
+++ b/src/core/lombok/javac/JavacASTAdapter.java
@@ -21,6 +21,7 @@
*/
package lombok.javac;
+import com.sun.source.util.Trees;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -35,6 +36,9 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
*/
public class JavacASTAdapter implements JavacASTVisitor {
/** {@inheritDoc} */
+ @Override public void setTrees(Trees trees) {}
+
+ /** {@inheritDoc} */
@Override public void visitCompilationUnit(JavacNode top, JCCompilationUnit unit) {}
/** {@inheritDoc} */
diff --git a/src/core/lombok/javac/JavacASTVisitor.java b/src/core/lombok/javac/JavacASTVisitor.java
index c57e657a..565980f9 100644
--- a/src/core/lombok/javac/JavacASTVisitor.java
+++ b/src/core/lombok/javac/JavacASTVisitor.java
@@ -23,6 +23,7 @@ package lombok.javac;
import java.io.PrintStream;
+import com.sun.source.util.Trees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
@@ -37,6 +38,8 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
* calling the appropriate visit and endVisit methods.
*/
public interface JavacASTVisitor {
+ void setTrees(Trees trees);
+
/**
* Called at the very beginning and end.
*/
@@ -121,6 +124,8 @@ public interface JavacASTVisitor {
this.out = out;
}
+ @Override public void setTrees(Trees trees) {}
+
private void forcePrint(String text, Object... params) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < indent; i++) sb.append(" ");
diff --git a/src/core/lombok/javac/JavacAnnotationHandler.java b/src/core/lombok/javac/JavacAnnotationHandler.java
index a86aa6c6..dd4e7098 100644
--- a/src/core/lombok/javac/JavacAnnotationHandler.java
+++ b/src/core/lombok/javac/JavacAnnotationHandler.java
@@ -26,6 +26,7 @@ import java.lang.annotation.Annotation;
import lombok.core.AnnotationValues;
import lombok.core.SpiLoadUtil;
+import com.sun.source.util.Trees;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
/**
@@ -40,6 +41,8 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation;
* You also need to register yourself via SPI discovery as being an implementation of {@code JavacAnnotationHandler}.
*/
public abstract class JavacAnnotationHandler<T extends Annotation> {
+ protected Trees trees;
+
/**
* Called when an annotation is found that is likely to match the annotation you're interested in.
*
@@ -63,4 +66,8 @@ public abstract class JavacAnnotationHandler<T extends Annotation> {
@SuppressWarnings("unchecked") public Class<T> getAnnotationHandledByThisHandler() {
return (Class<T>) SpiLoadUtil.findAnnotationClass(getClass(), JavacAnnotationHandler.class);
}
+
+ public void setTrees(Trees trees) {
+ this.trees = trees;
+ }
}
diff --git a/src/core/lombok/javac/JavacImportList.java b/src/core/lombok/javac/JavacImportList.java
index 2665ca7c..468d8c7b 100644
--- a/src/core/lombok/javac/JavacImportList.java
+++ b/src/core/lombok/javac/JavacImportList.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,22 +24,21 @@ package lombok.javac;
import java.util.ArrayList;
import java.util.Collection;
+import lombok.core.ImportList;
+import lombok.core.LombokInternalAliasing;
+
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
-import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.util.List;
-import lombok.core.ImportList;
-import lombok.core.LombokInternalAliasing;
-
public class JavacImportList implements ImportList {
- private final JCExpression pkg;
+ private final String pkgStr;
private final List<JCTree> defs;
public JavacImportList(JCCompilationUnit cud) {
- this.pkg = cud.pid;
+ this.pkgStr = PackageName.getPackageName(cud);
this.defs = cud.defs;
}
@@ -58,7 +57,6 @@ public class JavacImportList implements ImportList {
}
@Override public boolean hasStarImport(String packageName) {
- String pkgStr = pkg == null ? null : pkg.toString();
if (pkgStr != null && pkgStr.equals(packageName)) return true;
if ("java.lang".equals(packageName)) return true;
@@ -86,7 +84,7 @@ public class JavacImportList implements ImportList {
@Override public Collection<String> applyNameToStarImports(String startsWith, String name) {
ArrayList<String> out = new ArrayList<String>();
- if (pkg != null && topLevelName(pkg).equals(startsWith)) out.add(pkg.toString() + "." + name);
+ if (pkgStr != null && topLevelName(pkgStr).equals(startsWith)) out.add(pkgStr + "." + name);
for (JCTree def : defs) {
if (!(def instanceof JCImport)) continue;
@@ -110,8 +108,14 @@ public class JavacImportList implements ImportList {
return tree.toString();
}
+ private String topLevelName(String packageName) {
+ int idx = packageName.indexOf(".");
+ if (idx == -1) return packageName;
+ return packageName.substring(0, idx);
+ }
+
@Override public String applyUnqualifiedNameToPackage(String unqualified) {
- if (pkg == null) return unqualified;
- return pkg.toString() + "." + unqualified;
+ if (pkgStr == null) return unqualified;
+ return pkgStr + "." + unqualified;
}
}
diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java
index 6eef36eb..fa24c2f9 100644
--- a/src/core/lombok/javac/JavacNode.java
+++ b/src/core/lombok/javac/JavacNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -23,6 +23,7 @@ package lombok.javac;
import java.util.List;
+import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
import lombok.core.AST.Kind;
@@ -51,6 +52,13 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
super(ast, node, children, kind);
}
+ public Element getElement() {
+ if (node instanceof JCClassDecl) return ((JCClassDecl) node).sym;
+ if (node instanceof JCMethodDecl) return ((JCMethodDecl) node).sym;
+ if (node instanceof JCVariableDecl) return ((JCVariableDecl) node).sym;
+ return null;
+ }
+
public int getEndPosition(DiagnosticPosition pos) {
JCCompilationUnit cu = (JCCompilationUnit) top().get();
return Javac.getEndPosition(pos, cu);
@@ -66,40 +74,40 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
public void traverse(JavacASTVisitor visitor) {
switch (this.getKind()) {
case COMPILATION_UNIT:
- visitor.visitCompilationUnit(this, (JCCompilationUnit)get());
+ visitor.visitCompilationUnit(this, (JCCompilationUnit) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitCompilationUnit(this, (JCCompilationUnit)get());
+ visitor.endVisitCompilationUnit(this, (JCCompilationUnit) get());
break;
case TYPE:
- visitor.visitType(this, (JCClassDecl)get());
+ visitor.visitType(this, (JCClassDecl) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitType(this, (JCClassDecl)get());
+ visitor.endVisitType(this, (JCClassDecl) get());
break;
case FIELD:
- visitor.visitField(this, (JCVariableDecl)get());
+ visitor.visitField(this, (JCVariableDecl) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitField(this, (JCVariableDecl)get());
+ visitor.endVisitField(this, (JCVariableDecl) get());
break;
case METHOD:
- visitor.visitMethod(this, (JCMethodDecl)get());
+ visitor.visitMethod(this, (JCMethodDecl) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitMethod(this, (JCMethodDecl)get());
+ visitor.endVisitMethod(this, (JCMethodDecl) get());
break;
case INITIALIZER:
- visitor.visitInitializer(this, (JCBlock)get());
+ visitor.visitInitializer(this, (JCBlock) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitInitializer(this, (JCBlock)get());
+ visitor.endVisitInitializer(this, (JCBlock) get());
break;
case ARGUMENT:
JCMethodDecl parentMethod = (JCMethodDecl) up().get();
- visitor.visitMethodArgument(this, (JCVariableDecl)get(), parentMethod);
+ visitor.visitMethodArgument(this, (JCVariableDecl) get(), parentMethod);
ast.traverseChildren(visitor, this);
- visitor.endVisitMethodArgument(this, (JCVariableDecl)get(), parentMethod);
+ visitor.endVisitMethodArgument(this, (JCVariableDecl) get(), parentMethod);
break;
case LOCAL:
- visitor.visitLocal(this, (JCVariableDecl)get());
+ visitor.visitLocal(this, (JCVariableDecl) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitLocal(this, (JCVariableDecl)get());
+ visitor.endVisitLocal(this, (JCVariableDecl) get());
break;
case STATEMENT:
visitor.visitStatement(this, get());
@@ -109,21 +117,21 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
case ANNOTATION:
switch (up().getKind()) {
case TYPE:
- visitor.visitAnnotationOnType((JCClassDecl)up().get(), this, (JCAnnotation)get());
+ visitor.visitAnnotationOnType((JCClassDecl) up().get(), this, (JCAnnotation) get());
break;
case FIELD:
- visitor.visitAnnotationOnField((JCVariableDecl)up().get(), this, (JCAnnotation)get());
+ visitor.visitAnnotationOnField((JCVariableDecl) up().get(), this, (JCAnnotation) get());
break;
case METHOD:
- visitor.visitAnnotationOnMethod((JCMethodDecl)up().get(), this, (JCAnnotation)get());
+ visitor.visitAnnotationOnMethod((JCMethodDecl) up().get(), this, (JCAnnotation) get());
break;
case ARGUMENT:
- JCVariableDecl argument = (JCVariableDecl)up().get();
- JCMethodDecl method = (JCMethodDecl)up().up().get();
- visitor.visitAnnotationOnMethodArgument(argument, method, this, (JCAnnotation)get());
+ JCVariableDecl argument = (JCVariableDecl) up().get();
+ JCMethodDecl method = (JCMethodDecl) up().up().get();
+ visitor.visitAnnotationOnMethodArgument(argument, method, this, (JCAnnotation) get());
break;
case LOCAL:
- visitor.visitAnnotationOnLocal((JCVariableDecl)up().get(), this, (JCAnnotation)get());
+ visitor.visitAnnotationOnLocal((JCVariableDecl) up().get(), this, (JCAnnotation) get());
break;
default:
throw new AssertionError("Annotion not expected as child of a " + up().getKind());
@@ -138,9 +146,9 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
@Override public String getName() {
final Name n;
- if (node instanceof JCClassDecl) n = ((JCClassDecl)node).name;
- else if (node instanceof JCMethodDecl) n = ((JCMethodDecl)node).name;
- else if (node instanceof JCVariableDecl) n = ((JCVariableDecl)node).name;
+ if (node instanceof JCClassDecl) n = ((JCClassDecl) node).name;
+ else if (node instanceof JCMethodDecl) n = ((JCMethodDecl) node).name;
+ else if (node instanceof JCVariableDecl) n = ((JCVariableDecl) node).name;
else n = null;
return n == null ? null : n.toString();
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
index 23d7d482..8cc239e1 100644
--- a/src/core/lombok/javac/JavacResolution.java
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2014 The Project Lombok Authors.
+ * Copyright (C) 2011-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
@@ -24,14 +24,17 @@ package lombok.javac;
import static lombok.javac.Javac.*;
import static lombok.javac.JavacTreeMaker.TypeTag.typeTag;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Map;
import javax.lang.model.type.TypeKind;
+import javax.tools.JavaFileObject;
import lombok.Lombok;
+import lombok.core.debug.AssertionLogger;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
@@ -57,6 +60,7 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Log;
public class JavacResolution {
private final Attr attr;
@@ -98,7 +102,7 @@ public class JavacResolution {
@Override public void visitClassDef(JCClassDecl tree) {
if (copyAt != null) return;
- env = enter.getClassEnv(tree.sym);
+ if (tree.sym != null) env = enter.getClassEnv(tree.sym);
}
@Override public void visitMethodDef(JCMethodDecl tree) {
@@ -138,16 +142,70 @@ public class JavacResolution {
EnvFinder finder = new EnvFinder(node.getContext());
while (!stack.isEmpty()) stack.pop().accept(finder);
- TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker());
+ TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker(), node.getContext());
JCTree copy = mirrorMaker.copy(finder.copyAt());
-
- attrib(copy, finder.get());
- return mirrorMaker.getOriginalToCopyMap();
+ Log log = Log.instance(node.getContext());
+ JavaFileObject oldFileObject = log.useSource(((JCCompilationUnit) node.top().get()).getSourceFile());
+ try {
+ memberEnterAndAttribute(copy, finder.get(), node.getContext());
+ return mirrorMaker.getOriginalToCopyMap();
+ } finally {
+ log.useSource(oldFileObject);
+ }
} finally {
messageSuppressor.enableLoggers();
}
}
+ private static Field memberEnterDotEnv;
+
+ private static Field getMemberEnterDotEnv() {
+ if (memberEnterDotEnv != null) return memberEnterDotEnv;
+ try {
+ Field f = MemberEnter.class.getDeclaredField("env");
+ f.setAccessible(true);
+ memberEnterDotEnv = f;
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+
+ return memberEnterDotEnv;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Env<AttrContext> getEnvOfMemberEnter(MemberEnter memberEnter) {
+ Field f = getMemberEnterDotEnv();
+ try {
+ return (Env<AttrContext>) f.get(memberEnter);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private static void setEnvOfMemberEnter(MemberEnter memberEnter, Env<AttrContext> env) {
+ Field f = getMemberEnterDotEnv();
+ try {
+ f.set(memberEnter, env);
+ } catch (Exception e) {
+ return;
+ }
+ }
+
+ private void memberEnterAndAttribute(JCTree copy, Env<AttrContext> env, Context context) {
+ MemberEnter memberEnter = MemberEnter.instance(context);
+ Env<AttrContext> oldEnv = getEnvOfMemberEnter(memberEnter);
+ setEnvOfMemberEnter(memberEnter, env);
+ try {
+ copy.accept(memberEnter);
+ } catch (Exception ignore) {
+ // intentionally ignored; usually even if this step fails, val will work (but not for val in method local inner classes and anonymous inner classes).
+ AssertionLogger.assertLog("member enter failed.", ignore);
+ } finally {
+ setEnvOfMemberEnter(memberEnter, oldEnv);
+ }
+ attrib(copy, env);
+ }
+
public void resolveClassMember(JavacNode node) {
ArrayDeque<JCTree> stack = new ArrayDeque<JCTree>();
@@ -171,8 +229,13 @@ public class JavacResolution {
}
private void attrib(JCTree tree, Env<AttrContext> env) {
+ if (env.enclClass.type == null) try {
+ env.enclClass.type = Type.noType;
+ } catch (Throwable ignore) {
+ // This addresses issue #1553 which involves JDK9; if it doesn't exist, we probably don't need to set it.
+ }
if (tree instanceof JCBlock) attr.attribStat(tree, env);
- else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl)tree).body, env);
+ else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl) tree).body, env);
else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env);
else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl");
}
@@ -210,6 +273,7 @@ public class JavacResolution {
}
public static Type ifTypeIsIterableToComponent(Type type, JavacAST ast) {
+ if (type == null) return null;
Types types = Types.instance(ast.getContext());
Symtab syms = Symtab.instance(ast.getContext());
Type boundType = ReflectiveAccess.Types_upperBound(types, type);
@@ -241,7 +305,7 @@ public class JavacResolution {
Type type0 = type;
while (type0 instanceof ArrayType) {
dims++;
- type0 = ((ArrayType)type0).elemtype;
+ type0 = ((ArrayType) type0).elemtype;
}
JCExpression result = typeToJCTree0(type0, ast, allowCompound, allowVoid);
diff --git a/src/core/lombok/javac/JavacTransformer.java b/src/core/lombok/javac/JavacTransformer.java
index 004a6035..54977a59 100644
--- a/src/core/lombok/javac/JavacTransformer.java
+++ b/src/core/lombok/javac/JavacTransformer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,7 @@ import java.util.SortedSet;
import javax.annotation.processing.Messager;
+import com.sun.source.util.Trees;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
@@ -38,9 +39,9 @@ public class JavacTransformer {
private final HandlerLibrary handlers;
private final Messager messager;
- public JavacTransformer(Messager messager) {
+ public JavacTransformer(Messager messager, Trees trees) {
this.messager = messager;
- this.handlers = HandlerLibrary.load(messager);
+ this.handlers = HandlerLibrary.load(messager, trees);
}
public SortedSet<Long> getPriorities() {
@@ -54,7 +55,7 @@ public class JavacTransformer {
public void transform(long priority, Context context, java.util.List<JCCompilationUnit> compilationUnitsRaw) {
List<JCCompilationUnit> compilationUnits;
if (compilationUnitsRaw instanceof List<?>) {
- compilationUnits = (List<JCCompilationUnit>)compilationUnitsRaw;
+ compilationUnits = (List<JCCompilationUnit>) compilationUnitsRaw;
} else {
compilationUnits = List.nil();
for (int i = compilationUnitsRaw.size() -1; i >= 0; i--) {
diff --git a/src/core/lombok/javac/apt/EmptyLombokFileObject.java b/src/core/lombok/javac/apt/EmptyLombokFileObject.java
index 7298e920..5a3a7def 100644
--- a/src/core/lombok/javac/apt/EmptyLombokFileObject.java
+++ b/src/core/lombok/javac/apt/EmptyLombokFileObject.java
@@ -19,7 +19,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package lombok.javac.apt;
import java.io.ByteArrayInputStream;
diff --git a/src/core/lombok/javac/apt/InterceptingJavaFileManager.java b/src/core/lombok/javac/apt/InterceptingJavaFileManager.java
index 303bdc2f..9b58d111 100644
--- a/src/core/lombok/javac/apt/InterceptingJavaFileManager.java
+++ b/src/core/lombok/javac/apt/InterceptingJavaFileManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 The Project Lombok Authors.
+ * Copyright (C) 2010-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
@@ -42,14 +42,14 @@ final class InterceptingJavaFileManager extends ForwardingJavaFileManager<JavaFi
}
@Override public JavaFileObject getJavaFileForOutput(Location location, String className, final Kind kind, FileObject sibling) throws IOException {
- if (className.startsWith("lombok.dummy.ForceNewRound")) {
+ if (className.contains("lombok.dummy.ForceNewRound")) {
final String name = className.replace(".", "/") + kind.extension;
return LombokFileObjects.createEmpty(compiler, name, kind);
}
+
JavaFileObject fileObject = fileManager.getJavaFileForOutput(location, className, kind, sibling);
- if (kind != Kind.CLASS) {
- return fileObject;
- }
+ if (kind != Kind.CLASS) return fileObject;
+
return LombokFileObjects.createIntercepting(compiler, fileObject, className, diagnostics);
}
} \ No newline at end of file
diff --git a/src/core/lombok/javac/apt/Javac9BaseFileObjectWrapper.java b/src/core/lombok/javac/apt/Javac9BaseFileObjectWrapper.java
new file mode 100644
index 00000000..f9fe2a7d
--- /dev/null
+++ b/src/core/lombok/javac/apt/Javac9BaseFileObjectWrapper.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package lombok.javac.apt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.nio.file.Path;
+
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+
+import com.sun.tools.javac.file.BaseFileManager;
+
+class Javac9BaseFileObjectWrapper extends com.sun.tools.javac.file.PathFileObject {
+ private final LombokFileObject delegate;
+
+ public Javac9BaseFileObjectWrapper(BaseFileManager fileManager, Path path, LombokFileObject delegate) {
+ super(fileManager, path);
+ this.delegate = delegate;
+ }
+
+ @Override public boolean isNameCompatible(String simpleName, Kind kind) {
+ return delegate.isNameCompatible(simpleName, kind);
+ }
+
+ @Override public URI toUri() {
+ return delegate.toUri();
+ }
+
+ @SuppressWarnings("all")
+ @Override public String getName() {
+ return delegate.getName();
+ }
+
+ @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+ return delegate.getCharContent(ignoreEncodingErrors);
+ }
+
+ @Override public InputStream openInputStream() throws IOException {
+ return delegate.openInputStream();
+ }
+
+ @Override public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+ return delegate.openReader(ignoreEncodingErrors);
+ }
+
+ @Override public Writer openWriter() throws IOException {
+ return delegate.openWriter();
+ }
+
+ @Override public OutputStream openOutputStream() throws IOException {
+ return delegate.openOutputStream();
+ }
+
+ @Override public long getLastModified() {
+ return delegate.getLastModified();
+ }
+
+ @Override public boolean delete() {
+ return delegate.delete();
+ }
+
+ @Override public Kind getKind() {
+ return delegate.getKind();
+ }
+
+ @Override public NestingKind getNestingKind() {
+ return delegate.getNestingKind();
+ }
+
+ @Override public Modifier getAccessLevel() {
+ return delegate.getAccessLevel();
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof Javac9BaseFileObjectWrapper)) {
+ return false;
+ }
+ return delegate.equals(((Javac9BaseFileObjectWrapper)obj).delegate);
+ }
+
+ @Override public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override public String toString() {
+ return delegate.toString();
+ }
+} \ No newline at end of file
diff --git a/src/core/lombok/javac/apt/LombokFileObjects.java b/src/core/lombok/javac/apt/LombokFileObjects.java
index 412e449b..aba10540 100644
--- a/src/core/lombok/javac/apt/LombokFileObjects.java
+++ b/src/core/lombok/javac/apt/LombokFileObjects.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 The Project Lombok Authors.
+ * Copyright (C) 2010-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
@@ -22,20 +22,32 @@
package lombok.javac.apt;
+import java.io.IOException;
import java.lang.reflect.Method;
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import lombok.core.DiagnosticsReceiver;
+import com.sun.tools.javac.file.BaseFileManager;
+
//Can't use SimpleJavaFileObject so we copy/paste most of its content here, because javac doesn't follow the interface,
//and casts to its own BaseFileObject type. D'oh!
final class LombokFileObjects {
- enum Compiler {
- JAVAC6 {
+
+ interface Compiler {
+ Compiler JAVAC6 = new Compiler() {
private Method decoderMethod = null;
private final AtomicBoolean decoderIsSet = new AtomicBoolean();
@@ -46,13 +58,13 @@ final class LombokFileObjects {
@Override public Method getDecoderMethod() {
synchronized (decoderIsSet) {
if (decoderIsSet.get()) return decoderMethod;
- decoderMethod = getDecoderMethod("com.sun.tools.javac.util.BaseFileObject");
+ decoderMethod = LombokFileObjects.getDecoderMethod("com.sun.tools.javac.util.BaseFileObject");
decoderIsSet.set(true);
return decoderMethod;
}
}
- },
- JAVAC7 {
+ };
+ Compiler JAVAC7 = new Compiler() {
private Method decoderMethod = null;
private final AtomicBoolean decoderIsSet = new AtomicBoolean();
@@ -63,46 +75,82 @@ final class LombokFileObjects {
@Override public Method getDecoderMethod() {
synchronized (decoderIsSet) {
if (decoderIsSet.get()) return decoderMethod;
- decoderMethod = getDecoderMethod("com.sun.tools.javac.file.BaseFileObject");
+ decoderMethod = LombokFileObjects.getDecoderMethod("com.sun.tools.javac.file.BaseFileObject");
decoderIsSet.set(true);
return decoderMethod;
}
}
};
- static Method getDecoderMethod(String className) {
- Method m = null;
- try {
- m = Class.forName(className).getDeclaredMethod("getDecoder", boolean.class);
- m.setAccessible(true);
- } catch (NoSuchMethodException e) {
- // Intentional fallthrough - getDecoder(boolean) is not always present.
- } catch (ClassNotFoundException e) {
- // Intentional fallthrough - getDecoder(boolean) is not always present.
- }
- return m;
- }
+ JavaFileObject wrap(LombokFileObject fileObject);
+ Method getDecoderMethod();
+ }
- abstract JavaFileObject wrap(LombokFileObject fileObject);
- abstract Method getDecoderMethod();
+ static Method getDecoderMethod(String className) {
+ Method m = null;
+ try {
+ m = Class.forName(className).getDeclaredMethod("getDecoder", boolean.class);
+ m.setAccessible(true);
+ } catch (NoSuchMethodException e) {
+ // Intentional fallthrough - getDecoder(boolean) is not always present.
+ } catch (ClassNotFoundException e) {
+ // Intentional fallthrough - getDecoder(boolean) is not always present.
+ }
+ return m;
}
private LombokFileObjects() {}
+ private static final List<String> KNOWN_JAVA9_FILE_MANAGERS = Arrays.asList(
+ "com.google.errorprone.MaskedClassLoader$MaskedFileManager",
+ "com.google.devtools.build.buildjar.javac.BlazeJavacMain$ClassloaderMaskingFileManager",
+ "com.google.devtools.build.java.turbine.javac.JavacTurbineCompiler$ClassloaderMaskingFileManager",
+ "org.netbeans.modules.java.source.parsing.ProxyFileManager",
+ "com.sun.tools.javac.api.ClientCodeWrapper$WrappedStandardJavaFileManager",
+ "com.sun.tools.javac.main.DelegatingJavaFileManager$DelegatingSJFM" // IntelliJ + JDK10
+ );
+
static Compiler getCompiler(JavaFileManager jfm) {
String jfmClassName = jfm != null ? jfm.getClass().getName() : "null";
if (jfmClassName.equals("com.sun.tools.javac.util.DefaultFileManager")) return Compiler.JAVAC6;
if (jfmClassName.equals("com.sun.tools.javac.util.JavacFileManager")) return Compiler.JAVAC6;
- if (jfmClassName.equals("com.sun.tools.javac.file.JavacFileManager")) return Compiler.JAVAC7;
+ if (jfmClassName.equals("com.sun.tools.javac.file.JavacFileManager")) {
+ try {
+ Class<?> superType = Class.forName("com.sun.tools.javac.file.BaseFileManager");
+ if (superType.isInstance(jfm)) {
+ return new Java9Compiler(jfm);
+ }
+ }
+ catch (Throwable e) {}
+ return Compiler.JAVAC7;
+ }
+ if (KNOWN_JAVA9_FILE_MANAGERS.contains(jfmClassName)) {
+ try {
+ return new Java9Compiler(jfm);
+ }
+ catch (Throwable e) {}
+ }
+ try {
+ if (Class.forName("com.sun.tools.javac.file.PathFileObject") == null) throw new NullPointerException();
+ return new Java9Compiler(jfm);
+ } catch (Throwable e) {}
try {
if (Class.forName("com.sun.tools.javac.file.BaseFileObject") == null) throw new NullPointerException();
return Compiler.JAVAC7;
- } catch (Exception e) {}
+ } catch (Throwable e) {}
try {
if (Class.forName("com.sun.tools.javac.util.BaseFileObject") == null) throw new NullPointerException();
return Compiler.JAVAC6;
- } catch (Exception e) {}
- return null;
+ } catch (Throwable e) {}
+
+ StringBuilder sb = new StringBuilder(jfmClassName);
+ if (jfm != null) {
+ sb.append(" extends ").append(jfm.getClass().getSuperclass().getName());
+ for (Class<?> cls : jfm.getClass().getInterfaces()) {
+ sb.append(" implements ").append(cls.getName());
+ }
+ }
+ throw new IllegalArgumentException(sb.toString());
}
static JavaFileObject createEmpty(Compiler compiler, String name, Kind kind) {
@@ -112,4 +160,113 @@ final class LombokFileObjects {
static JavaFileObject createIntercepting(Compiler compiler, JavaFileObject delegate, String fileName, DiagnosticsReceiver diagnostics) {
return compiler.wrap(new InterceptingJavaFileObject(delegate, fileName, diagnostics, compiler.getDecoderMethod()));
}
+
+ static class Java9Compiler implements Compiler {
+ private final BaseFileManager fileManager;
+
+ public Java9Compiler(JavaFileManager jfm) {
+ fileManager = asBaseFileManager(jfm);
+ }
+
+ @Override public JavaFileObject wrap(LombokFileObject fileObject) {
+ return new Javac9BaseFileObjectWrapper(fileManager, toPath(fileObject), fileObject);
+ }
+
+ @Override public Method getDecoderMethod() {
+ return null;
+ }
+
+ private static Path toPath(LombokFileObject fileObject) {
+ URI uri = fileObject.toUri();
+ if (uri.getScheme() == null) {
+ uri = URI.create("file:///" + uri);
+ }
+ try {
+ return Paths.get(uri);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Problems in URI '" + uri + "' (" + fileObject.toUri() + ")", e);
+ }
+ }
+
+ private static BaseFileManager asBaseFileManager(JavaFileManager jfm) {
+ if (jfm instanceof BaseFileManager) {
+ return (BaseFileManager) jfm;
+ }
+ return new FileManagerWrapper(jfm);
+ }
+
+ static class FileManagerWrapper extends BaseFileManager {
+ JavaFileManager manager;
+
+ public FileManagerWrapper(JavaFileManager manager) {
+ super(null);
+ this.manager = manager;
+ }
+
+ @Override
+ public int isSupportedOption(String option) {
+ return manager.isSupportedOption(option);
+ }
+
+ @Override
+ public ClassLoader getClassLoader(Location location) {
+ return manager.getClassLoader(location);
+ }
+
+ @Override
+ public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException {
+ return manager.list(location, packageName, kinds, recurse);
+ }
+
+ @Override
+ public String inferBinaryName(Location location, JavaFileObject file) {
+ return manager.inferBinaryName(location, file);
+ }
+
+ @Override
+ public boolean isSameFile(FileObject a, FileObject b) {
+ return manager.isSameFile(a, b);
+ }
+
+ @Override
+ public boolean handleOption(String current, Iterator<String> remaining) {
+ return manager.handleOption(current, remaining);
+ }
+
+ @Override
+ public boolean hasLocation(Location location) {
+ return manager.hasLocation(location);
+ }
+
+ @Override
+ public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException {
+ return manager.getJavaFileForInput(location, className, kind);
+ }
+
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException {
+ return manager.getJavaFileForOutput(location, className, kind, sibling);
+ }
+
+ @Override
+ public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
+ return manager.getFileForInput(location, packageName, relativeName);
+ }
+
+ @Override
+ public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {
+ return manager.getFileForOutput(location, packageName, relativeName, sibling);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ manager.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ manager.close();
+ }
+ }
+ }
}
diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java
new file mode 100644
index 00000000..9c0a2dfa
--- /dev/null
+++ b/src/core/lombok/javac/apt/LombokProcessor.java
@@ -0,0 +1,402 @@
+/*
+ * 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
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.apt;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+
+import lombok.Lombok;
+import lombok.core.DiagnosticsReceiver;
+import lombok.javac.JavacTransformer;
+
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.jvm.ClassWriter;
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.processing.JavacFiler;
+import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.util.Context;
+
+/**
+ * This Annotation Processor is the standard injection mechanism for lombok-enabling the javac compiler.
+ *
+ * To actually enable lombok in a javac compilation run, this class should be in the classpath when
+ * running javac; that's the only requirement.
+ */
+@SupportedAnnotationTypes("*")
+public class LombokProcessor extends AbstractProcessor {
+ private JavacProcessingEnvironment processingEnv;
+ private JavacTransformer transformer;
+ private Trees trees;
+ private boolean lombokDisabled = false;
+
+ /** {@inheritDoc} */
+ @Override public void init(ProcessingEnvironment procEnv) {
+ super.init(procEnv);
+ if (System.getProperty("lombok.disable") != null) {
+ lombokDisabled = true;
+ return;
+ }
+
+ this.processingEnv = (JavacProcessingEnvironment) procEnv;
+
+ placePostCompileAndDontMakeForceRoundDummiesHook();
+ trees = Trees.instance(procEnv);
+ transformer = new JavacTransformer(procEnv.getMessager(), trees);
+ SortedSet<Long> p = transformer.getPriorities();
+ if (p.isEmpty()) {
+ this.priorityLevels = new long[] {0L};
+ this.priorityLevelsRequiringResolutionReset = new HashSet<Long>();
+ } else {
+ this.priorityLevels = new long[p.size()];
+ int i = 0;
+ for (Long prio : p) this.priorityLevels[i++] = prio;
+ this.priorityLevelsRequiringResolutionReset = transformer.getPrioritiesRequiringResolutionReset();
+ }
+ }
+
+ private static final String JPE = "com.sun.tools.javac.processing.JavacProcessingEnvironment";
+ private static final Field javacProcessingEnvironment_discoveredProcs = getFieldAccessor(JPE, "discoveredProcs");
+ private static final Field discoveredProcessors_procStateList = getFieldAccessor(JPE + "$DiscoveredProcessors", "procStateList");
+ private static final Field processorState_processor = getFieldAccessor(JPE + "$processor", "processor");
+
+ 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;
+ } catch (ClassNotFoundException e) {
+ return null;
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+ }
+
+ // The intent of this method is to have lombok emit a warning if it's not 'first in line'. However, pragmatically speaking, you're always looking at one of two cases:
+ // (A) The other processor(s) running before lombok require lombok to have run or they crash. So, they crash, and unfortunately we are never even init-ed; the warning is never emitted.
+ // (B) The other processor(s) don't care about it at all. So, it doesn't actually matter that lombok isn't first.
+ // Hence, for now, no warnings.
+ @SuppressWarnings("unused")
+ private String listAnnotationProcessorsBeforeOurs() {
+ try {
+ Object discoveredProcessors = javacProcessingEnvironment_discoveredProcs.get(this.processingEnv);
+ ArrayList<?> states = (ArrayList<?>) discoveredProcessors_procStateList.get(discoveredProcessors);
+ if (states == null || states.isEmpty()) return null;
+ if (states.size() == 1) return processorState_processor.get(states.get(0)).getClass().getName();
+
+ int idx = 0;
+ StringBuilder out = new StringBuilder();
+ for (Object processState : states) {
+ idx++;
+ String name = processorState_processor.get(processState).getClass().getName();
+ if (out.length() > 0) out.append(", ");
+ out.append("[").append(idx).append("] ").append(name);
+ }
+ return out.toString();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private void placePostCompileAndDontMakeForceRoundDummiesHook() {
+ stopJavacProcessingEnvironmentFromClosingOurClassloader();
+
+ forceMultipleRoundsInNetBeansEditor();
+ Context context = processingEnv.getContext();
+ disablePartialReparseInNetBeansEditor(context);
+ try {
+ Method keyMethod = Context.class.getDeclaredMethod("key", Class.class);
+ keyMethod.setAccessible(true);
+ Object key = keyMethod.invoke(context, JavaFileManager.class);
+ Field htField = Context.class.getDeclaredField("ht");
+ htField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<Object,Object> ht = (Map<Object,Object>) htField.get(context);
+ final JavaFileManager originalFiler = (JavaFileManager) ht.get(key);
+ if (!(originalFiler instanceof InterceptingJavaFileManager)) {
+ final Messager messager = processingEnv.getMessager();
+ DiagnosticsReceiver receiver = new MessagerDiagnosticsReceiver(messager);
+
+ JavaFileManager newFiler = new InterceptingJavaFileManager(originalFiler, receiver);
+ ht.put(key, newFiler);
+ Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager");
+ filerFileManagerField.setAccessible(true);
+ filerFileManagerField.set(processingEnv.getFiler(), newFiler);
+
+ replaceFileManagerJdk9(context, newFiler);
+ }
+ } catch (Exception e) {
+ throw Lombok.sneakyThrow(e);
+ }
+ }
+
+ private void replaceFileManagerJdk9(Context context, JavaFileManager newFiler) {
+ try {
+ JavaCompiler compiler = (JavaCompiler) JavaCompiler.class.getDeclaredMethod("instance", Context.class).invoke(null, context);
+ try {
+ Field fileManagerField = JavaCompiler.class.getDeclaredField("fileManager");
+ fileManagerField.setAccessible(true);
+ fileManagerField.set(compiler, newFiler);
+ }
+ catch (Exception e) {}
+
+ try {
+ Field writerField = JavaCompiler.class.getDeclaredField("writer");
+ writerField.setAccessible(true);
+ ClassWriter writer = (ClassWriter) writerField.get(compiler);
+ Field fileManagerField = ClassWriter.class.getDeclaredField("fileManager");
+ fileManagerField.setAccessible(true);
+ fileManagerField.set(writer, newFiler);
+ }
+ catch (Exception e) {}
+ }
+ catch (Exception e) {
+ }
+ }
+
+ private void forceMultipleRoundsInNetBeansEditor() {
+ try {
+ Field f = JavacProcessingEnvironment.class.getDeclaredField("isBackgroundCompilation");
+ f.setAccessible(true);
+ f.set(processingEnv, true);
+ } catch (NoSuchFieldException e) {
+ // only NetBeans has it
+ } catch (Throwable t) {
+ throw Lombok.sneakyThrow(t);
+ }
+ }
+
+ private void disablePartialReparseInNetBeansEditor(Context context) {
+ try {
+ Class<?> cancelServiceClass = Class.forName("com.sun.tools.javac.util.CancelService");
+ Method cancelServiceInstace = cancelServiceClass.getDeclaredMethod("instance", Context.class);
+ Object cancelService = cancelServiceInstace.invoke(null, context);
+ if (cancelService == null) return;
+ Field parserField = cancelService.getClass().getDeclaredField("parser");
+ parserField.setAccessible(true);
+ Object parser = parserField.get(cancelService);
+ Field supportsReparseField = parser.getClass().getDeclaredField("supportsReparse");
+ supportsReparseField.setAccessible(true);
+ supportsReparseField.set(parser, false);
+ } catch (ClassNotFoundException e) {
+ // only NetBeans has it
+ } catch (NoSuchFieldException e) {
+ // only NetBeans has it
+ } catch (Throwable t) {
+ throw Lombok.sneakyThrow(t);
+ }
+ }
+
+ private static ClassLoader wrapClassLoader(final ClassLoader parent) {
+ return new ClassLoader() {
+
+ public Class<?> loadClass(String name) throws ClassNotFoundException {
+ return parent.loadClass(name);
+ }
+
+ public String toString() {
+ return parent.toString();
+ }
+
+ public URL getResource(String name) {
+ return parent.getResource(name);
+ }
+
+ public Enumeration<URL> getResources(String name) throws IOException {
+ return parent.getResources(name);
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return parent.getResourceAsStream(name);
+ }
+
+ public void setDefaultAssertionStatus(boolean enabled) {
+ parent.setDefaultAssertionStatus(enabled);
+ }
+
+ public void setPackageAssertionStatus(String packageName, boolean enabled) {
+ parent.setPackageAssertionStatus(packageName, enabled);
+ }
+
+ public void setClassAssertionStatus(String className, boolean enabled) {
+ parent.setClassAssertionStatus(className, enabled);
+ }
+
+ public void clearAssertionStatus() {
+ parent.clearAssertionStatus();
+ }
+ };
+ }
+
+ private void stopJavacProcessingEnvironmentFromClosingOurClassloader() {
+ try {
+ Field f = JavacProcessingEnvironment.class.getDeclaredField("processorClassLoader");
+ f.setAccessible(true);
+ ClassLoader unwrapped = (ClassLoader) f.get(processingEnv);
+ if (unwrapped == null) return;
+ ClassLoader wrapped = wrapClassLoader(unwrapped);
+ f.set(processingEnv, wrapped);
+ } catch (NoSuchFieldException e) {
+ // Some versions of javac have this (and call close on it), some don't. I guess this one doesn't have it.
+ } catch (Throwable t) {
+ throw Lombok.sneakyThrow(t);
+ }
+ }
+
+ private final IdentityHashMap<JCCompilationUnit, Long> roots = new IdentityHashMap<JCCompilationUnit, Long>();
+ private long[] priorityLevels;
+ private Set<Long> priorityLevelsRequiringResolutionReset;
+
+ /** {@inheritDoc} */
+ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (lombokDisabled) return false;
+ if (roundEnv.processingOver()) return false;
+
+ // We have: A sorted set of all priority levels: 'priorityLevels'
+
+ // Step 1: Take all CUs which aren't already in the map. Give them the first priority level.
+
+ String randomModuleName = null;
+
+ for (Element element : roundEnv.getRootElements()) {
+ if (randomModuleName == null) randomModuleName = getModuleNameFor(element);
+ JCCompilationUnit unit = toUnit(element);
+ if (unit == null) continue;
+ if (roots.containsKey(unit)) continue;
+ roots.put(unit, priorityLevels[0]);
+ }
+
+ while (true) {
+ // Step 2: For all CUs (in the map, not the roundEnv!), run them across all handlers at their current prio level.
+
+ for (long prio : priorityLevels) {
+ List<JCCompilationUnit> cusForThisRound = new ArrayList<JCCompilationUnit>();
+ for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) {
+ Long prioOfCu = entry.getValue();
+ if (prioOfCu == null || prioOfCu != prio) continue;
+ cusForThisRound.add(entry.getKey());
+ }
+ transformer.transform(prio, processingEnv.getContext(), cusForThisRound);
+ }
+
+ // Step 3: Push up all CUs to the next level. Set level to null if there is no next level.
+
+ Set<Long> newLevels = new HashSet<Long>();
+ for (int i = priorityLevels.length - 1; i >= 0; i--) {
+ Long curLevel = priorityLevels[i];
+ Long nextLevel = (i == priorityLevels.length - 1) ? null : priorityLevels[i + 1];
+ List<JCCompilationUnit> cusToAdvance = new ArrayList<JCCompilationUnit>();
+ for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) {
+ if (curLevel.equals(entry.getValue())) {
+ cusToAdvance.add(entry.getKey());
+ newLevels.add(nextLevel);
+ }
+ }
+ for (JCCompilationUnit unit : cusToAdvance) {
+ roots.put(unit, nextLevel);
+ }
+ }
+ newLevels.remove(null);
+
+ // Step 4: If ALL values are null, quit. Else, either do another loop right now or force a resolution reset by forcing a new round in the annotation processor.
+
+ if (newLevels.isEmpty()) return false;
+ newLevels.retainAll(priorityLevelsRequiringResolutionReset);
+ if (!newLevels.isEmpty()) {
+ // Force a new round to reset resolution. The next round will cause this method (process) to be called again.
+ forceNewRound(randomModuleName, (JavacFiler) processingEnv.getFiler());
+ return false;
+ }
+ // None of the new levels need resolution, so just keep going.
+ }
+ }
+
+ private int dummyCount = 0;
+ private void forceNewRound(String randomModuleName, JavacFiler filer) {
+ if (!filer.newFiles()) {
+ try {
+ String name = "lombok.dummy.ForceNewRound" + (dummyCount++);
+ if (randomModuleName != null) name = randomModuleName + "/" + name;
+ JavaFileObject dummy = filer.createSourceFile(name);
+ Writer w = dummy.openWriter();
+ w.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ processingEnv.getMessager().printMessage(Kind.WARNING,
+ "Can't force a new processing round. Lombok won't work.");
+ }
+ }
+ }
+
+ private String getModuleNameFor(Element element) {
+ while (element != null) {
+ if (element.getKind().name().equals("MODULE")) {
+ String n = element.getSimpleName().toString().trim();
+ return n.isEmpty() ? null : n;
+ }
+ Element n = element.getEnclosingElement();
+ if (n == element) return null;
+ element = n;
+ }
+ return null;
+ }
+
+ private JCCompilationUnit toUnit(Element element) {
+ TreePath path = trees == null ? null : trees.getPath(element);
+ if (path == null) return null;
+
+ return (JCCompilationUnit) path.getCompilationUnit();
+ }
+
+ /**
+ * We just return the latest version of whatever JDK we run on. Stupid? Yeah, but it's either that or warnings on all versions but 1.
+ */
+ @Override public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+}
diff --git a/src/core/lombok/javac/apt/Processor.java b/src/core/lombok/javac/apt/Processor.java
index ce4d75ff..7a187148 100644
--- a/src/core/lombok/javac/apt/Processor.java
+++ b/src/core/lombok/javac/apt/Processor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,295 +21,211 @@
*/
package lombok.javac.apt;
+import static javax.tools.StandardLocation.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.Writer;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.List;
import java.util.Map;
+import java.util.Properties;
import java.util.Set;
-import java.util.SortedSet;
import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Messager;
+import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic.Kind;
+import javax.tools.Diagnostic;
import javax.tools.JavaFileManager;
-import javax.tools.JavaFileObject;
-
-import lombok.Lombok;
-import lombok.core.DiagnosticsReceiver;
-import lombok.javac.JavacTransformer;
-import com.sun.source.util.TreePath;
-import com.sun.source.util.Trees;
import com.sun.tools.javac.processing.JavacFiler;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
-import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
-import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Options;
/**
- * This Annotation Processor is the standard injection mechanism for lombok-enabling the javac compiler.
- *
- * To actually enable lombok in a javac compilation run, this class should be in the classpath when
- * running javac; that's the only requirement.
+ * 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.
*/
+@Deprecated
@SupportedAnnotationTypes("*")
public class Processor extends AbstractProcessor {
-
- private JavacProcessingEnvironment processingEnv;
- private JavacTransformer transformer;
- private Trees trees;
- private boolean lombokDisabled = false;
/** {@inheritDoc} */
@Override public void init(ProcessingEnvironment procEnv) {
super.init(procEnv);
if (System.getProperty("lombok.disable") != null) {
- lombokDisabled = true;
return;
}
-
- this.processingEnv = (JavacProcessingEnvironment) procEnv;
- placePostCompileAndDontMakeForceRoundDummiesHook();
- transformer = new JavacTransformer(procEnv.getMessager());
- trees = Trees.instance(procEnv);
- SortedSet<Long> p = transformer.getPriorities();
- if (p.isEmpty()) {
- this.priorityLevels = new long[] {0L};
- this.priorityLevelsRequiringResolutionReset = new HashSet<Long>();
- } else {
- this.priorityLevels = new long[p.size()];
- int i = 0;
- for (Long prio : p) this.priorityLevels[i++] = prio;
- this.priorityLevelsRequiringResolutionReset = transformer.getPrioritiesRequiringResolutionReset();
- }
+ procEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, "Wrong usage of 'lombok.javac.apt.Processor'. " + report(procEnv));
}
- private void placePostCompileAndDontMakeForceRoundDummiesHook() {
- stopJavacProcessingEnvironmentFromClosingOurClassloader();
-
- forceMultipleRoundsInNetBeansEditor();
- Context context = processingEnv.getContext();
- disablePartialReparseInNetBeansEditor(context);
+ private String report(ProcessingEnvironment procEnv) {
+ String data = collectData(procEnv);
try {
- Method keyMethod = Context.class.getDeclaredMethod("key", Class.class);
- keyMethod.setAccessible(true);
- Object key = keyMethod.invoke(context, JavaFileManager.class);
- Field htField = Context.class.getDeclaredField("ht");
- htField.setAccessible(true);
- @SuppressWarnings("unchecked")
- Map<Object,Object> ht = (Map<Object,Object>) htField.get(context);
- final JavaFileManager originalFiler = (JavaFileManager) ht.get(key);
-
- if (!(originalFiler instanceof InterceptingJavaFileManager)) {
- final Messager messager = processingEnv.getMessager();
- DiagnosticsReceiver receiver = new MessagerDiagnosticsReceiver(messager);
-
- JavaFileManager newFiler = new InterceptingJavaFileManager(originalFiler, receiver);
- ht.put(key, newFiler);
- Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager");
- filerFileManagerField.setAccessible(true);
- filerFileManagerField.set(processingEnv.getFiler(), newFiler);
- }
+ return writeFile(data);
} catch (Exception e) {
- throw Lombok.sneakyThrow(e);
+ return "Report:\n\n" + data;
}
}
- private void forceMultipleRoundsInNetBeansEditor() {
- try {
- Field f = JavacProcessingEnvironment.class.getDeclaredField("isBackgroundCompilation");
- f.setAccessible(true);
- f.set(processingEnv, true);
- } catch (NoSuchFieldException e) {
- // only NetBeans has it
- } catch (Throwable t) {
- throw Lombok.sneakyThrow(t);
- }
+ private String writeFile(String data) throws IOException {
+ File file = File.createTempFile("lombok-processor-report-", ".txt");
+ OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file));
+ writer.write(data);
+ writer.close();
+ return "Report written to '" + file.getCanonicalPath() + "'\n";
}
-
- private void disablePartialReparseInNetBeansEditor(Context context) {
- try {
- Class<?> cancelServiceClass = Class.forName("com.sun.tools.javac.util.CancelService");
- Method cancelServiceInstace = cancelServiceClass.getDeclaredMethod("instance", Context.class);
- Object cancelService = cancelServiceInstace.invoke(null, context);
- if (cancelService == null) return;
- Field parserField = cancelService.getClass().getDeclaredField("parser");
- parserField.setAccessible(true);
- Object parser = parserField.get(cancelService);
- Field supportsReparseField = parser.getClass().getDeclaredField("supportsReparse");
- supportsReparseField.setAccessible(true);
- supportsReparseField.set(parser, false);
- } catch (ClassNotFoundException e) {
- // only NetBeans has it
- } catch (NoSuchFieldException e) {
- // only NetBeans has it
- } catch (Throwable t) {
- throw Lombok.sneakyThrow(t);
- }
+
+ private String collectData(ProcessingEnvironment procEnv) {
+ StringBuilder message = new StringBuilder();
+ message.append("Problem report for usages of 'lombok.javac.apt.Processor'\n\n");
+ listOptions(message, procEnv);
+ findServices(message, procEnv.getFiler());
+ addStacktrace(message);
+ listProperties(message);
+ return message.toString();
}
- private static ClassLoader wrapClassLoader(final ClassLoader parent) {
- return new ClassLoader() {
-
- public Class<?> loadClass(String name) throws ClassNotFoundException {
- return parent.loadClass(name);
- }
-
- public String toString() {
- return parent.toString();
- }
-
- public URL getResource(String name) {
- return parent.getResource(name);
- }
-
- public Enumeration<URL> getResources(String name) throws IOException {
- return parent.getResources(name);
- }
-
- public InputStream getResourceAsStream(String name) {
- return parent.getResourceAsStream(name);
- }
-
- public void setDefaultAssertionStatus(boolean enabled) {
- parent.setDefaultAssertionStatus(enabled);
- }
-
- public void setPackageAssertionStatus(String packageName, boolean enabled) {
- parent.setPackageAssertionStatus(packageName, enabled);
- }
-
- public void setClassAssertionStatus(String className, boolean enabled) {
- parent.setClassAssertionStatus(className, enabled);
+ private void listOptions(StringBuilder message, ProcessingEnvironment procEnv) {
+ try {
+ JavacProcessingEnvironment environment = (JavacProcessingEnvironment) procEnv;
+ Options instance = Options.instance(environment.getContext());
+ Field field = Options.class.getDeclaredField("values");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked") Map<String, String> values = (Map<String, String>) field.get(instance);
+ if (values.isEmpty()) {
+ message.append("Options: empty\n\n");
+ return;
}
-
- public void clearAssertionStatus() {
- parent.clearAssertionStatus();
+ message.append("Compiler Options:\n");
+ for (Map.Entry<String, String> value : values.entrySet()) {
+ message.append("- ");
+ string(message, value.getKey());
+ message.append(" = ");
+ string(message, value.getValue());
+ message.append("\n");
}
- };
- }
-
- private void stopJavacProcessingEnvironmentFromClosingOurClassloader() {
- try {
- Field f = JavacProcessingEnvironment.class.getDeclaredField("processorClassLoader");
- f.setAccessible(true);
- ClassLoader unwrapped = (ClassLoader) f.get(processingEnv);
- if (unwrapped == null) return;
- ClassLoader wrapped = wrapClassLoader(unwrapped);
- f.set(processingEnv, wrapped);
- } catch (NoSuchFieldException e) {
- // Some versions of javac have this (and call close on it), some don't. I guess this one doesn't have it.
- } catch (Throwable t) {
- throw Lombok.sneakyThrow(t);
- }
- }
-
- private final IdentityHashMap<JCCompilationUnit, Long> roots = new IdentityHashMap<JCCompilationUnit, Long>();
- private long[] priorityLevels;
- private Set<Long> priorityLevelsRequiringResolutionReset;
-
- /** {@inheritDoc} */
- @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- if (lombokDisabled) return false;
- if (roundEnv.processingOver()) return false;
-
- // We have: A sorted set of all priority levels: 'priorityLevels'
-
- // Step 1: Take all CUs which aren't already in the map. Give them the first priority level.
-
- for (Element element : roundEnv.getRootElements()) {
- JCCompilationUnit unit = toUnit(element);
- if (unit == null) continue;
- if (roots.containsKey(unit)) continue;
- roots.put(unit, priorityLevels[0]);
+ message.append("\n");
+ } catch (Exception e) {
+ message.append("No options available\n\n");
}
- while (true) {
- // Step 2: For all CUs (in the map, not the roundEnv!), run them across all handlers at their current prio level.
-
- for (long prio : priorityLevels) {
- List<JCCompilationUnit> cusForThisRound = new ArrayList<JCCompilationUnit>();
- for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) {
- Long prioOfCu = entry.getValue();
- if (prioOfCu == null || prioOfCu != prio) continue;
- cusForThisRound.add(entry.getKey());
- }
- transformer.transform(prio, processingEnv.getContext(), cusForThisRound);
+ }
+
+ private void findServices(StringBuilder message, Filer filer) {
+ try {
+ Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager");
+ filerFileManagerField.setAccessible(true);
+ 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");
+ if (!resources.hasMoreElements()) {
+ message.append("No processors discovered\n\n");
+ return;
}
-
- // Step 3: Push up all CUs to the next level. Set level to null if there is no next level.
-
- Set<Long> newLevels = new HashSet<Long>();
- for (int i = priorityLevels.length - 1; i >= 0; i--) {
- Long curLevel = priorityLevels[i];
- Long nextLevel = (i == priorityLevels.length - 1) ? null : priorityLevels[i + 1];
- List<JCCompilationUnit> cusToAdvance = new ArrayList<JCCompilationUnit>();
- for (Map.Entry<JCCompilationUnit, Long> entry : roots.entrySet()) {
- if (curLevel.equals(entry.getValue())) {
- cusToAdvance.add(entry.getKey());
- newLevels.add(nextLevel);
+ message.append("Discovered processors:\n");
+ while (resources.hasMoreElements()) {
+ URL processorUrl = resources.nextElement();
+ message.append("- '").append(processorUrl).append("'");
+ InputStream content = (InputStream) processorUrl.getContent();
+ if (content != null) try {
+ InputStreamReader reader = new InputStreamReader(content, "UTF-8");
+ StringWriter sw = new StringWriter();
+ char[] buffer = new char[8192];
+ int read = 0;
+ while ((read = reader.read(buffer))!= -1) {
+ sw.write(buffer, 0, read);
}
- }
- for (JCCompilationUnit unit : cusToAdvance) {
- roots.put(unit, nextLevel);
+ String wholeFile = sw.toString();
+ if (wholeFile.contains("lombok.javac.apt.Processor")) {
+ message.append(" <= problem\n");
+ } else {
+ message.append(" (ok)\n");
+ }
+ message.append(" ").append(wholeFile.replace("\n", "\n ")).append("\n");
+ } finally {
+ content.close();
}
}
- newLevels.remove(null);
-
- // Step 4: If ALL values are null, quit. Else, either do another loop right now or force a resolution reset by forcing a new round in the annotation processor.
-
- if (newLevels.isEmpty()) return false;
- newLevels.retainAll(priorityLevelsRequiringResolutionReset);
- if (newLevels.isEmpty()) {
- // None of the new levels need resolution, so just keep going.
- continue;
- } else {
- // Force a new round to reset resolution. The next round will cause this method (process) to be called again.
- forceNewRound((JavacFiler) processingEnv.getFiler());
- return false;
- }
+ } catch (Exception e) {
+ message.append("Filer information unavailable\n");
}
+ message.append("\n");
}
-
- private int dummyCount = 0;
- private void forceNewRound(JavacFiler filer) {
- if (!filer.newFiles()) {
- try {
- JavaFileObject dummy = filer.createSourceFile("lombok.dummy.ForceNewRound" + (dummyCount++));
- Writer w = dummy.openWriter();
- w.close();
- } catch (Exception e) {
- e.printStackTrace();
- processingEnv.getMessager().printMessage(Kind.WARNING,
- "Can't force a new processing round. Lombok won't work.");
+
+ private void addStacktrace(StringBuilder message) {
+ StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+ if (stackTraceElements != null) {
+ message.append("Called from\n");
+ for (int i = 1; i < stackTraceElements.length; i++) {
+ StackTraceElement element = stackTraceElements[i];
+ if (!element.getClassName().equals("lombok.javac.apt.Processor")) message.append("- ").append(element).append("\n");
}
+ } else {
+ message.append("No stacktrace available\n");
}
+ message.append("\n");
+ }
+
+ private void listProperties(StringBuilder message) {
+ Properties properties = System.getProperties();
+ ArrayList<String> propertyNames = new ArrayList<String>(properties.stringPropertyNames());
+ Collections.sort(propertyNames);
+ message.append("Properties: \n");
+ for (String propertyName : propertyNames) {
+ if (propertyName.startsWith("user.")) continue;
+ message.append("- ").append(propertyName).append(" = ");
+ string(message, System.getProperty(propertyName));
+ message.append("\n");
+ }
+ message.append("\n");
}
- private JCCompilationUnit toUnit(Element element) {
- TreePath path = trees == null ? null : trees.getPath(element);
- if (path == null) return null;
-
- return (JCCompilationUnit) path.getCompilationUnit();
+ private static void string(StringBuilder sb, String s) {
+ if (s == null) {
+ sb.append("null");
+ return;
+ }
+ sb.append("\"");
+ for (int i = 0; i < s.length(); i++) sb.append(escape(s.charAt(i)));
+ sb.append("\"");
}
+ private static String escape(char ch) {
+ switch (ch) {
+ case '\b': return "\\b";
+ case '\f': return "\\f";
+ case '\n': return "\\n";
+ case '\r': return "\\r";
+ case '\t': return "\\t";
+ case '\'': return "\\'";
+ case '\"': return "\\\"";
+ case '\\': return "\\\\";
+ default:
+ if (ch < 32) return String.format("\\%03o", (int) ch);
+ return String.valueOf(ch);
+ }
+ }
+
/**
* We just return the latest version of whatever JDK we run on. Stupid? Yeah, but it's either that or warnings on all versions but 1.
*/
@Override public SourceVersion getSupportedSourceVersion() {
return SourceVersion.values()[SourceVersion.values().length - 1];
}
+
+ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return false;
+ }
}
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index 1885b8b4..86ac00e6 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-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
@@ -22,18 +22,22 @@
package lombok.javac.handlers;
import java.util.ArrayList;
-import java.util.Collections;
+
+import javax.lang.model.element.Modifier;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
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.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCIf;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
@@ -46,16 +50,22 @@ import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Builder.ObtainVia;
import lombok.ConfigurationKeys;
+import lombok.Singular;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
-import lombok.experimental.Builder;
+import lombok.core.handlers.HandlerUtil;
import lombok.experimental.NonFinal;
+import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import static lombok.javac.Javac.*;
@@ -64,13 +74,41 @@ import static lombok.javac.JavacTreeMaker.TypeTag.*;
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends JavacAnnotationHandler<Builder> {
- @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder");
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
+ private static final boolean toBoolean(Object expr, boolean defaultValue) {
+ if (expr == null) return defaultValue;
+ if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0;
+ return ((Boolean) expr).booleanValue();
+ }
+
+ private static class BuilderFieldData {
+ JCExpression type;
+ Name rawName;
+ Name name;
+ Name nameOfDefaultProvider;
+ Name nameOfSetFlag;
+ SingularData singularData;
+ ObtainVia obtainVia;
+ JavacNode obtainViaNode;
+ JavacNode originalFieldNode;
+ java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
+ }
+
+ @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) {
Builder builderInstance = annotation.getInstance();
+
+ // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them.
+ boolean fluent = toBoolean(annotation.getActualExpression("fluent"), true);
+ boolean chain = toBoolean(annotation.getActualExpression("chain"), true);
+
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
+ String toBuilderMethodName = "toBuilder";
+ boolean toBuilder = builderInstance.toBuilder();
+ java.util.List<Name> typeArgsForToBuilder = null;
if (builderMethodName == null) builderMethodName = "builder";
if (buildMethodName == null) buildMethodName = "build";
@@ -82,73 +120,99 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
}
- deleteAnnotationIfNeccessary(annotationNode, Builder.class);
- deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder");
+ deleteAnnotationIfNeccessary(annotationNode, Builder.class, "lombok.experimental.Builder");
JavacNode parent = annotationNode.up();
- java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>();
- java.util.List<Name> namesOfParameters = new ArrayList<Name>();
+ java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
JCExpression returnType;
List<JCTypeParameter> typeParams = List.nil();
List<JCExpression> thrownExceptions = List.nil();
- Name nameOfStaticBuilderMethod;
+ Name nameOfBuilderMethod;
JavacNode tdParent;
- JCMethodDecl fillParametersFrom = parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null;
+ JavacNode fillParametersFrom = parent.get() instanceof JCMethodDecl ? parent : null;
+ boolean addCleaning = false;
+ boolean isStatic = true;
if (parent.get() instanceof JCClassDecl) {
tdParent = parent;
JCClassDecl td = (JCClassDecl) tdParent.get();
ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>();
- @SuppressWarnings("deprecation")
- boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
- for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent));
+ for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) {
JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
- // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes
- // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves.
- // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that.
- if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue;
- namesOfParameters.add(removePrefixFromField(fieldNode));
- typesOfParameters.add(fd.vartype);
+ JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true);
+ boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode));
+ BuilderFieldData bfd = new BuilderFieldData();
+ bfd.rawName = fd.name;
+ bfd.name = removePrefixFromField(fieldNode);
+ bfd.type = fd.vartype;
+ bfd.singularData = getSingularData(fieldNode);
+ bfd.originalFieldNode = fieldNode;
+
+ if (bfd.singularData != null && isDefault != null) {
+ isDefault.addError("@Builder.Default and @Singular cannot be mixed.");
+ isDefault = null;
+ }
+
+ if (fd.init == null && isDefault != null) {
+ isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;').");
+ isDefault = null;
+ }
+
+ if (fd.init != null && isDefault == null) {
+ if (isFinal) continue;
+ fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final.");
+ }
+
+ if (isDefault != null) {
+ bfd.nameOfDefaultProvider = parent.toName("$default$" + bfd.name);
+ bfd.nameOfSetFlag = parent.toName(bfd.name + "$set");
+ JCMethodDecl md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, td.typarams);
+ recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
+ if (md != null) injectMethod(tdParent, md);
+ }
+ addObtainVia(bfd, fieldNode);
+ builderFields.add(bfd);
allFields.append(fieldNode);
}
-
- new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode);
+ handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode);
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
thrownExceptions = List.nil();
- nameOfStaticBuilderMethod = null;
+ nameOfBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) {
- if (!fillParametersFrom.typarams.isEmpty()) {
+ JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
+ if (!jmd.typarams.isEmpty()) {
annotationNode.addError("@Builder is not supported on constructors with constructor type parameters.");
return;
}
+
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
- thrownExceptions = fillParametersFrom.thrown;
- nameOfStaticBuilderMethod = null;
+ thrownExceptions = jmd.thrown;
+ nameOfBuilderMethod = null;
if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder";
} else if (fillParametersFrom != null) {
tdParent = parent.up();
JCClassDecl td = (JCClassDecl) tdParent.get();
- if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) {
- annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
- return;
+ JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
+ isStatic = (jmd.mods.flags & Flags.STATIC) != 0;
+ JCExpression fullReturnType = jmd.restype;
+ returnType = fullReturnType;
+ typeParams = jmd.typarams;
+ thrownExceptions = jmd.thrown;
+ nameOfBuilderMethod = jmd.name;
+ if (returnType instanceof JCTypeApply) {
+ returnType = cloneType(tdParent.getTreeMaker(), returnType, ast, annotationNode.getContext());
}
- returnType = fillParametersFrom.restype;
- typeParams = fillParametersFrom.typarams;
- thrownExceptions = fillParametersFrom.thrown;
- nameOfStaticBuilderMethod = fillParametersFrom.name;
if (builderClassName.isEmpty()) {
- if (returnType instanceof JCTypeApply) {
- returnType = ((JCTypeApply) returnType).clazz;
- }
if (returnType instanceof JCFieldAccess) {
builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
} else if (returnType instanceof JCIdent) {
@@ -166,96 +230,375 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (Character.isLowerCase(builderClassName.charAt(0))) {
builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
}
-
- } else {
+ } else if (returnType instanceof JCTypeApply) {
+ JCExpression clazz = ((JCTypeApply) returnType).clazz;
+ if (clazz instanceof JCFieldAccess) {
+ builderClassName = ((JCFieldAccess) clazz).name + "Builder";
+ } else if (clazz instanceof JCIdent) {
+ builderClassName = ((JCIdent) clazz).name + "Builder";
+ }
+ }
+
+ if (builderClassName.isEmpty()) {
// This shouldn't happen.
System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass());
builderClassName = td.name.toString() + "Builder";
}
}
+ if (toBuilder) {
+ final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
+ if (returnType instanceof JCArrayTypeTree) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ Name simpleName;
+ String pkg;
+ List<JCExpression> tpOnRet = List.nil();
+
+ if (fullReturnType instanceof JCTypeApply) {
+ tpOnRet = ((JCTypeApply) fullReturnType).arguments;
+ }
+
+ JCExpression namingType = returnType;
+ if (returnType instanceof JCTypeApply) namingType = ((JCTypeApply) returnType).clazz;
+
+ if (namingType instanceof JCIdent) {
+ simpleName = ((JCIdent) namingType).name;
+ pkg = null;
+ } else if (namingType instanceof JCFieldAccess) {
+ JCFieldAccess jcfa = (JCFieldAccess) namingType;
+ simpleName = jcfa.name;
+ pkg = unpack(jcfa.selected);
+ if (pkg.startsWith("ERR:")) {
+ String err = pkg.substring(4, pkg.indexOf("__ERR__"));
+ annotationNode.addError(err);
+ return;
+ }
+ } else {
+ annotationNode.addError("Expected a (parameterized) type here instead of a " + namingType.getClass().getName());
+ return;
+ }
+
+ if (pkg != null && !parent.getPackageDeclaration().equals(pkg)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (!tdParent.getName().contentEquals(simpleName)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ List<JCTypeParameter> tpOnMethod = jmd.typarams;
+ List<JCTypeParameter> tpOnType = ((JCClassDecl) tdParent.get()).typarams;
+ typeArgsForToBuilder = new ArrayList<Name>();
+
+ for (JCTypeParameter tp : tpOnMethod) {
+ int pos = -1;
+ int idx = -1;
+ for (JCExpression tOnRet : tpOnRet) {
+ idx++;
+ if (!(tOnRet instanceof JCIdent)) continue;
+ if (((JCIdent) tOnRet).name != tp.name) continue;
+ pos = idx;
+ }
+
+ if (pos == -1 || tpOnType.size() <= pos) {
+ annotationNode.addError("@Builder(toBuilder=true) requires that each type parameter on the static method is part of the typeargs of the return value. Type parameter " + tp.name + " is not part of the return type.");
+ return;
+ }
+ typeArgsForToBuilder.add(tpOnType.get(pos).name);
+ }
+ }
} else {
- annotationNode.addError("@Builder is only supported on types, constructors, and static methods.");
+ annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
return;
}
if (fillParametersFrom != null) {
- for (JCVariableDecl param : fillParametersFrom.params) {
- namesOfParameters.add(param.name);
- typesOfParameters.add(param.vartype);
+ for (JavacNode param : fillParametersFrom.down()) {
+ if (param.getKind() != Kind.ARGUMENT) continue;
+ BuilderFieldData bfd = new BuilderFieldData();
+ JCVariableDecl raw = (JCVariableDecl) param.get();
+ bfd.name = raw.name;
+ bfd.rawName = raw.name;
+ bfd.type = raw.vartype;
+ bfd.singularData = getSingularData(param);
+ bfd.originalFieldNode = param;
+ addObtainVia(bfd, param);
+ builderFields.add(bfd);
}
}
JavacNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
- builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast);
+ builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast);
} else {
+ JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get();
+ if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) {
+ annotationNode.addError("Existing Builder must be a static inner class.");
+ return;
+ } else if (!isStatic && builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) {
+ annotationNode.addError("Existing Builder must be a non-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;
+ }
+ }
+ }
}
- java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast);
- java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>();
- for (JavacNode fieldNode : fieldNodes) {
- JCMethodDecl newMethod = makeSetterMethodForBuilder(builderType, fieldNode, annotationNode, builderInstance.fluent(), builderInstance.chain());
- if (newMethod != null) newMethods.add(newMethod);
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ if (bfd.singularData.getSingularizer().requiresCleaning()) {
+ addCleaning = true;
+ break;
+ }
+ }
+ if (bfd.obtainVia != null) {
+ if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) {
+ bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\").");
+ return;
+ }
+ if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) {
+ bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set.");
+ return;
+ }
+ }
+ }
+
+ generateBuilderFields(builderType, builderFields, ast);
+ if (addCleaning) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCVariableDecl uncleanField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName("$lombokUnclean"), maker.TypeIdent(CTC_BOOLEAN), null);
+ injectFieldAndMarkGenerated(builderType, uncleanField);
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), null, annotationNode);
+ JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, annotationNode);
if (cd != null) injectMethod(builderType, cd);
}
- for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod);
+ for (BuilderFieldData bfd : builderFields) {
+ makeSetterMethodsForBuilder(builderType, bfd, annotationNode, fluent, chain);
+ }
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl md = generateBuildMethod(buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions);
+ JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
if (md != null) injectMethod(builderType, md);
}
if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ java.util.List<JavacNode> fieldNodes = new ArrayList<JavacNode>();
+ for (BuilderFieldData bfd : builderFields) {
+ fieldNodes.addAll(bfd.createdFields);
+ }
JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
if (md != null) injectMethod(builderType, md);
}
+ if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
+
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams);
+ JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, annotationNode, tdParent, typeParams);
+ recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
if (md != null) injectMethod(tdParent, md);
}
+
+ if (toBuilder) {
+ switch (methodExists(toBuilderMethodName, tdParent, 0)) {
+ case EXISTS_BY_USER:
+ annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+ return;
+ case NOT_EXISTS:
+ List<JCTypeParameter> tps = typeParams;
+ if (typeArgsForToBuilder != null) {
+ ListBuffer<JCTypeParameter> lb = new ListBuffer<JCTypeParameter>();
+ JavacTreeMaker maker = tdParent.getTreeMaker();
+ for (Name n : typeArgsForToBuilder) {
+ lb.append(maker.TypeParameter(n, List.<JCExpression>nil()));
+ }
+ tps = lb.toList();
+ }
+ JCMethodDecl md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
+ recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
+ }
+
+ private static String unpack(JCExpression expr) {
+ StringBuilder sb = new StringBuilder();
+ unpack(sb, expr);
+ return sb.toString();
+ }
+
+ private static void unpack(StringBuilder sb, JCExpression expr) {
+ if (expr instanceof JCIdent) {
+ sb.append(((JCIdent) expr).name.toString());
+ return;
+ }
+
+ if (expr instanceof JCFieldAccess) {
+ JCFieldAccess jcfa = (JCFieldAccess) expr;
+ unpack(sb, jcfa.selected);
+ sb.append(".").append(jcfa.name.toString());
+ return;
+ }
+
+ if (expr instanceof JCTypeApply) {
+ sb.setLength(0);
+ sb.append("ERR:");
+ sb.append("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate.");
+ sb.append("__ERR__");
+ return;
+ }
+
+ sb.setLength(0);
+ sb.append("ERR:");
+ sb.append("Expected a type of some sort, not a " + expr.getClass().getName());
+ sb.append("__ERR__");
}
- public JCMethodDecl generateBuildMethod(String name, Name staticName, JCExpression returnType, java.util.List<Name> fieldNames, JavacNode type, List<JCExpression> thrownExceptions) {
+ 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();
+
+ ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
+ for (JCTypeParameter typeParam : typeParams) {
+ typeArgs.append(maker.Ident(typeParam.name));
+ }
+
+ JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCExpression>nil(), null);
+ JCExpression invoke = call;
+ for (BuilderFieldData bfd : builderFields) {
+ Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString()));
+ JCExpression arg;
+ if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+ arg = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field()));
+ } else {
+ if (bfd.obtainVia.isStatic()) {
+ JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method()));
+ arg = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName("this"))));
+ } else {
+ JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method()));
+ arg = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil());
+ }
+ }
+ 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));
+ 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);
+ }
+
+ private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
+ JavacTreeMaker maker = type.getTreeMaker();
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, type, source, statements);
+ }
+ }
+
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0))));
+ JCBlock body = maker.Block(0, statements.toList());
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ /*
+ * if (shouldReturnThis) {
+ methodType = cloneSelfType(field);
+ }
+
+ if (methodType == null) {
+ //WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6.
+ methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID));
+ shouldReturnThis = false;
+ }
+
+ */
+ }
+
+ private JCMethodDecl generateBuildMethod(JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) {
JavacTreeMaker maker = type.getTreeMaker();
JCExpression call;
- JCStatement statement;
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ if (addCleaning) {
+ JCExpression notClean = maker.Unary(CTC_NOT, maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")));
+ JCStatement invokeClean = maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(type.toName("$lombokClean")), List.<JCExpression>nil()));
+ JCIf ifUnclean = maker.If(notClean, invokeClean, null);
+ statements.append(ifUnclean);
+ }
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name);
+ }
+ }
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
- for (Name n : fieldNames) {
- args.append(maker.Ident(n));
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.nameOfSetFlag != null) {
+ statements.append(maker.VarDef(maker.Modifiers(0L), bfd.name, cloneType(maker, bfd.type, source, tdParent.getContext()), maker.Select(maker.Ident(type.toName("this")), bfd.name)));
+ statements.append(maker.If(maker.Unary(CTC_NOT, maker.Ident(bfd.nameOfSetFlag)), maker.Exec(maker.Assign(maker.Ident(bfd.name),maker.Apply(typeParameterNames(maker, ((JCClassDecl) tdParent.get()).typarams), maker.Select(maker.Ident(((JCClassDecl) tdParent.get()).name), bfd.nameOfDefaultProvider), List.<JCExpression>nil()))), null));
+ }
+ args.append(maker.Ident(bfd.name));
}
- if (staticName == null) {
+ if (addCleaning) {
+ statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 1))));
+ }
+
+ if (builderName == null) {
call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
- statement = maker.Return(call);
+ statements.append(maker.Return(call));
} else {
ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>();
for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
typeParams.append(maker.Ident(tp.name));
}
-
- JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
+ JCExpression callee = maker.Ident(((JCClassDecl) type.up().get()).name);
+ if (!isStatic) callee = maker.Select(callee, type.up().toName("this"));
+ JCExpression fn = maker.Select(callee, builderName);
call = maker.Apply(typeParams.toList(), fn, args.toList());
if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) {
- statement = maker.Exec(call);
+ statements.append(maker.Exec(call));
} else {
- statement = maker.Return(call);
+ statements.append(maker.Return(call));
}
}
- JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ JCBlock body = maker.Block(0, statements.toList());
- return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(name), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
- public JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams) {
+ public JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List<JCTypeParameter> params) {
+ JavacTreeMaker maker = fieldNode.getTreeMaker();
+ JCVariableDecl field = (JCVariableDecl) fieldNode.get();
+
+ JCStatement statement = maker.Return(field.init);
+ field.init = null;
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ int modifiers = Flags.PRIVATE | Flags.STATIC;
+ return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), copyTypeParams(fieldNode, params), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
+ public JCMethodDecl generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) {
JavacTreeMaker maker = type.getTreeMaker();
ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
@@ -267,53 +610,71 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCStatement statement = maker.Return(call);
JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
- return maker.MethodDef(maker.Modifiers(Flags.STATIC | Flags.PUBLIC), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ int modifiers = Flags.PUBLIC;
+ if (isStatic) modifiers |= Flags.STATIC;
+ return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
}
- public java.util.List<JavacNode> addFieldsToBuilder(JavacNode builderType, java.util.List<Name> namesOfParameters, java.util.List<JCExpression> typesOfParameters, JCTree source) {
- int len = namesOfParameters.size();
+ public void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
+ int len = builderFields.size();
java.util.List<JavacNode> existing = new ArrayList<JavacNode>();
for (JavacNode child : builderType.down()) {
if (child.getKind() == Kind.FIELD) existing.add(child);
}
- java.util.List<JavacNode>out = new ArrayList<JavacNode>();
-
- top:
for (int i = len - 1; i >= 0; i--) {
- Name name = namesOfParameters.get(i);
- for (JavacNode exists : existing) {
- Name n = ((JCVariableDecl) exists.get()).name;
- if (n.equals(name)) {
- out.add(exists);
- continue top;
+ BuilderFieldData bfd = builderFields.get(i);
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source));
+ } else {
+ JavacNode field = null, setFlag = null;
+ for (JavacNode exists : existing) {
+ Name n = ((JCVariableDecl) exists.get()).name;
+ if (n.equals(bfd.name)) field = exists;
+ if (n.equals(bfd.nameOfSetFlag)) setFlag = exists;
+ }
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ if (field == null) {
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
+ JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null);
+ field = injectFieldAndMarkGenerated(builderType, newField);
}
+ if (setFlag == null && bfd.nameOfSetFlag != null) {
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
+ JCVariableDecl newField = maker.VarDef(mods, bfd.nameOfSetFlag, maker.TypeIdent(CTC_BOOLEAN), null);
+ injectFieldAndMarkGenerated(builderType, newField);
+ }
+ bfd.createdFields.add(field);
}
- JavacTreeMaker maker = builderType.getTreeMaker();
- JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
- JCVariableDecl newField = maker.VarDef(mods, name, cloneType(maker, typesOfParameters.get(i), source, builderType.getContext()), null);
- out.add(injectField(builderType, newField));
}
-
- Collections.reverse(out);
- return out;
}
+ public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain) {
+ boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode);
+ if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
+ makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, fluent, chain);
+ } else {
+ fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain);
+ }
+ }
- public JCMethodDecl makeSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) {
+ private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain) {
Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
for (JavacNode child : builderType.down()) {
if (child.getKind() != Kind.METHOD) continue;
- Name existingName = ((JCMethodDecl) child.get()).name;
- if (existingName.equals(fieldName)) return null;
+ JCMethodDecl methodDecl = (JCMethodDecl) child.get();
+ Name existingName = methodDecl.name;
+ if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return;
}
- boolean isBoolean = isBoolean(fieldNode);
- String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean);
+ String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
+
+ JavacTreeMaker maker = fieldNode.getTreeMaker();
- JavacTreeMaker maker = builderType.getTreeMaker();
- return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+
+ injectMethod(builderType, newMethod);
}
public JavacNode findInnerClass(JavacNode parent, String name) {
@@ -325,10 +686,77 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return null;
}
- public JavacNode makeBuilderClass(JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
+ public JavacNode makeBuilderClass(boolean isStatic, JavacNode source, JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
JavacTreeMaker maker = tdParent.getTreeMaker();
- JCModifiers mods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC);
- JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
+ int modifiers = Flags.PUBLIC;
+ if (isStatic) modifiers |= Flags.STATIC;
+ JCModifiers mods = maker.Modifiers(modifiers);
+ JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
return injectType(tdParent, builder);
}
+
+ private void addObtainVia(BuilderFieldData bfd, JavacNode node) {
+ for (JavacNode child : node.down()) {
+ if (!annotationTypeMatches(ObtainVia.class, child)) continue;
+ AnnotationValues<ObtainVia> ann = createAnnotation(ObtainVia.class, child);
+ bfd.obtainVia = ann.getInstance();
+ bfd.obtainViaNode = child;
+ deleteAnnotationIfNeccessary(child, ObtainVia.class);
+ return;
+ }
+ }
+
+ /**
+ * Returns the explicitly requested singular annotation on this node (field
+ * or parameter), or null if there's no {@code @Singular} annotation on it.
+ *
+ * @param node The node (field or method param) to inspect for its name and potential {@code @Singular} annotation.
+ */
+ private SingularData getSingularData(JavacNode node) {
+ for (JavacNode child : node.down()) {
+ if (!annotationTypeMatches(Singular.class, child)) continue;
+ Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
+ AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
+ deleteAnnotationIfNeccessary(child, Singular.class);
+ String explicitSingular = ann.getInstance().value();
+ if (explicitSingular.isEmpty()) {
+ if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
+ node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ explicitSingular = pluralName.toString();
+ } else {
+ explicitSingular = autoSingularize(pluralName.toString());
+ if (explicitSingular == null) {
+ node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
+ explicitSingular = pluralName.toString();
+ }
+ }
+ }
+ Name singularName = node.toName(explicitSingular);
+
+ JCExpression type = null;
+ if (node.get() instanceof JCVariableDecl) {
+ type = ((JCVariableDecl) node.get()).vartype;
+ }
+
+ String name = null;
+ List<JCExpression> typeArgs = List.nil();
+ if (type instanceof JCTypeApply) {
+ typeArgs = ((JCTypeApply) type).arguments;
+ type = ((JCTypeApply) type).clazz;
+ }
+
+ name = type.toString();
+
+ String targetFqn = JavacSingularsRecipes.get().toQualified(name);
+ JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
+ }
+
+ return null;
+ }
}
diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java
new file mode 100644
index 00000000..4c4ba0e8
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleBuilderDefault.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017-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
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers;
+
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+
+import lombok.Builder;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+
+@ProviderFor(JavacAnnotationHandler.class)
+@HandlerPriority(-1025) //HandleBuilder's level, minus one.
+public class HandleBuilderDefault extends JavacAnnotationHandler<Builder.Default> {
+ @Override public void handle(AnnotationValues<Builder.Default> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ JavacNode annotatedField = annotationNode.up();
+ if (annotatedField.getKind() != Kind.FIELD) return;
+ JavacNode classWithAnnotatedField = annotatedField.up();
+ if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)) {
+ annotationNode.addWarning("@Builder.Default requires @Builder on the class for it to mean anything.");
+ deleteAnnotationIfNeccessary(annotationNode, Builder.Default.class);
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java
index 6043d1cb..dca25ee7 100644
--- a/src/core/lombok/javac/handlers/HandleConstructor.java
+++ b/src/core/lombok/javac/handlers/HandleConstructor.java
@@ -23,15 +23,18 @@ package lombok.javac.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
+import static lombok.javac.Javac.*;
+
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.ConfigurationKeys;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
import lombok.delombok.LombokOptionsFactory;
-import lombok.experimental.Builder;
+import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
@@ -39,15 +42,17 @@ import lombok.javac.JavacTreeMaker;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
-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.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
@@ -59,6 +64,8 @@ import com.sun.tools.javac.util.Name;
public class HandleConstructor {
@ProviderFor(JavacAnnotationHandler.class)
public static class HandleNoArgsConstructor extends JavacAnnotationHandler<NoArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<NoArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.NO_ARGS_CONSTRUCTOR_FLAG_USAGE, "@NoArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -66,18 +73,21 @@ public class HandleConstructor {
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
JavacNode typeNode = annotationNode.up();
if (!checkLegality(typeNode, annotationNode, NoArgsConstructor.class.getSimpleName())) return;
- List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode);
+ List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode);
NoArgsConstructor ann = annotation.getInstance();
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- List<JavacNode> fields = List.nil();
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, SkipIfConstructorExists.NO, null, annotationNode);
+ boolean force = ann.force();
+ List<JavacNode> fields = force ? findFinalFields(typeNode) : List.<JavacNode>nil();
+ handleConstructor.generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, annotationNode);
}
}
@ProviderFor(JavacAnnotationHandler.class)
public static class HandleRequiredArgsConstructor extends JavacAnnotationHandler<RequiredArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE, "@RequiredArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -85,23 +95,28 @@ public class HandleConstructor {
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
JavacNode typeNode = annotationNode.up();
if (!checkLegality(typeNode, annotationNode, RequiredArgsConstructor.class.getSimpleName())) return;
- List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode);
+ List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor", annotationNode);
RequiredArgsConstructor ann = annotation.getInstance();
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- Boolean suppressConstructorProperties = null;
if (annotation.isExplicit("suppressConstructorProperties")) {
- @SuppressWarnings("deprecation")
- boolean suppress = ann.suppressConstructorProperties();
- suppressConstructorProperties = suppress;
+ annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'.");
}
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
+ handleConstructor.generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode);
}
}
public static List<JavacNode> findRequiredFields(JavacNode typeNode) {
+ return findFields(typeNode, true);
+ }
+
+ public static List<JavacNode> findFinalFields(JavacNode typeNode) {
+ return findFields(typeNode, false);
+ }
+
+ public static List<JavacNode> findFields(JavacNode typeNode, boolean nullMarked) {
ListBuffer<JavacNode> fields = new ListBuffer<JavacNode>();
for (JavacNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -112,7 +127,7 @@ public class HandleConstructor {
//Skip static fields.
if ((fieldFlags & Flags.STATIC) != 0) continue;
boolean isFinal = (fieldFlags & Flags.FINAL) != 0;
- boolean isNonNull = !findAnnotations(child, NON_NULL_PATTERN).isEmpty();
+ boolean isNonNull = nullMarked && !findAnnotations(child, NON_NULL_PATTERN).isEmpty();
if ((isFinal || isNonNull) && fieldDecl.init == null) fields.append(child);
}
return fields.toList();
@@ -120,6 +135,8 @@ public class HandleConstructor {
@ProviderFor(JavacAnnotationHandler.class)
public static class HandleAllArgsConstructor extends JavacAnnotationHandler<AllArgsConstructor> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
@Override public void handle(AnnotationValues<AllArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.ALL_ARGS_CONSTRUCTOR_FLAG_USAGE, "@AllArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor");
@@ -127,22 +144,23 @@ public class HandleConstructor {
deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
JavacNode typeNode = annotationNode.up();
if (!checkLegality(typeNode, annotationNode, AllArgsConstructor.class.getSimpleName())) return;
- List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode);
+ List<JCAnnotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor", annotationNode);
AllArgsConstructor ann = annotation.getInstance();
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- Boolean suppressConstructorProperties = null;
if (annotation.isExplicit("suppressConstructorProperties")) {
- @SuppressWarnings("deprecation")
- boolean suppress = ann.suppressConstructorProperties();
- suppressConstructorProperties = suppress;
+ annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'.");
}
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
+ handleConstructor.generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode);
}
}
public static List<JavacNode> findAllFields(JavacNode typeNode) {
+ return findAllFields(typeNode, false);
+ }
+
+ public static List<JavacNode> findAllFields(JavacNode typeNode, boolean evenFinalInitialized) {
ListBuffer<JavacNode> fields = new ListBuffer<JavacNode>();
for (JavacNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -154,7 +172,7 @@ public class HandleConstructor {
if ((fieldFlags & Flags.STATIC) != 0) continue;
//Skip initialized final fields
boolean isFinal = (fieldFlags & Flags.FINAL) != 0;
- if (!isFinal || fieldDecl.init == null) fields.append(child);
+ if (evenFinalInitialized || !isFinal || fieldDecl.init == null) fields.append(child);
}
return fields.toList();
}
@@ -174,7 +192,7 @@ public class HandleConstructor {
}
public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
- generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), staticName, skipIfConstructorExists, null, source);
+ generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, source);
}
public enum SkipIfConstructorExists {
@@ -182,10 +200,10 @@ public class HandleConstructor {
}
public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
- generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), staticName, skipIfConstructorExists, null, source);
+ generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, source);
}
- public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, Boolean suppressConstructorProperties, JavacNode source) {
+ public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
@@ -193,8 +211,8 @@ public class HandleConstructor {
for (JavacNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
boolean skipGeneration = annotationTypeMatches(NoArgsConstructor.class, child) ||
- annotationTypeMatches(AllArgsConstructor.class, child) ||
- annotationTypeMatches(RequiredArgsConstructor.class, child);
+ annotationTypeMatches(AllArgsConstructor.class, child) ||
+ annotationTypeMatches(RequiredArgsConstructor.class, child);
if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
skipGeneration = annotationTypeMatches(Builder.class, child);
@@ -214,11 +232,23 @@ public class HandleConstructor {
}
}
- JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, suppressConstructorProperties, source);
- injectMethod(typeNode, constr);
+ JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, source);
+ ListBuffer<Type> argTypes = new ListBuffer<Type>();
+ for (JavacNode fieldNode : fields) {
+ Type mirror = getMirrorForFieldType(fieldNode);
+ if (mirror == null) {
+ argTypes = null;
+ break;
+ }
+ argTypes.append(mirror);
+ }
+ List<Type> argTypes_ = argTypes == null ? null : argTypes.toList();
+ injectMethod(typeNode, constr, argTypes_, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
if (staticConstrRequired) {
- JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, fields, source.get());
- injectMethod(typeNode, staticConstr);
+ ClassSymbol sym = ((JCClassDecl) typeNode.get()).sym;
+ Type returnType = sym == null ? null : sym.type;
+ JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, allToDefault ? List.<JavacNode>nil() : fields, source.get());
+ injectMethod(typeNode, staticConstr, argTypes_, returnType);
}
}
@@ -236,18 +266,20 @@ public class HandleConstructor {
mods.annotations = mods.annotations.append(annotation);
}
- public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, Boolean suppressConstructorProperties, JavacNode source) {
+ @SuppressWarnings("deprecation") public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean allToDefault, JavacNode source) {
JavacTreeMaker maker = typeNode.getTreeMaker();
boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
if (isEnum) level = AccessLevel.PRIVATE;
- if (suppressConstructorProperties == null) {
- if (fields.isEmpty()) {
- suppressConstructorProperties = false;
- } else {
- suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
- }
+ boolean addConstructorProperties;
+
+ if (fields.isEmpty()) {
+ addConstructorProperties = false;
+ } else {
+ Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES);
+ addConstructorProperties = v != null ? v.booleanValue() :
+ Boolean.FALSE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES));
}
ListBuffer<JCStatement> nullChecks = new ListBuffer<JCStatement>();
@@ -259,29 +291,55 @@ public class HandleConstructor {
Name fieldName = removePrefixFromField(fieldNode);
Name rawName = field.name;
List<JCAnnotation> nonNulls = findAnnotations(fieldNode, NON_NULL_PATTERN);
- List<JCAnnotation> nullables = findAnnotations(fieldNode, NULLABLE_PATTERN);
- long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
- JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null);
- params.append(param);
+ if (!allToDefault) {
+ List<JCAnnotation> nullables = findAnnotations(fieldNode, NULLABLE_PATTERN);
+ long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null);
+ params.append(param);
+ if (!nonNulls.isEmpty()) {
+ JCStatement nullCheck = generateNullCheck(maker, fieldNode, param, source);
+ if (nullCheck != null) nullChecks.append(nullCheck);
+ }
+ }
JCFieldAccess thisX = maker.Select(maker.Ident(fieldNode.toName("this")), rawName);
- JCAssign assign = maker.Assign(thisX, maker.Ident(fieldName));
+ JCExpression assign = maker.Assign(thisX, allToDefault ? getDefaultExpr(maker, field.vartype) : maker.Ident(fieldName));
assigns.append(maker.Exec(assign));
-
- if (!nonNulls.isEmpty()) {
- JCStatement nullCheck = generateNullCheck(maker, fieldNode, source);
- if (nullCheck != null) nullChecks.append(nullCheck);
- }
}
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
- if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
+ if (!allToDefault && addConstructorProperties && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
addConstructorProperties(mods, typeNode, fields);
}
if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor));
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"),
- null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
- maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext());
+ null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
+ maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext());
+ }
+
+ private static JCExpression getDefaultExpr(JavacTreeMaker maker, JCExpression type) {
+ if (type instanceof JCPrimitiveTypeTree) {
+ switch (((JCPrimitiveTypeTree) type).getPrimitiveTypeKind()) {
+ case BOOLEAN:
+ return maker.Literal(CTC_BOOLEAN, 0);
+ case CHAR:
+ return maker.Literal(CTC_CHAR, 0);
+ default:
+ case BYTE:
+ case SHORT:
+ case INT:
+ return maker.Literal(CTC_INT, 0);
+ case LONG:
+ return maker.Literal(CTC_LONG, 0L);
+ case FLOAT:
+ return maker.Literal(CTC_FLOAT, 0F);
+ case DOUBLE:
+ return maker.Literal(CTC_DOUBLE, 0D);
+ }
+ }
+
+ return maker.Literal(CTC_BOT, null);
+
}
public static boolean isLocalType(JavacNode type) {
diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java
index 9ecf8754..15f1fd83 100644
--- a/src/core/lombok/javac/handlers/HandleData.java
+++ b/src/core/lombok/javac/handlers/HandleData.java
@@ -40,6 +40,12 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation;
*/
@ProviderFor(JavacAnnotationHandler.class)
public class HandleData extends JavacAnnotationHandler<Data> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+ private HandleGetter handleGetter = new HandleGetter();
+ private HandleSetter handleSetter = new HandleSetter();
+ private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode();
+ private HandleToString handleToString = new HandleToString();
+
@Override public void handle(AnnotationValues<Data> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data");
@@ -54,11 +60,10 @@ public class HandleData extends JavacAnnotationHandler<Data> {
String staticConstructorName = annotation.getInstance().staticConstructor();
- // TODO move this to the end OR move it to the top in eclipse.
- new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
- new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
- new HandleToString().generateToStringForType(typeNode, annotationNode);
+ handleConstructor.generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
+ handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
+ handleSetter.generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
+ handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode);
+ handleToString.generateToStringForType(typeNode, annotationNode);
}
}
diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java
index fc3c81f3..49bef769 100644
--- a/src/core/lombok/javac/handlers/HandleDelegate.java
+++ b/src/core/lombok/javac/handlers/HandleDelegate.java
@@ -299,7 +299,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
annotations = com.sun.tools.javac.util.List.nil();
}
- JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annotations);
+ JCModifiers mods = maker.Modifiers(PUBLIC, annotations);
JCExpression returnType = JavacResolution.typeToJCTree((Type) sig.type.getReturnType(), annotation.getAst(), true);
boolean useReturn = sig.type.getReturnType().getKind() != TypeKind.VOID;
ListBuffer<JCVariableDecl> params = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCVariableDecl>();
@@ -312,7 +312,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
for (TypeMirror param : sig.type.getTypeVariables()) {
Name name = ((TypeVar) param).tsym.name;
- ListBuffer<JCExpression> bounds = types.getBounds((TypeVar) param).isEmpty() ? null : new ListBuffer<JCExpression>();
+ ListBuffer<JCExpression> bounds = new ListBuffer<JCExpression>();
for (Type type : types.getBounds((TypeVar) param)) {
bounds.append(JavacResolution.typeToJCTree(type, annotation.getAst(), true));
}
@@ -326,11 +326,15 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
}
int idx = 0;
+ String[] paramNames = sig.getParameterNames();
+ boolean varargs = sig.elem.isVarArgs();
for (TypeMirror param : sig.type.getParameterTypes()) {
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, annotation.getContext());
JCModifiers paramMods = maker.Modifiers(flags);
- String[] paramNames = sig.getParameterNames();
Name name = annotation.toName(paramNames[idx++]);
+ if (varargs && idx == paramNames.length) {
+ paramMods.flags |= VARARGS;
+ }
params.append(maker.VarDef(paramMods, name, JavacResolution.typeToJCTree((Type) param, annotation.getAst(), true), null));
args.append(maker.Ident(name));
}
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 1a6f4a8f..d8bfd154 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -32,6 +32,7 @@ import java.util.Collections;
import lombok.ConfigurationKeys;
import lombok.EqualsAndHashCode;
import lombok.core.AST.Kind;
+import lombok.core.configuration.CallSuperType;
import lombok.core.AnnotationValues;
import lombok.core.handlers.HandlerUtil;
import lombok.javac.Javac;
@@ -94,7 +95,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
List<String> excludes = List.from(ann.exclude());
List<String> includes = List.from(ann.of());
JavacNode typeNode = annotationNode.up();
- List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam=", annotationNode);
+ List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
checkForBogusFieldNames(typeNode, annotation);
Boolean callSuper = ann.callSuper();
@@ -120,14 +121,18 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
return;
}
- generateMethods(typeNode, source, null, null, null, false, FieldAccess.GETTER, List.<JCAnnotation>nil());
+ Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
+ FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
+
+ generateMethods(typeNode, source, null, null, null, false, access, List.<JCAnnotation>nil());
}
public void generateMethods(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
+ Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
+
boolean notAClass = true;
if (typeNode.get() instanceof JCClassDecl) {
- long flags = ((JCClassDecl)typeNode.get()).mods.flags;
+ long flags = ((JCClassDecl) typeNode.get()).mods.flags;
notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0;
}
@@ -140,7 +145,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
boolean implicitCallSuper = callSuper == null;
if (callSuper == null) {
try {
- callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ callSuper = ((Boolean) EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {
throw new InternalError("Lombok bug - this cannot happen - can't find callSuper field in EqualsAndHashCode annotation.");
}
@@ -157,8 +162,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
return;
}
- if (!isDirectDescendantOfObject && !callSuper && implicitCallSuper) {
- source.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.");
+ if (implicitCallSuper && !isDirectDescendantOfObject) {
+ CallSuperType cst = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_CALL_SUPER);
+ if (cst == null) cst = CallSuperType.WARN;
+
+ switch (cst) {
+ default:
+ case WARN:
+ source.addWarning("Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type.");
+ callSuper = false;
+ break;
+ case SKIP:
+ callSuper = false;
+ break;
+ case CALL:
+ callSuper = true;
+ break;
+ }
}
ListBuffer<JavacNode> nodesForEquality = new ListBuffer<JavacNode>();
@@ -184,7 +204,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
}
- boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
+ boolean isFinal = (((JCClassDecl) typeNode.get()).mods.flags & Flags.FINAL) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
@@ -202,8 +222,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
// The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 2 methods are
// all inter-related and should be written by the same entity.
String msg = String.format("Not generating %s: One of equals or hashCode exists. " +
- "You should either write both of these or none of these (in the latter case, lombok generates them).",
- equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
+ "You should either write both of these or none of these (in the latter case, lombok generates them).",
+ equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
source.addWarning(msg);
}
return;
@@ -213,6 +233,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
JCMethodDecl equalsMethod = createEquals(typeNode, nodesForEquality.toList(), callSuper, fieldAccess, needsCanEqual, source.get(), onParam);
+
injectMethod(typeNode, equalsMethod);
if (needsCanEqual && canEqualExists == MemberExistsResult.NOT_EXISTS) {
@@ -237,20 +258,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext());
/* final int PRIME = X; */ {
- if (!fields.isEmpty() || callSuper) {
+ if (!fields.isEmpty()) {
statements.append(maker.VarDef(maker.Modifiers(finalFlag), primeName, maker.TypeIdent(CTC_INT), maker.Literal(HandlerUtil.primeForHashcode())));
}
}
- /* int result = 1; */ {
- statements.append(maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(CTC_INT), maker.Literal(1)));
- }
-
- if (callSuper) {
- JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")),
- List.<JCExpression>nil());
- statements.append(createResultCalculation(typeNode, callToSuper));
+ /* int result = ... */ {
+ final JCExpression init;
+ if (callSuper) {
+ /* ... super.hashCode(); */
+ init = maker.Apply(List.<JCExpression>nil(),
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")),
+ List.<JCExpression>nil());
+ } else {
+ /* ... 1; */
+ init = maker.Literal(1);
+ }
+ statements.append(maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(CTC_INT), init));
}
Name dollar = typeNode.toName("$");
@@ -258,14 +282,14 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression fType = getFieldType(fieldNode, fieldAccess);
JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
if (fType instanceof JCPrimitiveTypeTree) {
- switch (((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind()) {
+ switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) {
case BOOLEAN:
/* this.fieldName ? X : Y */
- statements.append(createResultCalculation(typeNode, maker.Conditional(fieldAccessor,
- maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse()))));
+ statements.append(createResultCalculation(typeNode, maker.Parens(maker.Conditional(fieldAccessor,
+ maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse())))));
break;
case LONG: {
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), fieldAccessor));
statements.append(createResultCalculation(typeNode, longToIntForHashCode(maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
}
@@ -273,17 +297,17 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
case FLOAT:
/* Float.floatToIntBits(this.fieldName) */
statements.append(createResultCalculation(typeNode, maker.Apply(
- List.<JCExpression>nil(),
- genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"),
- List.of(fieldAccessor))));
+ List.<JCExpression>nil(),
+ genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"),
+ List.of(fieldAccessor))));
break;
case DOUBLE: {
/* longToIntForHashCode(Double.doubleToLongBits(this.fieldName)) */
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
JCExpression init = maker.Apply(
- List.<JCExpression>nil(),
- genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"),
- List.of(fieldAccessor));
+ List.<JCExpression>nil(),
+ genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"),
+ List.of(fieldAccessor));
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), init));
statements.append(createResultCalculation(typeNode, longToIntForHashCode(maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
}
@@ -299,23 +323,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
} else if (fType instanceof JCArrayTypeTree) {
/* java.util.Arrays.deepHashCode(this.fieldName) //use just hashCode() for primitive arrays. */
- boolean multiDim = ((JCArrayTypeTree)fType).elemtype instanceof JCArrayTypeTree;
- boolean primitiveArray = ((JCArrayTypeTree)fType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree;
+ boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree;
boolean useDeepHC = multiDim || !primitiveArray;
JCExpression hcMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepHC ? "deepHashCode" : "hashCode");
statements.append(createResultCalculation(typeNode, maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(fieldAccessor))));
} else /* objects */ {
/* final java.lang.Object $fieldName = this.fieldName; */
- /* $fieldName == null ? 0 : $fieldName.hashCode() */
+ /* ($fieldName == null ? NULL_PRIME : $fieldName.hashCode()) */
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, genJavaLangTypeRef(typeNode, "Object"), fieldAccessor));
JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(dollarFieldName), typeNode.toName("hashCode")),
- List.<JCExpression>nil());
+ List.<JCExpression>nil());
JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(dollarFieldName), maker.Literal(CTC_BOT, null));
- statements.append(createResultCalculation(typeNode, maker.Conditional(thisEqualsNull, maker.Literal(0), hcCall)));
+ statements.append(createResultCalculation(typeNode, maker.Parens(maker.Conditional(thisEqualsNull, maker.Literal(HandlerUtil.primeForNull()), hcCall))));
}
}
@@ -325,11 +349,11 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCBlock body = maker.Block(0, statements.toList());
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("hashCode"), returnType,
- List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
+ List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
}
public JCExpressionStatement createResultCalculation(JavacNode typeNode, JCExpression expr) {
- /* result = result * PRIME + (expr); */
+ /* result = result * PRIME + expr; */
JavacTreeMaker maker = typeNode.getTreeMaker();
Name resultName = typeNode.toName(RESULT_NAME);
JCExpression mult = maker.Binary(CTC_MUL, maker.Ident(resultName), maker.Ident(typeNode.toName(PRIME_NAME)));
@@ -339,35 +363,56 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/** The 2 references must be clones of each other. */
public JCExpression longToIntForHashCode(JavacTreeMaker maker, JCExpression ref1, JCExpression ref2) {
- /* (int)(ref >>> 32 ^ ref) */
+ /* (int) (ref >>> 32 ^ ref) */
JCExpression shift = maker.Binary(CTC_UNSIGNED_SHIFT_RIGHT, ref1, maker.Literal(32));
JCExpression xorBits = maker.Binary(CTC_BITXOR, shift, ref2);
- return maker.TypeCast(maker.TypeIdent(CTC_INT), xorBits);
+ return maker.TypeCast(maker.TypeIdent(CTC_INT), maker.Parens(xorBits));
}
- public JCExpression createTypeReference(JavacNode type) {
+ public JCExpression createTypeReference(JavacNode type, boolean addWildcards) {
java.util.List<String> list = new ArrayList<String>();
+ java.util.List<Integer> genericsCount = addWildcards ? new ArrayList<Integer>() : null;
+
list.add(type.getName());
+ if (addWildcards) genericsCount.add(((JCClassDecl) type.get()).typarams.size());
+ boolean staticContext = (((JCClassDecl) type.get()).getModifiers().flags & Flags.STATIC) != 0;
JavacNode tNode = type.up();
+
while (tNode != null && tNode.getKind() == Kind.TYPE) {
list.add(tNode.getName());
+ if (addWildcards) genericsCount.add(staticContext ? 0 : ((JCClassDecl) tNode.get()).typarams.size());
+ if (!staticContext) staticContext = (((JCClassDecl) tNode.get()).getModifiers().flags & Flags.STATIC) != 0;
tNode = tNode.up();
}
Collections.reverse(list);
+ if (addWildcards) Collections.reverse(genericsCount);
JavacTreeMaker maker = type.getTreeMaker();
+
JCExpression chain = maker.Ident(type.toName(list.get(0)));
+ if (addWildcards) chain = wildcardify(maker, chain, genericsCount.get(0));
for (int i = 1; i < list.size(); i++) {
chain = maker.Select(chain, type.toName(list.get(i)));
+ if (addWildcards) chain = wildcardify(maker, chain, genericsCount.get(i));
}
return chain;
}
+ private JCExpression wildcardify(JavacTreeMaker maker, JCExpression expr, int count) {
+ if (count == 0) return expr;
+
+ ListBuffer<JCExpression> wildcards = new ListBuffer<JCExpression>();
+ for (int i = 0 ; i < count ; i++) {
+ wildcards.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
+ }
+
+ return maker.TypeApply(expr, wildcards.toList());
+ }
+
public JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JCTree source, List<JCAnnotation> onParam) {
JavacTreeMaker maker = typeNode.getTreeMaker();
- JCClassDecl type = (JCClassDecl) typeNode.get();
Name oName = typeNode.toName("o");
Name otherName = typeNode.toName("other");
@@ -385,35 +430,21 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* if (o == this) return true; */ {
statements.append(maker.If(maker.Binary(CTC_EQUAL, maker.Ident(oName),
- maker.Ident(thisName)), returnBool(maker, true), null));
+ maker.Ident(thisName)), returnBool(maker, true), null));
}
- /* if (!(o instanceof Outer.Inner.MyType) return false; */ {
+ /* if (!(o instanceof Outer.Inner.MyType)) return false; */ {
- JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode)));
+ JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.Parens(maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode, false))));
statements.append(maker.If(notInstanceOf, returnBool(maker, false), null));
}
- /* MyType<?> other = (MyType<?>) o; */ {
+ /* Outer.Inner.MyType<?> other = (Outer.Inner.MyType<?>) o; */ {
if (!fields.isEmpty() || needsCanEqual) {
- final JCExpression selfType1, selfType2;
- ListBuffer<JCExpression> wildcards1 = new ListBuffer<JCExpression>();
- ListBuffer<JCExpression> wildcards2 = new ListBuffer<JCExpression>();
- for (int i = 0 ; i < type.typarams.length() ; i++) {
- wildcards1.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
- wildcards2.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
- }
-
- if (type.typarams.isEmpty()) {
- selfType1 = maker.Ident(type.name);
- selfType2 = maker.Ident(type.name);
- } else {
- selfType1 = maker.TypeApply(maker.Ident(type.name), wildcards1.toList());
- selfType2 = maker.TypeApply(maker.Ident(type.name), wildcards2.toList());
- }
+ final JCExpression selfType1 = createTypeReference(typeNode, true), selfType2 = createTypeReference(typeNode, true);
statements.append(
- maker.VarDef(maker.Modifiers(finalFlag), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName))));
+ maker.VarDef(maker.Modifiers(finalFlag), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName))));
}
}
@@ -423,8 +454,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression thisRef = maker.Ident(thisName);
JCExpression castThisRef = maker.TypeCast(genJavaLangTypeRef(typeNode, "Object"), thisRef);
JCExpression equalityCheck = maker.Apply(exprNil,
- maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")),
- List.of(castThisRef));
+ maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")),
+ List.of(castThisRef));
statements.append(maker.If(maker.Unary(CTC_NOT, equalityCheck), returnBool(maker, false), null));
}
}
@@ -432,8 +463,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* if (!super.equals(o)) return false; */
if (callSuper) {
JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
- List.<JCExpression>of(maker.Ident(oName)));
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
+ List.<JCExpression>of(maker.Ident(oName)));
JCUnary superNotEqual = maker.Unary(CTC_NOT, callToSuper);
statements.append(maker.If(superNotEqual, returnBool(maker, false), null));
}
@@ -462,19 +493,19 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
} else if (fType instanceof JCArrayTypeTree) {
/* if (!java.util.Arrays.deepEquals(this.fieldName, other.fieldName)) return false; //use equals for primitive arrays. */
- boolean multiDim = ((JCArrayTypeTree)fType).elemtype instanceof JCArrayTypeTree;
- boolean primitiveArray = ((JCArrayTypeTree)fType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree;
+ boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree;
boolean useDeepEquals = multiDim || !primitiveArray;
JCExpression eqMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepEquals ? "deepEquals" : "equals");
List<JCExpression> args = List.of(thisFieldAccessor, otherFieldAccessor);
statements.append(maker.If(maker.Unary(CTC_NOT,
- maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null));
+ maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null));
} else /* objects */ {
/* final java.lang.Object this$fieldName = this.fieldName; */
/* final java.lang.Object other$fieldName = other.fieldName; */
- /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false;; */
- Name fieldName = ((JCVariableDecl)fieldNode.get()).name;
+ /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */
+ Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
Name thisDollarFieldName = thisDollar.append(fieldName);
Name otherDollarFieldName = otherDollar.append(fieldName);
@@ -484,8 +515,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(thisDollarFieldName), maker.Literal(CTC_BOT, null));
JCExpression otherNotEqualsNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(otherDollarFieldName), maker.Literal(CTC_BOT, null));
JCExpression thisEqualsThat = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")),
- List.<JCExpression>of(maker.Ident(otherDollarFieldName)));
+ maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")),
+ List.<JCExpression>of(maker.Ident(otherDollarFieldName)));
JCExpression fieldsAreNotEqual = maker.Conditional(thisEqualsNull, otherNotEqualsNull, maker.Unary(CTC_NOT, thisEqualsThat));
statements.append(maker.If(fieldsAreNotEqual, returnBool(maker, false), null));
}
@@ -500,7 +531,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
public JCMethodDecl createCanEqual(JavacNode typeNode, JCTree source, List<JCAnnotation> onParam) {
- /* public boolean canEqual(final java.lang.Object other) {
+ /* protected boolean canEqual(final java.lang.Object other) {
* return other instanceof Outer.Inner.MyType;
* }
*/
@@ -515,18 +546,19 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
List<JCVariableDecl> params = List.of(maker.VarDef(maker.Modifiers(flags, onParam), otherName, objectType, null));
JCBlock body = maker.Block(0, List.<JCStatement>of(
- maker.Return(maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode)))));
+ maker.Return(maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode, false)))));
return recursiveSetGeneratedBy(maker.MethodDef(mods, canEqualName, returnType, List.<JCTypeParameter>nil(), params, List.<JCExpression>nil(), body, null), source, typeNode.getContext());
}
public JCStatement generateCompareFloatOrDouble(JCExpression thisDotField, JCExpression otherDotField,
- JavacTreeMaker maker, JavacNode node, boolean isDouble) {
+ JavacTreeMaker maker, JavacNode node, boolean isDouble) {
+
/* if (Float.compare(fieldName, other.fieldName) != 0) return false; */
JCExpression clazz = genJavaLangTypeRef(node, isDouble ? "Double" : "Float");
List<JCExpression> args = List.of(thisDotField, otherDotField);
JCBinary compareCallEquals0 = maker.Binary(CTC_NOT_EQUAL, maker.Apply(
- List.<JCExpression>nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0));
+ List.<JCExpression>nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0));
return maker.If(compareCallEquals0, returnBool(maker, false), null);
}
diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
index 335ab1fe..52f6c39c 100644
--- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 The Project Lombok Authors.
+ * Copyright (C) 2012-2016 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
@@ -31,23 +31,24 @@ import lombok.core.HandlerPriority;
import lombok.experimental.FieldDefaults;
import lombok.experimental.NonFinal;
import lombok.experimental.PackagePrivate;
-import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacASTAdapter;
+import lombok.javac.JavacASTVisitor;
import lombok.javac.JavacNode;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
-import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
/**
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
-@ProviderFor(JavacAnnotationHandler.class)
+@ProviderFor(JavacASTVisitor.class)
@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
-public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
+public class HandleFieldDefaults extends JavacASTAdapter {
public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
if (hasAnnotation(FieldDefaults.class, typeNode)) {
@@ -72,56 +73,82 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
//Skip fields that start with $
if (fieldDecl.name.toString().startsWith("$")) continue;
- setFieldDefaultsForField(field, errorNode.get(), level, makeFinal);
+ setFieldDefaultsForField(field, level, makeFinal);
}
return true;
}
- public void setFieldDefaultsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean makeFinal) {
+ public void setFieldDefaultsForField(JavacNode fieldNode, AccessLevel level, boolean makeFinal) {
JCVariableDecl field = (JCVariableDecl) fieldNode.get();
if (level != null && level != AccessLevel.NONE) {
if ((field.mods.flags & (Flags.PUBLIC | Flags.PRIVATE | Flags.PROTECTED)) == 0) {
if (!hasAnnotationAndDeleteIfNeccessary(PackagePrivate.class, fieldNode)) {
- field.mods.flags |= toJavacModifier(level);
+ if ((field.mods.flags & Flags.STATIC) == 0) {
+ field.mods.flags |= toJavacModifier(level);
+ }
}
}
}
if (makeFinal && (field.mods.flags & Flags.FINAL) == 0) {
if (!hasAnnotationAndDeleteIfNeccessary(NonFinal.class, fieldNode)) {
- field.mods.flags |= Flags.FINAL;
+ if ((field.mods.flags & Flags.STATIC) == 0) {
+ field.mods.flags |= Flags.FINAL;
+ }
}
}
fieldNode.rebuild();
}
- @Override public void handle(AnnotationValues<FieldDefaults> annotation, JCAnnotation ast, JavacNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
-
- deleteAnnotationIfNeccessary(annotationNode, FieldDefaults.class);
- deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
- JavacNode node = annotationNode.up();
- FieldDefaults instance = annotation.getInstance();
- AccessLevel level = instance.level();
- boolean makeFinal = instance.makeFinal();
+ @Override public void visitType(JavacNode typeNode, JCClassDecl type) {
+ AnnotationValues<FieldDefaults> fieldDefaults = null;
+ JavacNode source = typeNode;
- if (level == AccessLevel.NONE && !makeFinal) {
- annotationNode.addError("This does nothing; provide either level or makeFinal or both.");
- return;
+ boolean levelIsExplicit = false;
+ boolean makeFinalIsExplicit = false;
+ FieldDefaults fd = null;
+ for (JavacNode jn : typeNode.down()) {
+ if (jn.getKind() != Kind.ANNOTATION) continue;
+ JCAnnotation ann = (JCAnnotation) jn.get();
+ JCTree typeTree = ann.annotationType;
+ if (typeTree == null) continue;
+ String typeTreeToString = typeTree.toString();
+ if (!typeTreeToString.equals("FieldDefaults") && !typeTreeToString.equals("lombok.experimental.FieldDefaults")) continue;
+ if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue;
+
+ source = jn;
+ fieldDefaults = createAnnotation(FieldDefaults.class, jn);
+ levelIsExplicit = fieldDefaults.isExplicit("level");
+ makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal");
+
+ handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
+
+ fd = fieldDefaults.getInstance();
+ if (!levelIsExplicit && !makeFinalIsExplicit) {
+ jn.addError("This does nothing; provide either level or makeFinal or both.");
+ }
+
+ if (levelIsExplicit && fd.level() == AccessLevel.NONE) {
+ jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value.");
+ levelIsExplicit = false;
+ }
+
+ deleteAnnotationIfNeccessary(jn, FieldDefaults.class);
+ deleteImportFromCompilationUnit(jn, "lombok.AccessLevel");
+ break;
}
- if (level == AccessLevel.PACKAGE) {
- annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
- }
+ if (fd == null && (type.mods.flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0) return;
- if (!makeFinal && annotation.isExplicit("makeFinal")) {
- annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
- }
+ boolean defaultToPrivate = levelIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE));
+ boolean defaultToFinal = makeFinalIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE));
- if (node == null) return;
+ if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return;
+ AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null;
+ boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal;
- generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
+ generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false);
}
}
diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java
index a330dbc1..0540465d 100644
--- a/src/core/lombok/javac/handlers/HandleGetter.java
+++ b/src/core/lombok/javac/handlers/HandleGetter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -46,6 +46,7 @@ import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBinary;
@@ -94,7 +95,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
}
}
- public boolean fieldQualifiesForGetterGeneration(JavacNode field) {
+ public static boolean fieldQualifiesForGetterGeneration(JavacNode field) {
if (field.getKind() != Kind.FIELD) return false;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
//Skip fields that start with $
@@ -146,7 +147,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
if (node == null) return;
- List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod=", annotationNode);
+ List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod", annotationNode);
switch (node.getKind()) {
case FIELD:
@@ -182,6 +183,10 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
source.addError("'lazy' requires the field to be private and final.");
return;
}
+ if ((fieldDecl.mods.flags & Flags.TRANSIENT) != 0) {
+ source.addError("'lazy' is not supported on transient fields.");
+ return;
+ }
if (fieldDecl.init == null) {
source.addError("'lazy' requires field initialization.");
return;
@@ -215,7 +220,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC);
- injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), source.get(), lazy, onMethod));
+ injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), source.get(), lazy, onMethod), List.<Type>nil(), getMirrorForFieldType(fieldNode));
}
public JCMethodDecl createGetter(long access, JavacNode field, JavacTreeMaker treeMaker, JCTree source, boolean lazy, List<JCAnnotation> onMethod) {
diff --git a/src/core/lombok/javac/handlers/HandleHelper.java b/src/core/lombok/javac/handlers/HandleHelper.java
new file mode 100644
index 00000000..09ace4d6
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleHelper.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015-2016 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers;
+
+import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.TreeVisitor;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+import lombok.ConfigurationKeys;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.experimental.Helper;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+
+@ProviderFor(JavacAnnotationHandler.class)
+public class HandleHelper extends JavacAnnotationHandler<Helper> {
+ private List<JCStatement> getStatementsFromJcNode(JCTree tree) {
+ if (tree instanceof JCBlock) return ((JCBlock) tree).stats;
+ if (tree instanceof JCCase) return ((JCCase) tree).stats;
+ return null;
+ }
+
+ private void setStatementsOfJcNode(JCTree tree, List<JCStatement> statements) {
+ if (tree instanceof JCBlock) ((JCBlock) tree).stats = statements;
+ else if (tree instanceof JCCase) ((JCCase) tree).stats = statements;
+ else throw new IllegalArgumentException("Can't set statements on node type: " + tree.getClass());
+ }
+
+ @Override public void handle(AnnotationValues<Helper> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper");
+
+ deleteAnnotationIfNeccessary(annotationNode, Helper.class);
+ JavacNode annotatedType = annotationNode.up();
+ JavacNode containingBlock = annotatedType == null ? null : annotatedType.directUp();
+ List<JCStatement> origStatements = getStatementsFromJcNode(containingBlock == null ? null : containingBlock.get());
+
+ if (annotatedType == null || annotatedType.getKind() != Kind.TYPE || origStatements == null) {
+ annotationNode.addError("@Helper is legal only on method-local classes.");
+ return;
+ }
+
+ JCClassDecl annotatedType_ = (JCClassDecl) annotatedType.get();
+ Iterator<JCStatement> it = origStatements.iterator();
+ while (it.hasNext()) {
+ if (it.next() == annotatedType_) {
+ break;
+ }
+ }
+
+ java.util.List<String> knownMethodNames = new ArrayList<String>();
+
+ for (JavacNode ch : annotatedType.down()) {
+ if (ch.getKind() != Kind.METHOD) continue;
+ String n = ch.getName();
+ if (n == null || n.isEmpty() || n.charAt(0) == '<') continue;
+ knownMethodNames.add(n);
+ }
+
+ Collections.sort(knownMethodNames);
+ final String[] knownMethodNames_ = knownMethodNames.toArray(new String[knownMethodNames.size()]);
+
+ final Name helperName = annotationNode.toName("$" + annotatedType_.name);
+ final boolean[] helperUsed = new boolean[1];
+ final JavacTreeMaker maker = annotationNode.getTreeMaker();
+
+ TreeVisitor<Void, Void> visitor = new TreeScanner<Void, Void>() {
+ @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
+ JCMethodInvocation jcmi = (JCMethodInvocation) node;
+ apply(jcmi);
+ return super.visitMethodInvocation(node, p);
+ }
+
+ private void apply(JCMethodInvocation jcmi) {
+ if (!(jcmi.meth instanceof JCIdent)) return;
+ JCIdent jci = (JCIdent) jcmi.meth;
+ if (Arrays.binarySearch(knownMethodNames_, jci.name.toString()) < 0) return;
+ jcmi.meth = maker.Select(maker.Ident(helperName), jci.name);
+ helperUsed[0] = true;
+ }
+ };
+
+ while (it.hasNext()) {
+ JCStatement stat = it.next();
+ stat.accept(visitor, null);
+ }
+
+ if (!helperUsed[0]) {
+ annotationNode.addWarning("No methods of this helper class are ever used.");
+ return;
+ }
+
+ ListBuffer<JCStatement> newStatements = new ListBuffer<JCStatement>();
+
+ boolean mark = false;
+ for (JCStatement stat : origStatements) {
+ newStatements.append(stat);
+ if (mark || stat != annotatedType_) continue;
+ mark = true;
+ JCExpression init = maker.NewClass(null, List.<JCExpression>nil(), maker.Ident(annotatedType_.name), List.<JCExpression>nil(), null);
+ JCExpression varType = maker.Ident(annotatedType_.name);
+ JCVariableDecl decl = maker.VarDef(maker.Modifiers(Flags.FINAL), helperName, varType, init);
+ newStatements.append(decl);
+ }
+ setStatementsOfJcNode(containingBlock.get(), newStatements.toList());
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java
index ee7268f7..d0d709e3 100644
--- a/src/core/lombok/javac/handlers/HandleLog.java
+++ b/src/core/lombok/javac/handlers/HandleLog.java
@@ -105,7 +105,7 @@ public class HandleLog {
maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (useStatic ? Flags.STATIC : 0)),
typeNode.toName(logFieldName), loggerType, factoryMethodCall), source, typeNode.getContext());
- injectFieldSuppressWarnings(typeNode, fieldDecl);
+ injectFieldAndMarkGenerated(typeNode, fieldDecl);
return true;
}
@@ -175,6 +175,17 @@ public class HandleLog {
}
}
+ /**
+ * Handles the {@link lombok.extern.jbosslog.JBossLog} annotation for javac.
+ */
+ @ProviderFor(JavacAnnotationHandler.class)
+ public static class HandleJBossLog extends JavacAnnotationHandler<lombok.extern.jbosslog.JBossLog> {
+ @Override public void handle(AnnotationValues<lombok.extern.jbosslog.JBossLog> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JBOSSLOG_FLAG_USAGE, "@JBossLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log");
+ processAnnotation(LoggingFramework.JBOSSLOG, annotation, annotationNode, annotation.getInstance().topic());
+ }
+ }
+
enum LoggingFramework {
// private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(TargetType.class);
COMMONS(lombok.extern.apachecommons.CommonsLog.class, "org.apache.commons.logging.Log", "org.apache.commons.logging.LogFactory.getLog"),
@@ -200,6 +211,8 @@ public class HandleLog {
// private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(TargetType.class);
XSLF4J(lombok.extern.slf4j.XSlf4j.class, "org.slf4j.ext.XLogger", "org.slf4j.ext.XLoggerFactory.getXLogger"),
+ // private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(TargetType.class);
+ JBOSSLOG(lombok.extern.jbosslog.JBossLog.class, "org.jboss.logging.Logger", "org.jboss.logging.Logger.getLogger")
;
private final Class<? extends Annotation> annotationClass;
diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java
index cd8e3402..81aa1525 100644
--- a/src/core/lombok/javac/handlers/HandleNonNull.java
+++ b/src/core/lombok/javac/handlers/HandleNonNull.java
@@ -85,7 +85,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
}
if (declaration.body == null) {
- annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method.");
+ // This used to be a warning, but as @NonNull also has a documentary purpose, better to not warn about this. Since 1.16.7
return;
}
@@ -141,6 +141,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
List<JCStatement> newList = tail.prepend(nullCheck);
for (JCStatement stat : head) newList = newList.prepend(stat);
declaration.body.stats = newList;
+ annotationNode.getAst().setChanged();
}
public boolean isNullCheck(JCStatement stat) {
diff --git a/src/core/lombok/javac/handlers/HandlePrintAST.java b/src/core/lombok/javac/handlers/HandlePrintAST.java
index 9a52b9d9..0826d1d1 100644
--- a/src/core/lombok/javac/handlers/HandlePrintAST.java
+++ b/src/core/lombok/javac/handlers/HandlePrintAST.java
@@ -59,7 +59,7 @@ public class HandlePrintAST extends JavacAnnotationHandler<PrintAST> {
try {
stream.close();
} catch (Exception e) {
- Lombok.sneakyThrow(e);
+ throw Lombok.sneakyThrow(e);
}
}
}
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index 3c4329b2..1453aa27 100644
--- a/src/core/lombok/javac/handlers/HandleSetter.java
+++ b/src/core/lombok/javac/handlers/HandleSetter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -41,6 +41,8 @@ import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCBlock;
@@ -127,8 +129,8 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
if (level == AccessLevel.NONE || node == null) return;
- List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod=", annotationNode);
- List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam=", annotationNode);
+ List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod", annotationNode);
+ List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam", annotationNode);
switch (node.getKind()) {
case FIELD:
@@ -154,7 +156,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
return;
}
- JCVariableDecl fieldDecl = (JCVariableDecl)fieldNode.get();
+ JCVariableDecl fieldDecl = (JCVariableDecl) fieldNode.get();
String methodName = toSetterName(fieldNode);
if (methodName == null) {
@@ -188,16 +190,26 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC);
JCMethodDecl createdSetter = createSetter(access, fieldNode, fieldNode.getTreeMaker(), sourceNode, onMethod, onParam);
- injectMethod(fieldNode.up(), createdSetter);
+ Type fieldType = getMirrorForFieldType(fieldNode);
+ Type returnType;
+
+ if (shouldReturnThis(fieldNode)) {
+ ClassSymbol sym = ((JCClassDecl) fieldNode.up().get()).sym;
+ returnType = sym == null ? null : sym.type;
+ } else {
+ returnType = Javac.createVoidType(fieldNode.getSymbolTable(), CTC_VOID);
+ }
+
+ injectMethod(fieldNode.up(), createdSetter, fieldType == null ? null : List.of(fieldType), returnType);
}
public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
String setterName = toSetterName(field);
boolean returnThis = shouldReturnThis(field);
- return createSetter(access, field, treeMaker, setterName, returnThis, source, onMethod, onParam);
+ return createSetter(access, false, field, treeMaker, setterName, null, returnThis, source, onMethod, onParam);
}
- public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, String setterName, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
+ public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
if (setterName == null) return null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
@@ -223,6 +235,11 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
statements.append(treeMaker.Exec(assign));
}
+ if (booleanFieldToSet != null) {
+ JCAssign setBool = treeMaker.Assign(treeMaker.Ident(booleanFieldToSet), treeMaker.Literal(CTC_BOOLEAN, 1));
+ statements.append(treeMaker.Exec(setBool));
+ }
+
JCExpression methodType = null;
if (shouldReturnThis) {
methodType = cloneSelfType(field);
@@ -230,7 +247,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
if (methodType == null) {
//WARNING: Do not use field.getSymbolTable().voidType - that field has gone through non-backwards compatible API changes within javac1.6.
- methodType = treeMaker.Type(Javac.createVoidType(treeMaker, CTC_VOID));
+ methodType = treeMaker.Type(Javac.createVoidType(field.getSymbolTable(), CTC_VOID));
shouldReturnThis = false;
}
@@ -246,7 +263,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
JCExpression annotationMethodDefaultValue = null;
List<JCAnnotation> annsOnMethod = copyAnnotations(onMethod);
- if (isFieldDeprecated(field)) {
+ if (isFieldDeprecated(field) || deprecate) {
annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil()));
}
diff --git a/src/core/lombok/javac/handlers/HandleSynchronized.java b/src/core/lombok/javac/handlers/HandleSynchronized.java
index fb6678e6..20e85d7e 100644
--- a/src/core/lombok/javac/handlers/HandleSynchronized.java
+++ b/src/core/lombok/javac/handlers/HandleSynchronized.java
@@ -99,7 +99,7 @@ public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> {
JCVariableDecl fieldDecl = recursiveSetGeneratedBy(maker.VarDef(
maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (isStatic ? Flags.STATIC : 0)),
methodNode.toName(lockName), objectType, newObjectArray), ast, context);
- injectFieldSuppressWarnings(methodNode.up(), fieldDecl);
+ injectFieldAndMarkGenerated(methodNode.up(), fieldDecl);
}
if (method.body == null) return;
diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java
index 743e7b26..897d5f2c 100644
--- a/src/core/lombok/javac/handlers/HandleToString.java
+++ b/src/core/lombok/javac/handlers/HandleToString.java
@@ -117,7 +117,11 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
- generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER);
+
+ Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
+ FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
+
+ generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, access);
}
public void generateToString(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes,
diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java
new file mode 100644
index 00000000..ee8081d6
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleUtilityClass.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers;
+
+import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage;
+import static lombok.javac.Javac.CTC_VOID;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+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.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
+
+import lombok.ConfigurationKeys;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.experimental.UtilityClass;
+import lombok.javac.Javac;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+
+/**
+ * Handles the {@code @UtilityClass} annotation for javac.
+ */
+@HandlerPriority(-4096) //-2^12; to ensure @FieldDefaults picks up on the 'static' we set here.
+@ProviderFor(JavacAnnotationHandler.class)
+public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> {
+ @Override public void handle(AnnotationValues<UtilityClass> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass");
+
+ deleteAnnotationIfNeccessary(annotationNode, UtilityClass.class);
+
+ JavacNode typeNode = annotationNode.up();
+ if (!checkLegality(typeNode, annotationNode)) return;
+ changeModifiersAndGenerateConstructor(annotationNode.up(), annotationNode);
+ }
+
+ private static boolean checkLegality(JavacNode typeNode, JavacNode errorNode) {
+ JCClassDecl typeDecl = null;
+ if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get();
+ long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags;
+ boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0;
+
+ if (typeDecl == null || notAClass) {
+ errorNode.addError("@UtilityClass is only supported on a class (can't be an interface, enum, or annotation).");
+ return false;
+ }
+
+ // It might be an inner class. This is okay, but only if it is / can be a static inner class. Thus, all of its parents have to be static inner classes until the top-level.
+ JavacNode typeWalk = typeNode;
+ while (true) {
+ typeWalk = typeWalk.up();
+ switch (typeWalk.getKind()) {
+ case TYPE:
+ JCClassDecl typeDef = (JCClassDecl) typeWalk.get();
+ if ((typeDef.mods.flags & (Flags.STATIC | Flags.ANNOTATION | Flags.ENUM | Flags.INTERFACE)) != 0) continue;
+ if (typeWalk.up().getKind() == Kind.COMPILATION_UNIT) return true;
+ errorNode.addError("@UtilityClass automatically makes the class static, however, this class cannot be made static.");
+ return false;
+ case COMPILATION_UNIT:
+ return true;
+ default:
+ errorNode.addError("@UtilityClass cannot be placed on a method local or anonymous inner class, or any class nested in such a class.");
+ return false;
+ }
+ }
+ }
+
+ private void changeModifiersAndGenerateConstructor(JavacNode typeNode, JavacNode errorNode) {
+ JCClassDecl classDecl = (JCClassDecl) typeNode.get();
+
+ boolean makeConstructor = true;
+
+ classDecl.mods.flags |= Flags.FINAL;
+
+ boolean markStatic = true;
+
+ if (typeNode.up().getKind() == Kind.COMPILATION_UNIT) markStatic = false;
+ if (markStatic && typeNode.up().getKind() == Kind.TYPE) {
+ JCClassDecl typeDecl = (JCClassDecl) typeNode.up().get();
+ if ((typeDecl.mods.flags & Flags.INTERFACE) != 0) markStatic = false;
+ }
+
+ if (markStatic) classDecl.mods.flags |= Flags.STATIC;
+
+ for (JavacNode element : typeNode.down()) {
+ if (element.getKind() == Kind.FIELD) {
+ JCVariableDecl fieldDecl = (JCVariableDecl) element.get();
+ fieldDecl.mods.flags |= Flags.STATIC;
+ } else if (element.getKind() == Kind.METHOD) {
+ JCMethodDecl methodDecl = (JCMethodDecl) element.get();
+ if (methodDecl.name.contentEquals("<init>")) {
+ if (getGeneratedBy(methodDecl) == null && (methodDecl.mods.flags & Flags.GENERATEDCONSTR) == 0) {
+ element.addError("@UtilityClasses cannot have declared constructors.");
+ makeConstructor = false;
+ continue;
+ }
+ }
+
+ methodDecl.mods.flags |= Flags.STATIC;
+ } else if (element.getKind() == Kind.TYPE) {
+ JCClassDecl innerClassDecl = (JCClassDecl) element.get();
+ innerClassDecl.mods.flags |= Flags.STATIC;
+ }
+ }
+
+ if (makeConstructor) createPrivateDefaultConstructor(typeNode);
+ }
+
+ private void createPrivateDefaultConstructor(JavacNode typeNode) {
+ JavacTreeMaker maker = typeNode.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE, List.<JCAnnotation>nil());
+
+ Name name = typeNode.toName("<init>");
+ JCBlock block = maker.Block(0L, createThrowStatement(typeNode, maker));
+ JCMethodDecl methodDef = maker.MethodDef(mods, name, null, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), block, null);
+ JCMethodDecl constructor = recursiveSetGeneratedBy(methodDef, typeNode.get(), typeNode.getContext());
+ JavacHandlerUtil.injectMethod(typeNode, constructor, List.<Type>nil(), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
+ }
+
+ private List<JCStatement> createThrowStatement(JavacNode typeNode, JavacTreeMaker maker) {
+ JCExpression exceptionType = genJavaLangTypeRef(typeNode, "UnsupportedOperationException");
+ List<JCExpression> jceBlank = List.nil();
+ JCExpression message = maker.Literal("This is a utility class and cannot be instantiated");
+ JCExpression exceptionInstance = maker.NewClass(null, jceBlank, exceptionType, List.of(message), null);
+ JCStatement throwStatement = maker.Throw(exceptionInstance);
+ return List.of(throwStatement);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
index 0230e1b0..14130bc4 100644
--- a/src/core/lombok/javac/handlers/HandleVal.java
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Project Lombok Authors.
+ * Copyright (C) 2010-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
@@ -21,11 +21,12 @@
*/
package lombok.javac.handlers;
-import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.core.handlers.HandlerUtil.handleFlagUsage;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import lombok.ConfigurationKeys;
import lombok.val;
import lombok.core.HandlerPriority;
+import lombok.var;
import lombok.javac.JavacASTAdapter;
import lombok.javac.JavacASTVisitor;
import lombok.javac.JavacNode;
@@ -35,12 +36,14 @@ import lombok.javac.ResolutionResetNeeded;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCForLoop;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCNewArray;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
@@ -49,21 +52,36 @@ import com.sun.tools.javac.util.List;
@HandlerPriority(65536) // 2^16; resolution needs to work, so if the RHS expression is i.e. a call to a generated getter, we have to run after that getter has been generated.
@ResolutionResetNeeded
public class HandleVal extends JavacASTAdapter {
- @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) {
- if (local.vartype == null || (!local.vartype.toString().equals("val") && !local.vartype.toString().equals("lombok.val"))) return;
-
- JCTree source = local.vartype;
+
+ private static boolean eq(String typeTreeToString, String key) {
+ return typeTreeToString.equals(key) || typeTreeToString.equals("lombok." + key) || typeTreeToString.equals("lombok.experimental." + key);
+ }
+
+ @SuppressWarnings("deprecation") @Override
+ public void visitLocal(JavacNode localNode, JCVariableDecl local) {
+ JCTree typeTree = local.vartype;
+ if (typeTree == null) return;
+ String typeTreeToString = typeTree.toString();
- if (!typeMatches(val.class, localNode, local.vartype)) return;
+ if (!(eq(typeTreeToString, "val") || eq(typeTreeToString, "var"))) return;
+ boolean isVal = typeMatches(val.class, localNode, typeTree);
+ boolean isVar = typeMatches(var.class, localNode, typeTree);
+ if (!(isVal || isVar)) return;
- handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val");
+ if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val");
+ if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var");
JCTree parentRaw = localNode.directUp().get();
- if (parentRaw instanceof JCForLoop) {
+ if (isVal && parentRaw instanceof JCForLoop) {
localNode.addError("'val' is not allowed in old-style for loops");
return;
}
+ if (parentRaw instanceof JCForLoop && ((JCForLoop) parentRaw).getInitializer().size() > 1) {
+ localNode.addError("'var' is not allowed in old-style for loops if there is more than 1 initializer");
+ return;
+ }
+
JCExpression rhsOfEnhancedForLoop = null;
if (local.init == null) {
if (parentRaw instanceof JCEnhancedForLoop) {
@@ -72,22 +90,27 @@ public class HandleVal extends JavacASTAdapter {
}
}
+ final String annotation = typeTreeToString;
if (rhsOfEnhancedForLoop == null && local.init == null) {
- localNode.addError("'val' on a local variable requires an initializer expression");
+ localNode.addError("'" + annotation + "' on a local variable requires an initializer expression");
return;
}
if (local.init instanceof JCNewArray && ((JCNewArray)local.init).elemtype == null) {
- localNode.addError("'val' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })");
+ localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })");
return;
}
- if (localNode.shouldDeleteLombokAnnotations()) JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, "lombok.val");
+ if (localNode.shouldDeleteLombokAnnotations()) {
+ JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, val.class.getName());
+ JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, lombok.experimental.var.class.getName());
+ JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, var.class.getName());
+ }
- local.mods.flags |= Flags.FINAL;
+ if (isVal) local.mods.flags |= Flags.FINAL;
if (!localNode.shouldDeleteLombokAnnotations()) {
- JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), source, localNode.getContext());
+ JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeTree, localNode.getContext());
local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation);
}
@@ -101,15 +124,28 @@ public class HandleVal extends JavacASTAdapter {
try {
if (rhsOfEnhancedForLoop == null) {
if (local.init.type == null) {
+ if (isVar && local.init instanceof JCLiteral && ((JCLiteral) local.init).value == null) {
+ localNode.addError("variable initializer is 'null'");
+ }
JavacResolution resolver = new JavacResolution(localNode.getContext());
try {
type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type;
} catch (RuntimeException e) {
- System.err.println("Exception while resolving: " + localNode);
+ System.err.println("Exception while resolving: " + localNode + "(" + localNode.getFileName() + ")");
throw e;
}
} else {
type = local.init.type;
+ if (type.isErroneous()) {
+ try {
+ JavacResolution resolver = new JavacResolution(localNode.getContext());
+ local.type = Symtab.instance(localNode.getContext()).unknownType;
+ type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type;
+ } catch (RuntimeException e) {
+ System.err.println("Exception while resolving: " + localNode + "(" + localNode.getFileName() + ")");
+ throw e;
+ }
+ }
}
} else {
if (rhsOfEnhancedForLoop.type == null) {
@@ -138,14 +174,14 @@ public class HandleVal extends JavacASTAdapter {
}
localNode.getAst().setChanged();
} catch (JavacResolution.TypeNotConvertibleException e) {
- localNode.addError("Cannot use 'val' here because initializer expression does not have a representable type: " + e.getMessage());
+ localNode.addError("Cannot use '" + annotation + "' here because initializer expression does not have a representable type: " + e.getMessage());
local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
}
} catch (RuntimeException e) {
local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
throw e;
} finally {
- recursiveSetGeneratedBy(local.vartype, source, localNode.getContext());
+ recursiveSetGeneratedBy(local.vartype, typeTree, localNode.getContext());
}
}
}
diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java
index 90f6a98d..d1af4168 100644
--- a/src/core/lombok/javac/handlers/HandleValue.java
+++ b/src/core/lombok/javac/handlers/HandleValue.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 The Project Lombok Authors.
+ * Copyright (C) 2012-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
@@ -24,8 +24,6 @@ package lombok.javac.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
-import java.lang.annotation.Annotation;
-
import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.core.AnnotationValues;
@@ -49,13 +47,16 @@ import com.sun.tools.javac.tree.JCTree.JCModifiers;
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier.
public class HandleValue extends JavacAnnotationHandler<Value> {
+ private HandleFieldDefaults handleFieldDefaults = new HandleFieldDefaults();
+ private HandleConstructor handleConstructor = new HandleConstructor();
+ private HandleGetter handleGetter = new HandleGetter();
+ private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode();
+ private HandleToString handleToString = new HandleToString();
+
@Override public void handle(AnnotationValues<Value> annotation, JCAnnotation ast, JavacNode annotationNode) {
- @SuppressWarnings("deprecation")
- Class<? extends Annotation> oldExperimentalValue = lombok.experimental.Value.class;
-
handleFlagUsage(annotationNode, ConfigurationKeys.VALUE_FLAG_USAGE, "@Value");
- deleteAnnotationIfNeccessary(annotationNode, Value.class, oldExperimentalValue);
+ deleteAnnotationIfNeccessary(annotationNode, Value.class, "lombok.experimental.Value");
JavacNode typeNode = annotationNode.up();
boolean notAClass = !isClass(typeNode);
@@ -73,12 +74,10 @@ public class HandleValue extends JavacAnnotationHandler<Value> {
typeNode.rebuild();
}
}
- new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
-
- // TODO move this to the end OR move it to the top in eclipse.
- new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
- new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
- new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode);
- new HandleToString().generateToStringForType(typeNode, annotationNode);
+ handleFieldDefaults.generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true);
+ handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode);
+ handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true);
+ handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode);
+ handleToString.generateToStringForType(typeNode, annotationNode);
}
}
diff --git a/src/core/lombok/javac/handlers/HandleWither.java b/src/core/lombok/javac/handlers/HandleWither.java
index 8a7b49b2..987a3d34 100644
--- a/src/core/lombok/javac/handlers/HandleWither.java
+++ b/src/core/lombok/javac/handlers/HandleWither.java
@@ -41,6 +41,8 @@ import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
@@ -129,8 +131,8 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
if (level == AccessLevel.NONE || node == null) return;
- List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Setter(onMethod=", annotationNode);
- List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Setter(onParam=", annotationNode);
+ List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod", annotationNode);
+ List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(onParam", annotationNode);
switch (node.getKind()) {
case FIELD:
@@ -150,7 +152,10 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
}
}
- public void createWitherForField(AccessLevel level, JavacNode fieldNode, JavacNode source, boolean whineIfExists, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
+ public void createWitherForField(AccessLevel level, JavacNode fieldNode, JavacNode source, boolean strictMode, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
+ JavacNode typeNode = fieldNode.up();
+ boolean makeAbstract = typeNode != null && typeNode.getKind() == Kind.TYPE && (((JCClassDecl) typeNode.get()).mods.flags & Flags.ABSTRACT) != 0;
+
if (fieldNode.getKind() != Kind.FIELD) {
fieldNode.addError("@Wither is only supported on a class or a field.");
return;
@@ -165,17 +170,23 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
}
if ((fieldDecl.mods.flags & Flags.STATIC) != 0) {
- fieldNode.addWarning("Not generating wither for this field: Withers cannot be generated for static fields.");
+ if (strictMode) {
+ fieldNode.addWarning("Not generating wither for this field: Withers cannot be generated for static fields.");
+ }
return;
}
if ((fieldDecl.mods.flags & Flags.FINAL) != 0 && fieldDecl.init != null) {
- fieldNode.addWarning("Not generating wither for this field: Withers cannot be generated for final, initialized fields.");
+ if (strictMode) {
+ fieldNode.addWarning("Not generating wither for this field: Withers cannot be generated for final, initialized fields.");
+ }
return;
}
if (fieldDecl.name.toString().startsWith("$")) {
- fieldNode.addWarning("Not generating wither for this field: Withers cannot be generated for fields starting with $.");
+ if (strictMode) {
+ fieldNode.addWarning("Not generating wither for this field: Withers cannot be generated for fields starting with $.");
+ }
return;
}
@@ -184,7 +195,7 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
case EXISTS_BY_LOMBOK:
return;
case EXISTS_BY_USER:
- if (whineIfExists) {
+ if (strictMode) {
String altNameExpl = "";
if (!altName.equals(methodName)) altNameExpl = String.format(" (%s)", altName);
fieldNode.addWarning(
@@ -199,63 +210,71 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
long access = toJavacModifier(level);
- JCMethodDecl createdWither = createWither(access, fieldNode, fieldNode.getTreeMaker(), source, onMethod, onParam);
- injectMethod(fieldNode.up(), createdWither);
+ JCMethodDecl createdWither = createWither(access, fieldNode, fieldNode.getTreeMaker(), source, onMethod, onParam, makeAbstract);
+ ClassSymbol sym = ((JCClassDecl) fieldNode.up().get()).sym;
+ Type returnType = sym == null ? null : sym.type;
+
+ injectMethod(typeNode, createdWither, List.<Type>of(getMirrorForFieldType(fieldNode)), returnType);
}
- public JCMethodDecl createWither(long access, JavacNode field, JavacTreeMaker maker, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
+ public JCMethodDecl createWither(long access, JavacNode field, JavacTreeMaker maker, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam, boolean makeAbstract) {
String witherName = toWitherName(field);
if (witherName == null) return null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
- ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
List<JCAnnotation> nonNulls = findAnnotations(field, NON_NULL_PATTERN);
List<JCAnnotation> nullables = findAnnotations(field, NULLABLE_PATTERN);
Name methodName = field.toName(witherName);
- List<JCAnnotation> annsOnParam = copyAnnotations(onParam).appendList(nonNulls).appendList(nullables);
+ JCExpression returnType = cloneSelfType(field);
+
+ JCBlock methodBody = null;
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, field.getContext());
- JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, annsOnParam), fieldDecl.name, fieldDecl.vartype, null);
+ List<JCAnnotation> annsOnParam = copyAnnotations(onParam).appendList(nonNulls).appendList(nullables);
- JCExpression selfType = cloneSelfType(field);
- if (selfType == null) return null;
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, annsOnParam), fieldDecl.name, fieldDecl.vartype, null);
- ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
- for (JavacNode child : field.up().down()) {
- if (child.getKind() != Kind.FIELD) continue;
- JCVariableDecl childDecl = (JCVariableDecl) child.get();
- // Skip fields that start with $
- if (childDecl.name.toString().startsWith("$")) continue;
- long fieldFlags = childDecl.mods.flags;
- // Skip static fields.
- if ((fieldFlags & Flags.STATIC) != 0) continue;
- // Skip initialized final fields.
- if (((fieldFlags & Flags.FINAL) != 0) && childDecl.init != null) continue;
- if (child.get() == field.get()) {
- args.append(maker.Ident(fieldDecl.name));
+ if (!makeAbstract) {
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ JCExpression selfType = cloneSelfType(field);
+ if (selfType == null) return null;
+
+ ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
+ for (JavacNode child : field.up().down()) {
+ if (child.getKind() != Kind.FIELD) continue;
+ JCVariableDecl childDecl = (JCVariableDecl) child.get();
+ // Skip fields that start with $
+ if (childDecl.name.toString().startsWith("$")) continue;
+ long fieldFlags = childDecl.mods.flags;
+ // Skip static fields.
+ if ((fieldFlags & Flags.STATIC) != 0) continue;
+ // Skip initialized final fields.
+ if (((fieldFlags & Flags.FINAL) != 0) && childDecl.init != null) continue;
+ if (child.get() == field.get()) {
+ args.append(maker.Ident(fieldDecl.name));
+ } else {
+ args.append(createFieldAccessor(maker, child, FieldAccess.ALWAYS_FIELD));
+ }
+ }
+
+ JCNewClass newClass = maker.NewClass(null, List.<JCExpression>nil(), selfType, args.toList(), null);
+ JCExpression identityCheck = maker.Binary(CTC_EQUAL, createFieldAccessor(maker, field, FieldAccess.ALWAYS_FIELD), maker.Ident(fieldDecl.name));
+ JCConditional conditional = maker.Conditional(identityCheck, maker.Ident(field.toName("this")), newClass);
+ JCReturn returnStatement = maker.Return(conditional);
+
+ if (nonNulls.isEmpty()) {
+ statements.append(returnStatement);
} else {
- args.append(createFieldAccessor(maker, child, FieldAccess.ALWAYS_FIELD));
+ JCStatement nullCheck = generateNullCheck(maker, field, source);
+ if (nullCheck != null) statements.append(nullCheck);
+ statements.append(returnStatement);
}
+
+ methodBody = maker.Block(0, statements.toList());
}
-
- JCNewClass newClass = maker.NewClass(null, List.<JCExpression>nil(), selfType, args.toList(), null);
- JCExpression identityCheck = maker.Binary(CTC_EQUAL, createFieldAccessor(maker, field, FieldAccess.ALWAYS_FIELD), maker.Ident(fieldDecl.name));
- JCConditional conditional = maker.Conditional(identityCheck, maker.Ident(field.toName("this")), newClass);
- JCReturn returnStatement = maker.Return(conditional);
-
- if (nonNulls.isEmpty()) {
- statements.append(returnStatement);
- } else {
- JCStatement nullCheck = generateNullCheck(maker, field, source);
- if (nullCheck != null) statements.append(nullCheck);
- statements.append(returnStatement);
- }
-
- JCExpression returnType = cloneSelfType(field);
-
- JCBlock methodBody = maker.Block(0, statements.toList());
List<JCTypeParameter> methodGenericParams = List.nil();
List<JCVariableDecl> parameters = List.of(param);
List<JCExpression> throwsClauses = List.nil();
@@ -266,6 +285,7 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
if (isFieldDeprecated(field)) {
annsOnMethod = annsOnMethod.prepend(maker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil()));
}
+ if (makeAbstract) access = access | Flags.ABSTRACT;
JCMethodDecl decl = recursiveSetGeneratedBy(maker.MethodDef(maker.Modifiers(access, annsOnMethod), methodName, returnType,
methodGenericParams, parameters, throwsClauses, methodBody, annotationMethodDefaultValue), source.get(), field.getContext());
copyJavadoc(field, decl, CopyJavadoc.WITHER);
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 6413e8ef..65d09a9a 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-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
@@ -21,11 +21,13 @@
*/
package lombok.javac.handlers;
+import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.Javac.*;
import static lombok.javac.JavacAugments.JCTree_generatedNode;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -35,12 +37,15 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.lang.model.element.Element;
+
import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.Data;
import lombok.Getter;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
+import lombok.core.LombokImmutableList;
import lombok.core.AnnotationValues.AnnotationValue;
import lombok.core.TypeResolver;
import lombok.core.configuration.NullCheckExceptionType;
@@ -54,6 +59,14 @@ import lombok.javac.JavacTreeMaker;
import com.sun.tools.javac.code.BoundKind;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Scope;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Type.MethodType;
import com.sun.tools.javac.parser.Tokens.Comment;
import com.sun.tools.javac.tree.DocCommentTable;
import com.sun.tools.javac.tree.JCTree;
@@ -68,7 +81,6 @@ import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
-import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
@@ -80,6 +92,7 @@ import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCWildcard;
import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
+import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@@ -149,6 +162,10 @@ public class JavacHandlerUtil {
return node;
}
+ public static boolean hasAnnotation(String type, JavacNode node) {
+ return hasAnnotation(type, node, false);
+ }
+
public static boolean hasAnnotation(Class<? extends Annotation> type, JavacNode node) {
return hasAnnotation(type, node, false);
}
@@ -178,6 +195,48 @@ public class JavacHandlerUtil {
}
}
+ private static boolean hasAnnotation(String type, JavacNode node, boolean delete) {
+ if (node == null) return false;
+ if (type == null) return false;
+ switch (node.getKind()) {
+ case ARGUMENT:
+ case FIELD:
+ case LOCAL:
+ case TYPE:
+ case METHOD:
+ for (JavacNode child : node.down()) {
+ if (annotationTypeMatches(type, child)) {
+ if (delete) deleteAnnotationIfNeccessary(child, type);
+ return true;
+ }
+ }
+ // intentional fallthrough
+ default:
+ return false;
+ }
+ }
+
+ static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node, boolean delete) {
+ if (node == null) return null;
+ if (type == null) return null;
+ switch (node.getKind()) {
+ case ARGUMENT:
+ case FIELD:
+ case LOCAL:
+ case TYPE:
+ case METHOD:
+ for (JavacNode child : node.down()) {
+ if (annotationTypeMatches(type, child)) {
+ if (delete) deleteAnnotationIfNeccessary(child, type);
+ return child;
+ }
+ }
+ // intentional fallthrough
+ default:
+ return null;
+ }
+ }
+
/**
* Checks if the Annotation AST Node provided is likely to be an instance of the provided annotation type.
*
@@ -190,6 +249,17 @@ public class JavacHandlerUtil {
}
/**
+ * Checks if the Annotation AST Node provided is likely to be an instance of the provided annotation type.
+ *
+ * @param type An actual annotation type, such as {@code lombok.Getter.class}.
+ * @param node A Lombok AST node representing an annotation in source code.
+ */
+ public static boolean annotationTypeMatches(String type, JavacNode node) {
+ if (node.getKind() != Kind.ANNOTATION) return false;
+ return typeMatches(type, node, ((JCAnnotation)node.get()).annotationType);
+ }
+
+ /**
* Checks if the given TypeReference node is likely to be a reference to the provided class.
*
* @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type.
@@ -197,10 +267,21 @@ public class JavacHandlerUtil {
* @param typeNode A type reference to check.
*/
public static boolean typeMatches(Class<?> type, JavacNode node, JCTree typeNode) {
+ return typeMatches(type.getName(), node, typeNode);
+ }
+
+ /**
+ * Checks if the given TypeReference node is likely to be a reference to the provided class.
+ *
+ * @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type.
+ * @param node A Lombok AST node. Any node in the appropriate compilation unit will do (used to get access to import statements).
+ * @param typeNode A type reference to check.
+ */
+ public static boolean typeMatches(String type, JavacNode node, JCTree typeNode) {
String typeName = typeNode.toString();
TypeResolver resolver = new TypeResolver(node.getImportList());
- return resolver.typeMatches(node, type.getName(), typeName);
+ return resolver.typeMatches(node, type, typeName);
}
/**
@@ -209,6 +290,7 @@ public class JavacHandlerUtil {
* @return {@code true} if a field is marked deprecated, either by {@code @Deprecated} or in javadoc, otherwise {@code false}
*/
public static boolean isFieldDeprecated(JavacNode field) {
+ if (!(field.get() instanceof JCVariableDecl)) return false;
JCVariableDecl fieldNode = (JCVariableDecl) field.get();
if ((fieldNode.mods.flags & Flags.DEPRECATED) != 0) {
return true;
@@ -242,51 +324,45 @@ public class JavacHandlerUtil {
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
JCAnnotation anno = (JCAnnotation) node.get();
List<JCExpression> arguments = anno.getArguments();
- for (Method m : type.getDeclaredMethods()) {
- if (!Modifier.isPublic(m.getModifiers())) continue;
- String name = m.getName();
+
+ for (JCExpression arg : arguments) {
+ String mName;
+ JCExpression rhs;
java.util.List<String> raws = new ArrayList<String>();
java.util.List<Object> guesses = new ArrayList<Object>();
java.util.List<Object> expressions = new ArrayList<Object>();
final java.util.List<DiagnosticPosition> positions = new ArrayList<DiagnosticPosition>();
- boolean isExplicit = false;
- for (JCExpression arg : arguments) {
- String mName;
- JCExpression rhs;
-
- if (arg instanceof JCAssign) {
- JCAssign assign = (JCAssign) arg;
- mName = assign.lhs.toString();
- rhs = assign.rhs;
- } else {
- rhs = arg;
- mName = "value";
- }
-
- if (!mName.equals(name)) continue;
- isExplicit = true;
- if (rhs instanceof JCNewArray) {
- List<JCExpression> elems = ((JCNewArray)rhs).elems;
- for (JCExpression inner : elems) {
- raws.add(inner.toString());
- expressions.add(inner);
- guesses.add(calculateGuess(inner));
- positions.add(inner.pos());
- }
- } else {
- raws.add(rhs.toString());
- expressions.add(rhs);
- guesses.add(calculateGuess(rhs));
- positions.add(rhs.pos());
+ if (arg instanceof JCAssign) {
+ JCAssign assign = (JCAssign) arg;
+ mName = assign.lhs.toString();
+ rhs = assign.rhs;
+ } else {
+ rhs = arg;
+ mName = "value";
+ }
+
+ if (rhs instanceof JCNewArray) {
+ List<JCExpression> elems = ((JCNewArray)rhs).elems;
+ for (JCExpression inner : elems) {
+ raws.add(inner.toString());
+ expressions.add(inner);
+ guesses.add(calculateGuess(inner));
+ positions.add(inner.pos());
}
+ } else {
+ raws.add(rhs.toString());
+ expressions.add(rhs);
+ guesses.add(calculateGuess(rhs));
+ positions.add(rhs.pos());
}
- values.put(name, new AnnotationValue(node, raws, expressions, guesses, isExplicit) {
+ values.put(mName, new AnnotationValue(node, raws, expressions, guesses, true) {
@Override public void setError(String message, int valueIdx) {
if (valueIdx < 0) node.addError(message);
else node.addError(message, positions.get(valueIdx));
}
+
@Override public void setWarning(String message, int valueIdx) {
if (valueIdx < 0) node.addWarning(message);
else node.addWarning(message, positions.get(valueIdx));
@@ -294,6 +370,21 @@ public class JavacHandlerUtil {
});
}
+ for (Method m : type.getDeclaredMethods()) {
+ if (!Modifier.isPublic(m.getModifiers())) continue;
+ String name = m.getName();
+ if (!values.containsKey(name)) {
+ values.put(name, new AnnotationValue(node, new ArrayList<String>(), new ArrayList<Object>(), new ArrayList<Object>(), false) {
+ @Override public void setError(String message, int valueIdx) {
+ node.addError(message);
+ }
+ @Override public void setWarning(String message, int valueIdx) {
+ node.addWarning(message);
+ }
+ });
+ }
+ }
+
return new AnnotationValues<A>(type, values, node);
}
@@ -302,8 +393,7 @@ public class JavacHandlerUtil {
* then removes any import statement that imports this exact annotation (not star imports).
* Only does this if the DeleteLombokAnnotations class is in the context.
*/
- @SuppressWarnings("unchecked")
- public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) {
+ public static void deleteAnnotationIfNeccessary(JavacNode annotation, String annotationType) {
deleteAnnotationIfNeccessary0(annotation, annotationType);
}
@@ -312,12 +402,29 @@ public class JavacHandlerUtil {
* then removes any import statement that imports this exact annotation (not star imports).
* Only does this if the DeleteLombokAnnotations class is in the context.
*/
- @SuppressWarnings("unchecked")
+ public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) {
+ deleteAnnotationIfNeccessary0(annotation, annotationType.getName());
+ }
+
+ /**
+ * Removes the annotation from javac's AST (it remains in lombok's AST),
+ * then removes any import statement that imports this exact annotation (not star imports).
+ * Only does this if the DeleteLombokAnnotations class is in the context.
+ */
public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, Class<? extends Annotation> annotationType2) {
- deleteAnnotationIfNeccessary0(annotation, annotationType1, annotationType2);
+ deleteAnnotationIfNeccessary0(annotation, annotationType1.getName(), annotationType2.getName());
}
- private static void deleteAnnotationIfNeccessary0(JavacNode annotation, Class<? extends Annotation>... annotationTypes) {
+ /**
+ * Removes the annotation from javac's AST (it remains in lombok's AST),
+ * then removes any import statement that imports this exact annotation (not star imports).
+ * Only does this if the DeleteLombokAnnotations class is in the context.
+ */
+ public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, String annotationType2) {
+ deleteAnnotationIfNeccessary0(annotation, annotationType1.getName(), annotationType2);
+ }
+
+ private static void deleteAnnotationIfNeccessary0(JavacNode annotation, String... annotationTypes) {
if (inNetbeansEditor(annotation)) return;
if (!annotation.shouldDeleteLombokAnnotations()) return;
JavacNode parentNode = annotation.directUp();
@@ -346,8 +453,8 @@ public class JavacHandlerUtil {
}
parentNode.getAst().setChanged();
- for (Class<?> annotationType : annotationTypes) {
- deleteImportFromCompilationUnit(annotation, annotationType.getName());
+ for (String annotationType : annotationTypes) {
+ deleteImportFromCompilationUnit(annotation, annotationType);
}
}
@@ -445,9 +552,9 @@ public class JavacHandlerUtil {
return HandlerUtil.shouldReturnThis0(accessors, field.getAst());
}
- public static JCExpression cloneSelfType(JavacNode field) {
- JavacNode typeNode = field;
- JavacTreeMaker maker = field.getTreeMaker();
+ public static JCExpression cloneSelfType(JavacNode childOfType) {
+ JavacNode typeNode = childOfType;
+ JavacTreeMaker maker = childOfType.getTreeMaker();
while (typeNode != null && typeNode.getKind() != Kind.TYPE) typeNode = typeNode.up();
if (typeNode != null && typeNode.get() instanceof JCClassDecl) {
JCClassDecl type = (JCClassDecl) typeNode.get();
@@ -591,10 +698,7 @@ public class JavacHandlerUtil {
if (params < minArgs || params > maxArgs) continue;
}
- List<JCAnnotation> annotations = md.getModifiers().getAnnotations();
- if (annotations != null) for (JCAnnotation anno : annotations) {
- if (typeMatches(Tolerate.class, node, anno.getAnnotationType())) continue top;
- }
+ if (isTolerate(node, md)) continue top;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
@@ -605,6 +709,14 @@ public class JavacHandlerUtil {
return MemberExistsResult.NOT_EXISTS;
}
+ public static boolean isTolerate(JavacNode node, JCTree.JCMethodDecl md) {
+ List<JCAnnotation> annotations = md.getModifiers().getAnnotations();
+ if (annotations != null) for (JCTree.JCAnnotation anno : annotations) {
+ if (typeMatches(Tolerate.class, node, anno.getAnnotationType())) return true;
+ }
+ return false;
+ }
+
/**
* Checks if there is a (non-default) constructor. In case of multiple constructors (overloading), only
* the first constructor decides if EXISTS_BY_USER or EXISTS_BY_LOMBOK is returned.
@@ -615,15 +727,12 @@ public class JavacHandlerUtil {
node = upToTypeNode(node);
if (node != null && node.get() instanceof JCClassDecl) {
- top: for (JCTree def : ((JCClassDecl)node.get()).defs) {
+ for (JCTree def : ((JCClassDecl)node.get()).defs) {
if (def instanceof JCMethodDecl) {
JCMethodDecl md = (JCMethodDecl) def;
if (md.name.contentEquals("<init>")) {
if ((md.mods.flags & Flags.GENERATEDCONSTR) != 0) continue;
- List<JCAnnotation> annotations = md.getModifiers().getAnnotations();
- if (annotations != null) for (JCAnnotation anno : annotations) {
- if (typeMatches(Tolerate.class, node, anno.getAnnotationType())) continue top;
- }
+ if (isTolerate(node, md)) continue;
return getGeneratedBy(def) == null ? MemberExistsResult.EXISTS_BY_USER : MemberExistsResult.EXISTS_BY_LOMBOK;
}
}
@@ -709,7 +818,7 @@ public class JavacHandlerUtil {
// Check if the class has a @Getter annotation.
- if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) {
+ if (!hasGetterAnnotation && HandleGetter.fieldQualifiesForGetterGeneration(field)) {
//Check if the class has @Getter or @Data annotation.
JavacNode containingType = field.up();
@@ -803,13 +912,19 @@ public class JavacHandlerUtil {
return call;
}
+ public static Type getMirrorForFieldType(JavacNode fieldNode) {
+ Element fieldElement = fieldNode.getElement();
+ if (fieldElement instanceof VarSymbol) return ((VarSymbol) fieldElement).type;
+ return null;
+ }
+
/**
* Adds the given new field declaration to the provided type AST Node.
* The field carries the &#64;{@link SuppressWarnings}("all") annotation.
* Also takes care of updating the JavacAST.
*/
- public static void injectFieldSuppressWarnings(JavacNode typeNode, JCVariableDecl field) {
- injectField(typeNode, field, true);
+ public static JavacNode injectFieldAndMarkGenerated(JavacNode typeNode, JCVariableDecl field) {
+ return injectField(typeNode, field, true);
}
/**
@@ -821,21 +936,28 @@ public class JavacHandlerUtil {
return injectField(typeNode, field, false);
}
- private static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addSuppressWarnings) {
+ private static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addGenerated) {
JCClassDecl type = (JCClassDecl) typeNode.get();
- if (addSuppressWarnings) addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext());
+ if (addGenerated) {
+ addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext());
+ addGenerated(field.mods, typeNode, field.pos, getGeneratedBy(field), typeNode.getContext());
+ }
List<JCTree> insertAfter = null;
List<JCTree> insertBefore = type.defs;
- while (insertBefore.tail != null) {
+ while (true) {
+ boolean skip = false;
if (insertBefore.head instanceof JCVariableDecl) {
JCVariableDecl f = (JCVariableDecl) insertBefore.head;
- if (isEnumConstant(f) || isGenerated(f)) {
- insertAfter = insertBefore;
- insertBefore = insertBefore.tail;
- continue;
- }
+ if (isEnumConstant(f) || isGenerated(f)) skip = true;
+ } else if (insertBefore.head instanceof JCMethodDecl) {
+ if ((((JCMethodDecl) insertBefore.head).mods.flags & GENERATEDCONSTR) != 0) skip = true;
+ }
+ if (skip) {
+ insertAfter = insertBefore;
+ insertBefore = insertBefore.tail;
+ continue;
}
break;
}
@@ -854,13 +976,56 @@ public class JavacHandlerUtil {
return (field.mods.flags & Flags.ENUM) != 0;
}
+ // jdk9 support, types have changed, names stay the same
+ static class ClassSymbolMembersField {
+ private static final Field membersField;
+ private static final Method removeMethod;
+ private static final Method enterMethod;
+
+ static {
+ Field f = null;
+ 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);
+ } catch (Exception ex) {}
+ membersField = f;
+ removeMethod = r;
+ enterMethod = e;
+ }
+
+ static void remove(ClassSymbol from, Symbol toRemove) {
+ if (from == null) return;
+ try {
+ Scope scope = (Scope) membersField.get(from);
+ if (scope == null) return;
+ removeMethod.invoke(scope, toRemove);
+ } catch (Exception e) {}
+ }
+
+ static void enter(ClassSymbol from, Symbol toEnter) {
+ if (from == null) return;
+ try {
+ Scope scope = (Scope) membersField.get(from);
+ if (scope == null) return;
+ enterMethod.invoke(scope, toEnter);
+ } catch (Exception e) {}
+ }
+ }
+
+ public static void injectMethod(JavacNode typeNode, JCMethodDecl method) {
+ injectMethod(typeNode, method, null, null);
+ }
+
/**
* Adds the given new method declaration to the provided type AST Node.
* Can also inject constructors.
*
* Also takes care of updating the JavacAST.
*/
- public static void injectMethod(JavacNode typeNode, JCMethodDecl method) {
+ public static void injectMethod(JavacNode typeNode, JCMethodDecl method, List<Type> paramTypes, Type returnType) {
JCClassDecl type = (JCClassDecl) typeNode.get();
if (method.getName().contentEquals("<init>")) {
@@ -868,13 +1033,11 @@ public class JavacHandlerUtil {
int idx = 0;
for (JCTree def : type.defs) {
if (def instanceof JCMethodDecl) {
- if ((((JCMethodDecl)def).mods.flags & Flags.GENERATEDCONSTR) != 0) {
+ if ((((JCMethodDecl) def).mods.flags & Flags.GENERATEDCONSTR) != 0) {
JavacNode tossMe = typeNode.getNodeFor(def);
if (tossMe != null) tossMe.up().removeChild(tossMe);
type.defs = addAllButOne(type.defs, idx);
- if (type.sym != null && type.sym.members_field != null) {
- type.sym.members_field.remove(((JCMethodDecl)def).sym);
- }
+ ClassSymbolMembersField.remove(type.sym, ((JCMethodDecl)def).sym);
break;
}
}
@@ -883,11 +1046,21 @@ public class JavacHandlerUtil {
}
addSuppressWarningsAll(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext());
+ addGenerated(method.mods, typeNode, method.pos, getGeneratedBy(method), typeNode.getContext());
type.defs = type.defs.append(method);
+ fixMethodMirror(typeNode.getContext(), typeNode.getElement(), method.getModifiers().flags, method.getName(), paramTypes, returnType);
+
typeNode.add(method, Kind.METHOD);
}
+ private static void fixMethodMirror(Context context, Element typeMirror, long access, Name methodName, List<Type> paramTypes, Type returnType) {
+ if (typeMirror == null || paramTypes == null || returnType == null) return;
+ ClassSymbol cs = (ClassSymbol) typeMirror;
+ MethodSymbol methodSymbol = new MethodSymbol(access, methodName, new MethodType(paramTypes, returnType, List.<Type>nil(), Symtab.instance(context).methodClass), cs);
+ ClassSymbolMembersField.enter(cs, methodSymbol);
+ }
+
/**
* Adds an inner type (class, interface, enum) to the given type. Cannot inject top-level types.
*
@@ -898,6 +1071,7 @@ public class JavacHandlerUtil {
public static JavacNode injectType(JavacNode typeNode, final JCClassDecl type) {
JCClassDecl typeDecl = (JCClassDecl) typeNode.get();
addSuppressWarningsAll(type.mods, typeNode, type.pos, getGeneratedBy(type), typeNode.getContext());
+ addGenerated(type.mods, typeNode, type.pos, getGeneratedBy(type), typeNode.getContext());
typeDecl.defs = typeDecl.defs.append(type);
return typeNode.add(type, Kind.TYPE);
}
@@ -938,20 +1112,59 @@ public class JavacHandlerUtil {
public static void addSuppressWarningsAll(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) {
if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateSuppressWarnings()) return;
+ addAnnotation(mods, node, pos, source, context, "java.lang.SuppressWarnings", node.getTreeMaker().Literal("all"));
+
+ if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_FINDBUGS_SUPPRESSWARNINGS_ANNOTATIONS))) {
+ JavacTreeMaker maker = node.getTreeMaker();
+ JCExpression arg = maker.Assign(maker.Ident(node.toName("justification")), maker.Literal("generated code"));
+ addAnnotation(mods, node, pos, source, context, "edu.umd.cs.findbugs.annotations.SuppressFBWarnings", arg);
+ }
+ }
+
+ public static void addGenerated(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) {
+ if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateGenerated()) return;
+
+ if (HandlerUtil.shouldAddGenerated(node)) {
+ addAnnotation(mods, node, pos, source, context, "javax.annotation.Generated", node.getTreeMaker().Literal("lombok"));
+ }
+ if (Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_LOMBOK_GENERATED_ANNOTATIONS))) {
+ addAnnotation(mods, node, pos, source, context, "lombok.Generated", null);
+ }
+ }
+
+ private static void addAnnotation(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context, String annotationTypeFqn, JCExpression arg) {
+ boolean isJavaLangBased;
+ String simpleName; {
+ int idx = annotationTypeFqn.lastIndexOf('.');
+ simpleName = idx == -1 ? annotationTypeFqn : annotationTypeFqn.substring(idx + 1);
+
+ isJavaLangBased = idx == 9 && annotationTypeFqn.regionMatches(0, "java.lang.", 0, 10);
+ }
+
for (JCAnnotation ann : mods.annotations) {
JCTree annType = ann.getAnnotationType();
- Name lastPart = null;
- if (annType instanceof JCIdent) lastPart = ((JCIdent) annType).name;
- else if (annType instanceof JCFieldAccess) lastPart = ((JCFieldAccess) annType).name;
+ if (annType instanceof JCIdent) {
+ Name lastPart = ((JCIdent) annType).name;
+ if (lastPart.contentEquals(simpleName)) return;
+ }
- if (lastPart != null && lastPart.contentEquals("SuppressWarnings")) return;
+ if (annType instanceof JCFieldAccess) {
+ if (annType.toString().equals(annotationTypeFqn)) return;
+ }
}
+
JavacTreeMaker maker = node.getTreeMaker();
- JCExpression suppressWarningsType = genJavaLangTypeRef(node, "SuppressWarnings");
- JCLiteral allLiteral = maker.Literal("all");
- suppressWarningsType.pos = pos;
- allLiteral.pos = pos;
- JCAnnotation annotation = recursiveSetGeneratedBy(maker.Annotation(suppressWarningsType, List.<JCExpression>of(allLiteral)), source, context);
+ JCExpression annType = isJavaLangBased ? genJavaLangTypeRef(node, simpleName) : chainDotsString(node, annotationTypeFqn);
+ annType.pos = pos;
+ if (arg != null) {
+ arg.pos = pos;
+ if (arg instanceof JCAssign) {
+ ((JCAssign) arg).lhs.pos = pos;
+ ((JCAssign) arg).rhs.pos = pos;
+ }
+ }
+ List<JCExpression> argList = arg != null ? List.of(arg) : List.<JCExpression>nil();
+ JCAnnotation annotation = recursiveSetGeneratedBy(maker.Annotation(annType, argList), source, context);
annotation.pos = pos;
mods.annotations = mods.annotations.append(annotation);
}
@@ -985,6 +1198,17 @@ public class JavacHandlerUtil {
return chainDots(node, -1, null, null, elems);
}
+ public static JCExpression chainDots(JavacNode node, LombokImmutableList<String> elems) {
+ assert elems != null;
+
+ JavacTreeMaker maker = node.getTreeMaker();
+ JCExpression e = null;
+ for (String elem : elems) {
+ if (e == null) e = maker.Ident(node.toName(elem));
+ else e = maker.Select(e, node.toName(elem));
+ }
+ return e;
+ }
/**
* In javac, dotted access of any kind, from {@code java.lang.String} to {@code var.methodName}
* is represented by a fold-left of {@code Select} nodes with the leftmost string represented by
@@ -1013,7 +1237,6 @@ public class JavacHandlerUtil {
return e;
}
-
/**
* In javac, dotted access of any kind, from {@code java.lang.String} to {@code var.methodName}
@@ -1051,16 +1274,25 @@ public class JavacHandlerUtil {
}
/**
- * Generates a new statement that checks if the given variable is null, and if so, throws a specified exception with the
+ * Generates a new statement that checks if the given variable is null, and if so, throws a configured exception with the
* variable name as message.
- *
- * @param exName The name of the exception to throw; normally {@code java.lang.NullPointerException}.
*/
public static JCStatement generateNullCheck(JavacTreeMaker maker, JavacNode variable, JavacNode source) {
+ return generateNullCheck(maker, variable, (JCVariableDecl)variable.get(), source);
+ }
+
+ /**
+ * Generates a new statement that checks if the given variable is null, and if so, throws a configured exception with the
+ * variable name as message.
+ *
+ * This is a special case method reserved for use when the provided declaration differs from the
+ * variable's declaration, i.e. in a constructor or setter where the local parameter is named the same but with the prefix
+ * stripped as a result of @Accessors.prefix.
+ */
+ public static JCStatement generateNullCheck(JavacTreeMaker maker, JavacNode variable, JCVariableDecl varDecl, JavacNode source) {
NullCheckExceptionType exceptionType = source.getAst().readConfiguration(ConfigurationKeys.NON_NULL_EXCEPTION_TYPE);
if (exceptionType == null) exceptionType = NullCheckExceptionType.NULL_POINTER_EXCEPTION;
- JCVariableDecl varDecl = (JCVariableDecl) variable.get();
if (isPrimitive(varDecl.vartype)) return null;
Name fieldName = varDecl.name;
JCExpression exType = genTypeRef(variable, exceptionType.getExceptionType());
@@ -1102,19 +1334,9 @@ public class JavacHandlerUtil {
ListBuffer<JCExpression> params = new ListBuffer<JCExpression>();
ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>();
- try {
- for (JCExpression arg : ast.args) {
- String argName = "value";
- if (arg instanceof JCAssign) {
- JCAssign as = (JCAssign) arg;
- argName = as.lhs.toString();
- }
- if (!argName.equals(parameterName)) continue;
- }
- } catch (Exception ignore) {}
-
outer:
for (JCExpression param : ast.args) {
+ boolean allowRaw;
String nameOfParam = "value";
JCExpression valueOfParam = null;
if (param instanceof JCAssign) {
@@ -1126,6 +1348,15 @@ public class JavacHandlerUtil {
valueOfParam = assign.rhs;
}
+ /* strip trailing underscores */ {
+ int lastIdx;
+ for (lastIdx = nameOfParam.length() ; lastIdx > 0; lastIdx--) {
+ if (nameOfParam.charAt(lastIdx - 1) != '_') break;
+ }
+ allowRaw = lastIdx < nameOfParam.length();
+ nameOfParam = nameOfParam.substring(0, lastIdx);
+ }
+
if (!parameterName.equals(nameOfParam)) {
params.append(param);
continue outer;
@@ -1138,75 +1369,109 @@ public class JavacHandlerUtil {
String dummyAnnotationName = ((JCAnnotation) valueOfParam).annotationType.toString();
dummyAnnotationName = dummyAnnotationName.replace("_", "").replace("$", "").replace("x", "").replace("X", "");
if (dummyAnnotationName.length() > 0) {
- annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
- continue outer;
- }
- for (JCExpression expr : ((JCAnnotation) valueOfParam).args) {
- if (expr instanceof JCAssign && ((JCAssign) expr).lhs instanceof JCIdent) {
- JCIdent id = (JCIdent) ((JCAssign) expr).lhs;
- if ("value".equals(id.name.toString())) {
- expr = ((JCAssign) expr).rhs;
- } else {
- annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
- continue outer;
- }
+ if (allowRaw) {
+ result.append((JCAnnotation) valueOfParam);
+ } else {
+ addError(errorName, annotationNode);
+ continue outer;
}
-
- if (expr instanceof JCAnnotation) {
- result.append((JCAnnotation) expr);
- } else if (expr instanceof JCNewArray) {
- for (JCExpression expr2 : ((JCNewArray) expr).elems) {
- if (expr2 instanceof JCAnnotation) {
- result.append((JCAnnotation) expr2);
+ } else {
+ for (JCExpression expr : ((JCAnnotation) valueOfParam).args) {
+ if (expr instanceof JCAssign && ((JCAssign) expr).lhs instanceof JCIdent) {
+ JCIdent id = (JCIdent) ((JCAssign) expr).lhs;
+ if ("value".equals(id.name.toString())) {
+ expr = ((JCAssign) expr).rhs;
} else {
- annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
- continue outer;
+ addError(errorName, annotationNode);
}
}
- } else {
- annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
- continue outer;
+
+ if (expr instanceof JCAnnotation) {
+ result.append((JCAnnotation) expr);
+ } else if (expr instanceof JCNewArray) {
+ for (JCExpression expr2 : ((JCNewArray) expr).elems) {
+ if (expr2 instanceof JCAnnotation) {
+ result.append((JCAnnotation) expr2);
+ } else {
+ addError(errorName, annotationNode);
+ continue outer;
+ }
+ }
+ } else {
+ addError(errorName, annotationNode);
+ continue outer;
+ }
}
}
- } else {
- if (valueOfParam instanceof JCNewArray && ((JCNewArray) valueOfParam).elems.isEmpty()) {
- // Then we just remove it and move on (it's onMethod={} for example).
+ } else if (valueOfParam instanceof JCNewArray) {
+ JCNewArray arr = (JCNewArray) valueOfParam;
+ if (arr.elems.isEmpty()) {
+ // Just remove it, this is always fine.
+ } else if (allowRaw) {
+ for (JCExpression jce : arr.elems) {
+ if (jce instanceof JCAnnotation) result.append((JCAnnotation) jce);
+ else addError(errorName, annotationNode);
+ }
} else {
- annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ addError(errorName, annotationNode);
}
+ } else {
+ addError(errorName, annotationNode);
}
}
ast.args = params.toList();
return result.toList();
}
- public static List<JCTypeParameter> copyTypeParams(JavacTreeMaker maker, List<JCTypeParameter> params) {
+ private static void addError(String errorName, JavacNode node) {
+ if (node.getLatestJavaSpecSupported() < 8) {
+ node.addError("The correct format up to JDK7 is " + errorName + "=@__({@SomeAnnotation, @SomeOtherAnnotation}))");
+ } else {
+ node.addError("The correct format for JDK8+ is " + errorName + "_={@SomeAnnotation, @SomeOtherAnnotation})");
+ }
+ }
+
+ public static List<JCTypeParameter> copyTypeParams(JavacNode source, List<JCTypeParameter> params) {
if (params == null || params.isEmpty()) return params;
ListBuffer<JCTypeParameter> out = new ListBuffer<JCTypeParameter>();
- for (JCTypeParameter tp : params) out.append(maker.TypeParameter(tp.name, tp.bounds));
+ JavacTreeMaker maker = source.getTreeMaker();
+ Context context = source.getContext();
+ for (JCTypeParameter tp : params) {
+ List<JCExpression> bounds = tp.bounds;
+ if (bounds != null && !bounds.isEmpty()) {
+ ListBuffer<JCExpression> boundsCopy = new ListBuffer<JCExpression>();
+ for (JCExpression expr : tp.bounds) {
+ boundsCopy.append(cloneType(maker, expr, source.get(), context));
+ }
+ bounds = boundsCopy.toList();
+ }
+ out.append(maker.TypeParameter(tp.name, bounds));
+ }
return out.toList();
}
public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, Name typeName, List<JCTypeParameter> params) {
+ if (params.isEmpty()) {
+ return maker.Ident(typeName);
+ }
+ return maker.TypeApply(maker.Ident(typeName), typeParameterNames(maker, params));
+ }
+
+ public static List<JCExpression> typeParameterNames(JavacTreeMaker maker, List<JCTypeParameter> params) {
ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
-
- if (!params.isEmpty()) {
- for (JCTypeParameter param : params) {
- typeArgs.append(maker.Ident(param.name));
- }
-
- return maker.TypeApply(maker.Ident(typeName), typeArgs.toList());
+ for (JCTypeParameter param : params) {
+ typeArgs.append(maker.Ident(param.name));
}
-
- return maker.Ident(typeName);
+ return typeArgs.toList();
}
public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(JavacNode typeNode, JavacNode errorNode) {
List<String> disallowed = List.nil();
for (JavacNode child : typeNode.down()) {
- for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
+ for (String annType : INVALID_ON_BUILDERS) {
if (annotationTypeMatches(annType, child)) {
- disallowed = disallowed.append(annType.getSimpleName());
+ int lastIndex = annType.lastIndexOf('.');
+ disallowed = disallowed.append(lastIndex == -1 ? annType : annType.substring(lastIndex + 1));
}
}
}
@@ -1429,6 +1694,18 @@ public class JavacHandlerUtil {
} catch (Exception ignore) {}
}
+ private static final Pattern FIND_RETURN = Pattern.compile("^\\s*\\**\\s*@returns?\\s+.*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
+ static String addReturnsThisIfNeeded(String in) {
+ if (FIND_RETURN.matcher(in).find()) return in;
+
+ return addJavadocLine(in, "@return this");
+ }
+
+ static String addJavadocLine(String in, String line) {
+ if (in.endsWith("\n")) return in + line + "\n";
+ return in + "\n" + line;
+ }
+
private static class CopyJavadoc_8 {
static void copyJavadoc(JavacNode from, JCTree to, CopyJavadoc copyMode, Object dc) {
DocCommentTable dct = (DocCommentTable) dc;
@@ -1436,6 +1713,9 @@ public class JavacHandlerUtil {
if (javadoc != null) {
String[] filtered = copyMode.split(javadoc.getText());
+ if (copyMode == CopyJavadoc.SETTER && shouldReturnThis(from)) {
+ filtered[0] = addReturnsThisIfNeeded(filtered[0]);
+ }
dct.putComment(to, createJavadocComment(filtered[0], from));
dct.putComment(from.get(), createJavadocComment(filtered[1], from));
}
@@ -1469,6 +1749,9 @@ public class JavacHandlerUtil {
if (javadoc != null) {
String[] filtered = copyMode.split(javadoc);
+ if (copyMode == CopyJavadoc.SETTER && shouldReturnThis(from)) {
+ filtered[0] = addReturnsThisIfNeeded(filtered[0]);
+ }
docComments.put(to, filtered[0]);
docComments.put(from.get(), filtered[1]);
}
diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
new file mode 100644
index 00000000..6a76e1dd
--- /dev/null
+++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.SpiLoadUtil;
+import lombok.core.TypeLibrary;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+
+import com.sun.source.tree.Tree.Kind;
+import com.sun.tools.javac.code.BoundKind;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.JCWildcard;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+public class JavacSingularsRecipes {
+ private static final JavacSingularsRecipes INSTANCE = new JavacSingularsRecipes();
+ private final Map<String, JavacSingularizer> singularizers = new HashMap<String, JavacSingularizer>();
+ private final TypeLibrary singularizableTypes = new TypeLibrary();
+
+ private JavacSingularsRecipes() {
+ try {
+ loadAll(singularizableTypes, singularizers);
+ singularizableTypes.lock();
+ } catch (IOException e) {
+ System.err.println("Lombok's @Singularizable feature is broken due to misconfigured SPI files: " + e);
+ }
+ }
+
+ private static void loadAll(TypeLibrary library, Map<String, JavacSingularizer> map) throws IOException {
+ for (JavacSingularizer handler : SpiLoadUtil.findServices(JavacSingularizer.class, JavacSingularizer.class.getClassLoader())) {
+ for (String type : handler.getSupportedTypes()) {
+ JavacSingularizer existingSingularizer = map.get(type);
+ if (existingSingularizer != null) {
+ JavacSingularizer toKeep = existingSingularizer.getClass().getName().compareTo(handler.getClass().getName()) > 0 ? handler : existingSingularizer;
+ System.err.println("Multiple singularizers found for type " + type + "; the alphabetically first class is used: " + toKeep.getClass().getName());
+ map.put(type, toKeep);
+ } else {
+ map.put(type, handler);
+ library.addType(type);
+ }
+ }
+ }
+ }
+
+ public static JavacSingularsRecipes get() {
+ return INSTANCE;
+ }
+
+ public String toQualified(String typeReference) {
+ return singularizableTypes.toQualified(typeReference);
+ }
+
+ public JavacSingularizer getSingularizer(String fqn) {
+ return singularizers.get(fqn);
+ }
+
+ public static final class SingularData {
+ private final JavacNode annotation;
+ private final Name singularName;
+ private final Name pluralName;
+ private final List<JCExpression> typeArgs;
+ private final String targetFqn;
+ private final JavacSingularizer singularizer;
+
+ public SingularData(JavacNode annotation, Name singularName, Name pluralName, List<JCExpression> typeArgs, String targetFqn, JavacSingularizer singularizer) {
+ this.annotation = annotation;
+ this.singularName = singularName;
+ this.pluralName = pluralName;
+ this.typeArgs = typeArgs;
+ this.targetFqn = targetFqn;
+ this.singularizer = singularizer;
+ }
+
+ public JavacNode getAnnotation() {
+ return annotation;
+ }
+
+ public Name getSingularName() {
+ return singularName;
+ }
+
+ public Name getPluralName() {
+ return pluralName;
+ }
+
+ public List<JCExpression> getTypeArgs() {
+ return typeArgs;
+ }
+
+ public String getTargetFqn() {
+ return targetFqn;
+ }
+
+ public JavacSingularizer getSingularizer() {
+ return singularizer;
+ }
+
+ public String getTargetSimpleType() {
+ int idx = targetFqn.lastIndexOf(".");
+ return idx == -1 ? targetFqn : targetFqn.substring(idx + 1);
+ }
+ }
+
+ public static abstract class JavacSingularizer {
+ public abstract LombokImmutableList<String> getSupportedTypes();
+
+ protected JCModifiers makeMods(JavacTreeMaker maker, JavacNode node, boolean deprecate) {
+ if (deprecate) return maker.Modifiers(Flags.PUBLIC, List.<JCAnnotation>of(maker.Annotation(genJavaLangTypeRef(node, "Deprecated"), List.<JCExpression>nil())));
+ return maker.Modifiers(Flags.PUBLIC);
+ }
+
+ /** Checks if any of the to-be-generated nodes (fields, methods) already exist. If so, errors on these (singulars don't support manually writing some of it, and returns true). */
+ public boolean checkForAlreadyExistingNodesAndGenerateError(JavacNode builderType, SingularData data) {
+ for (JavacNode child : builderType.down()) {
+ switch (child.getKind()) {
+ case FIELD: {
+ JCVariableDecl field = (JCVariableDecl) child.get();
+ Name name = field.name;
+ if (name == null) break;
+ if (getGeneratedBy(field) != null) continue;
+ for (Name fieldToBeGenerated : listFieldsToBeGenerated(data, builderType)) {
+ if (!fieldToBeGenerated.equals(name)) continue;
+ child.addError("Manually adding a field that @Singular @Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use @Singular.");
+ return true;
+ }
+ break;
+ }
+ case METHOD: {
+ JCMethodDecl method = (JCMethodDecl) child.get();
+ Name name = method.name;
+ if (name == null) break;
+ if (getGeneratedBy(method) != null) continue;
+ for (Name methodToBeGenerated : listMethodsToBeGenerated(data, builderType)) {
+ if (!methodToBeGenerated.equals(name)) continue;
+ child.addError("Manually adding a method that @Singular @Builder would generate is not supported. If you want to manually manage the builder aspect for this field/parameter, don't use @Singular.");
+ return true;
+ }
+ break;
+ }}
+ }
+
+ return false;
+ }
+
+ public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ return Collections.singletonList(data.pluralName);
+ }
+
+ public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ Name p = data.pluralName;
+ Name s = data.singularName;
+ if (p.equals(s)) return Collections.singletonList(p);
+ return Arrays.asList(p, s);
+ }
+
+ public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source);
+ public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain);
+ public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName);
+
+ public boolean requiresCleaning() {
+ try {
+ return !getClass().getMethod("appendCleaningCode", SingularData.class, JavacNode.class, JCTree.class, ListBuffer.class).getDeclaringClass().equals(JavacSingularizer.class);
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
+ public void appendCleaningCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements) {
+ }
+
+ // -- Utility methods --
+
+ /**
+ * Adds the requested number of type arguments to the provided type, copying each argument in {@code typeArgs}. If typeArgs is too long, the extra elements are ignored.
+ * If {@code typeArgs} is null or too short, {@code java.lang.Object} will be substituted for each missing type argument.
+ *
+ * @param count The number of type arguments requested.
+ * @param addExtends If {@code true}, all bounds are either '? extends X' or just '?'. If false, the reverse is applied, and '? extends Foo' is converted to Foo, '?' to Object, etc.
+ * @param node Some node in the same AST. Just used to obtain makers and contexts and such.
+ * @param type The type to add generics to.
+ * @param typeArgs the list of type args to clone.
+ * @param source The source annotation that is the root cause of this code generation.
+ */
+ protected JCExpression addTypeArgs(int count, boolean addExtends, JavacNode node, JCExpression type, List<JCExpression> typeArgs, JCTree source) {
+ JavacTreeMaker maker = node.getTreeMaker();
+ List<JCExpression> clonedAndFixedTypeArgs = createTypeArgs(count, addExtends, node, typeArgs, source);
+
+ return maker.TypeApply(type, clonedAndFixedTypeArgs);
+ }
+
+ protected List<JCExpression> createTypeArgs(int count, boolean addExtends, JavacNode node, List<JCExpression> typeArgs, JCTree source) {
+ JavacTreeMaker maker = node.getTreeMaker();
+ Context context = node.getContext();
+
+ if (count < 0) throw new IllegalArgumentException("count is negative");
+ if (count == 0) return List.nil();
+ ListBuffer<JCExpression> arguments = new ListBuffer<JCExpression>();
+
+ if (typeArgs != null) for (JCExpression orig : typeArgs) {
+ if (!addExtends) {
+ if (orig.getKind() == Kind.UNBOUNDED_WILDCARD || orig.getKind() == Kind.SUPER_WILDCARD) {
+ arguments.append(genJavaLangTypeRef(node, "Object"));
+ } else if (orig.getKind() == Kind.EXTENDS_WILDCARD) {
+ JCExpression inner;
+ try {
+ inner = (JCExpression) ((JCWildcard) orig).inner;
+ } catch (Exception e) {
+ inner = genJavaLangTypeRef(node, "Object");
+ }
+ arguments.append(cloneType(maker, inner, source, context));
+ } else {
+ arguments.append(cloneType(maker, orig, source, context));
+ }
+ } else {
+ if (orig.getKind() == Kind.UNBOUNDED_WILDCARD || orig.getKind() == Kind.SUPER_WILDCARD) {
+ arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
+ } else if (orig.getKind() == Kind.EXTENDS_WILDCARD) {
+ arguments.append(cloneType(maker, orig, source, context));
+ } else {
+ arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.EXTENDS), cloneType(maker, orig, source, context)));
+ }
+ }
+ if (--count == 0) break;
+ }
+
+ while (count-- > 0) {
+ if (addExtends) {
+ arguments.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
+ } else {
+ arguments.append(genJavaLangTypeRef(node, "Object"));
+ }
+ }
+
+ return arguments.toList();
+ }
+
+ /** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */
+ protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard, boolean parens) {
+ Name thisName = builderType.toName("this");
+ JCExpression fn = maker.Select(maker.Select(maker.Ident(thisName), name), builderType.toName("size"));
+ JCExpression sizeInvoke = maker.Apply(List.<JCExpression>nil(), fn, List.<JCExpression>nil());
+ if (nullGuard) {
+ JCExpression isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(thisName), name), maker.Literal(CTC_BOT, 0));
+ JCExpression out = maker.Conditional(isNull, maker.Literal(CTC_INT, 0), sizeInvoke);
+ if (parens) return maker.Parens(out);
+ return out;
+ }
+ return sizeInvoke;
+ }
+
+ protected JCExpression cloneParamType(int index, JavacTreeMaker maker, List<JCExpression> typeArgs, JavacNode builderType, JCTree source) {
+ if (typeArgs == null || typeArgs.size() <= index) {
+ return genJavaLangTypeRef(builderType, "Object");
+ } else {
+ JCExpression originalType = typeArgs.get(index);
+ if (originalType.getKind() == Kind.UNBOUNDED_WILDCARD || originalType.getKind() == Kind.SUPER_WILDCARD) {
+ return genJavaLangTypeRef(builderType, "Object");
+ } else if (originalType.getKind() == Kind.EXTENDS_WILDCARD) {
+ try {
+ return cloneType(maker, (JCExpression) ((JCWildcard) originalType).inner, source, builderType.getContext());
+ } catch (Exception e) {
+ return genJavaLangTypeRef(builderType, "Object");
+ }
+ } else {
+ return cloneType(maker, originalType, source, builderType.getContext());
+ }
+ }
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java
new file mode 100644
index 00000000..e0621cf7
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaMapSingularizer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacGuavaMapSingularizer extends JavacGuavaSingularizer {
+ // TODO cgcc.ImmutableMultimap, cgcc.ImmutableListMultimap, cgcc.ImmutableSetMultimap
+ // TODO cgcc.ImmutableClassToInstanceMap
+ // TODO cgcc.ImmutableRangeMap
+
+ private static final LombokImmutableList<String> SUFFIXES =
+ LombokImmutableList.of("key", "value");
+ private static final LombokImmutableList<String> SUPPORTED_TYPES = LombokImmutableList.of(
+ "com.google.common.collect.ImmutableMap",
+ "com.google.common.collect.ImmutableBiMap",
+ "com.google.common.collect.ImmutableSortedMap"
+ );
+
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return SUPPORTED_TYPES;
+ }
+
+ @Override protected LombokImmutableList<String> getArgumentSuffixes() {
+ return SUFFIXES;
+ }
+
+ @Override protected String getAddMethodName() {
+ return "put";
+ }
+
+ @Override protected String getAddAllTypeName() {
+ return "java.util.Map";
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java
new file mode 100644
index 00000000..5c7fcab5
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSetListSingularizer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacGuavaSetListSingularizer extends JavacGuavaSingularizer {
+ // TODO com.google.common.collect.ImmutableRangeSet
+ // TODO com.google.common.collect.ImmutableMultiset and com.google.common.collect.ImmutableSortedMultiset
+ private static final LombokImmutableList<String> SUFFIXES = LombokImmutableList.of("");
+ private static final LombokImmutableList<String> SUPPORTED_TYPES = LombokImmutableList.of(
+ "com.google.common.collect.ImmutableCollection",
+ "com.google.common.collect.ImmutableList",
+ "com.google.common.collect.ImmutableSet",
+ "com.google.common.collect.ImmutableSortedSet"
+ );
+
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return SUPPORTED_TYPES;
+ }
+
+ @Override protected LombokImmutableList<String> getArgumentSuffixes() {
+ return SUFFIXES;
+ }
+
+ @Override protected String getAddMethodName() {
+ return "add";
+ }
+
+ @Override protected String getAddAllTypeName() {
+ return "java.lang.Iterable";
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
new file mode 100644
index 00000000..0ab7da54
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Collections;
+
+import lombok.core.GuavaTypeMap;
+import lombok.core.LombokImmutableList;
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+abstract class JavacGuavaSingularizer extends JavacSingularizer {
+ protected String getSimpleTargetTypeName(SingularData data) {
+ return GuavaTypeMap.getGuavaTypeName(data.getTargetFqn());
+ }
+
+ protected String getBuilderMethodName(SingularData data) {
+ String simpleTypeName = getSimpleTargetTypeName(data);
+ if ("ImmutableSortedSet".equals(simpleTypeName) || "ImmutableSortedMap".equals(simpleTypeName)) return "naturalOrder";
+ return "builder";
+ }
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ String simpleTypeName = getSimpleTargetTypeName(data);
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "com", "google", "common", "collect", simpleTypeName, "Builder");
+ type = addTypeArgs(getTypeArgumentsCount(), false, builderType, type, data.getTypeArgs(), source);
+
+ JCVariableDecl buildField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), data.getPluralName(), type, null);
+ return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ Symtab symbolTable = builderType.getSymbolTable();
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source);
+ }
+
+ private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) {
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ List<JCVariableDecl> params = List.nil();
+
+ JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
+ JCStatement clearField = maker.Exec(maker.Assign(thisDotField, maker.Literal(CTC_BOT, null)));
+ List<JCStatement> statements = returnStatement != null ? List.of(clearField, returnStatement) : List.of(clearField);
+
+ JCBlock body = maker.Block(0, statements);
+ Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString()));
+ JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+
+ LombokImmutableList<String> suffixes = getArgumentSuffixes();
+ Name[] names = new Name[suffixes.size()];
+ for (int i = 0; i < suffixes.size(); i++) {
+ String s = suffixes.get(i);
+ Name n = data.getSingularName();
+ names[i] = s.isEmpty() ? n : builderType.toName(s);
+ }
+
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName());
+ ListBuffer<JCExpression> invokeAddExprBuilder = new ListBuffer<JCExpression>();
+ for (int i = 0; i < suffixes.size(); i++) {
+ invokeAddExprBuilder.append(maker.Ident(names[i]));
+ }
+ List<JCExpression> invokeAddExpr = invokeAddExprBuilder.toList();
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, invokeAddExpr);
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name methodName = data.getSingularName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName(), methodName.toString()));
+ ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
+ for (int i = 0; i < suffixes.size(); i++) {
+ JCExpression pt = cloneParamType(i, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl p = maker.VarDef(maker.Modifiers(paramFlags), names[i], pt, null);
+ params.append(p);
+ }
+
+ JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params.toList(), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ protected void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source));
+ JCExpression thisDotFieldDotAddAll = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName() + "All");
+ JCExpression invokeAddAll = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAddAll, List.<JCExpression>of(maker.Ident(data.getPluralName())));
+ statements.append(maker.Exec(invokeAddAll));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name methodName = data.getPluralName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) methodName = builderType.toName(HandlerUtil.buildAccessorName(getAddMethodName() + "All", methodName.toString()));
+ JCExpression paramType;
+ String aaTypeName = getAddAllTypeName();
+ if (aaTypeName.startsWith("java.lang.") && aaTypeName.indexOf('.', 11) == -1) {
+ paramType = genJavaLangTypeRef(builderType, aaTypeName.substring(10));
+ } else {
+ paramType = chainDotsString(builderType, aaTypeName);
+ }
+ paramType = addTypeArgs(getTypeArgumentsCount(), true, builderType, paramType, data.getTypeArgs(), source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, List.of(param), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ List<JCExpression> jceBlank = List.nil();
+
+ JCExpression varType = chainDotsString(builderType, data.getTargetFqn());
+ int agrumentsCount = getTypeArgumentsCount();
+ varType = addTypeArgs(agrumentsCount, false, builderType, varType, data.getTypeArgs(), source);
+
+ JCExpression empty; {
+ //ImmutableX.of()
+ JCExpression emptyMethod = chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), "of");
+ List<JCExpression> invokeTypeArgs = createTypeArgs(agrumentsCount, false, builderType, data.getTypeArgs(), source);
+ empty = maker.Apply(invokeTypeArgs, emptyMethod, jceBlank);
+ }
+
+ JCExpression invokeBuild; {
+ //this.pluralName.build();
+ invokeBuild = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "build"), jceBlank);
+ }
+
+ JCExpression isNull; {
+ //this.pluralName == null
+ isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()), maker.Literal(CTC_BOT, null));
+ }
+
+ JCExpression init = maker.Conditional(isNull, empty, invokeBuild); // this.pluralName == null ? ImmutableX.of() : this.pluralName.build()
+
+ JCStatement jcs = maker.VarDef(maker.Modifiers(0), data.getPluralName(), varType, init);
+ statements.append(jcs);
+ }
+
+ protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+
+ JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
+ JCExpression thisDotField2 = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
+ JCExpression cond = maker.Binary(CTC_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+
+ JCExpression create = maker.Apply(jceBlank, chainDots(builderType, "com", "google", "common", "collect", getSimpleTargetTypeName(data), getBuilderMethodName(data)), jceBlank);
+ JCStatement thenPart = maker.Exec(maker.Assign(thisDotField2, create));
+
+ return maker.If(cond, thenPart, null);
+ }
+
+ protected abstract LombokImmutableList<String> getArgumentSuffixes();
+ protected abstract String getAddMethodName();
+ protected abstract String getAddAllTypeName();
+
+ protected int getTypeArgumentsCount() {
+ return getArgumentSuffixes().size();
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java
new file mode 100644
index 00000000..080266b8
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaTableSingularizer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacGuavaTableSingularizer extends JavacGuavaSingularizer {
+ private static final LombokImmutableList<String> SUFFIXES =
+ LombokImmutableList.of("rowKey", "columnKey", "value");
+ private static final LombokImmutableList<String> SUPPORTED_TYPES =
+ LombokImmutableList.of("com.google.common.collect.ImmutableTable");
+
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return SUPPORTED_TYPES;
+ }
+
+ @Override protected LombokImmutableList<String> getArgumentSuffixes() {
+ return SUFFIXES;
+ }
+
+ @Override protected String getAddMethodName() {
+ return "put";
+ }
+
+ @Override protected String getAddAllTypeName() {
+ return "com.google.common.collect.Table";
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
new file mode 100644
index 00000000..196ce45d
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Collections;
+
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularizer {
+ @Override public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ return super.listFieldsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.listMethodsToBeGenerated(data, builderType);
+ }
+
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ if (useGuavaInstead(builderType)) {
+ return guavaListSetSingularizer.generateFields(data, builderType, source);
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs(), source);
+
+ JCVariableDecl buildField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), data.getPluralName(), type, null);
+ return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
+ }
+
+ @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ Symtab symbolTable = builderType.getSymbolTable();
+ Name thisName = builderType.toName("this");
+
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
+ generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
+ generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
+ generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source);
+ }
+
+ private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) {
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ List<JCVariableDecl> params = List.nil();
+ List<JCExpression> jceBlank = List.nil();
+
+ JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName());
+ JCExpression thisDotFieldDotClear = maker.Select(maker.Select(maker.Ident(builderType.toName("this")), data.getPluralName()), builderType.toName("clear"));
+ JCStatement clearCall = maker.Exec(maker.Apply(jceBlank, thisDotFieldDotClear, jceBlank));
+ JCExpression cond = maker.Binary(CTC_NOT_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+ JCStatement ifSetCallClear = maker.If(cond, clearCall, null);
+ List<JCStatement> statements = returnStatement != null ? List.of(ifSetCallClear, returnStatement) : List.of(ifSetCallClear);
+
+ JCBlock body = maker.Block(0, statements);
+ Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString()));
+ JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getSingularName())));
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getSingularName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("add", name.toString()));
+ JCExpression paramType = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getSingularName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source));
+ JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "addAll");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotFieldDotAdd, List.<JCExpression>of(maker.Ident(data.getPluralName())));
+ statements.append(maker.Exec(invokeAdd));
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getPluralName();
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("addAll", name.toString()));
+ JCExpression paramType = chainDots(builderType, "java", "util", "Collection");
+ paramType = addTypeArgs(1, true, builderType, paramType, data.getTypeArgs(), source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
new file mode 100644
index 00000000..3002a98f
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.List", "java.util.Collection", "java.lang.Iterable");
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ List<JCExpression> jceBlank = List.nil();
+ ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
+
+ /* case 0: (empty); break; */ {
+ JCStatement assignStat; {
+ // pluralName = java.util.Collections.emptyList();
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "emptyList"), jceBlank);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase emptyCase = maker.Case(maker.Literal(CTC_INT, 0), List.of(assignStat, breakStat));
+ cases.append(emptyCase);
+ }
+
+ /* case 1: (singletonList); break; */ {
+ JCStatement assignStat; {
+ // pluralName = java.util.Collections.singletonList(this.pluralName.get(0));
+ JCExpression zeroLiteral = maker.Literal(CTC_INT, 0);
+ JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "get"), List.of(zeroLiteral));
+ List<JCExpression> args = List.of(arg);
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "singletonList"), args);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase singletonCase = maker.Case(maker.Literal(CTC_INT, 1), List.of(assignStat, breakStat));
+ cases.append(singletonCase);
+ }
+
+ /* default: Create with right size, then addAll */ {
+ List<JCStatement> defStats = createListCopy(maker, data, builderType, source);
+ JCCase defaultCase = maker.Case(null, defStats);
+ cases.append(defaultCase);
+ }
+
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false), cases.toList());
+ JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
+ localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source);
+ JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
+ statements.append(varDefStat);
+ statements.append(switchStat);
+ }
+
+ private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ Name thisName = builderType.toName("this");
+
+ JCExpression argToUnmodifiable; {
+ // new java.util.ArrayList<Generics>(this.pluralName);
+ List<JCExpression> constructorArgs = List.nil();
+ JCExpression thisDotPluralName = maker.Select(maker.Ident(thisName), data.getPluralName());
+ constructorArgs = List.<JCExpression>of(thisDotPluralName);
+ JCExpression targetTypeExpr = chainDots(builderType, "java", "util", "ArrayList");
+ targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs(), source);
+ argToUnmodifiable = maker.NewClass(null, jceBlank, targetTypeExpr, constructorArgs, null);
+ }
+
+ JCStatement unmodifiableStat; {
+ // pluralname = Collections.unmodifiableInterfaceType(-newlist-);
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "unmodifiableList"), List.of(argToUnmodifiable));
+ unmodifiableStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+
+ return List.of(unmodifiableStat);
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
new file mode 100644
index 00000000..fd699275
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2015-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.Arrays;
+
+import lombok.core.LombokImmutableList;
+import lombok.core.handlers.HandlerUtil;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Map", "java.util.SortedMap", "java.util.NavigableMap");
+ }
+
+ @Override public java.util.List<Name> listFieldsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listFieldsToBeGenerated(data, builderType);
+ }
+
+ String p = data.getPluralName().toString();
+ return Arrays.asList(builderType.toName(p + "$key"), builderType.toName(p + "$value"));
+ }
+
+ @Override public java.util.List<Name> listMethodsToBeGenerated(SingularData data, JavacNode builderType) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.listMethodsToBeGenerated(data, builderType);
+ }
+
+ return super.listMethodsToBeGenerated(data, builderType);
+ }
+
+ @Override public java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source) {
+ if (useGuavaInstead(builderType)) {
+ return guavaMapSingularizer.generateFields(data, builderType, source);
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ JCVariableDecl buildKeyField; {
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ type = addTypeArgs(1, false, builderType, type, data.getTypeArgs(), source);
+ buildKeyField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName(data.getPluralName() + "$key"), type, null);
+ }
+
+ JCVariableDecl buildValueField; {
+ JCExpression type = JavacHandlerUtil.chainDots(builderType, "java", "util", "ArrayList");
+ List<JCExpression> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.size() > 1) tArgs = tArgs.tail;
+ else tArgs = List.nil();
+ type = addTypeArgs(1, false, builderType, type, tArgs, source);
+ buildValueField = maker.VarDef(maker.Modifiers(Flags.PRIVATE), builderType.toName(data.getPluralName() + "$value"), type, null);
+ }
+
+ JavacNode valueFieldNode = injectFieldAndMarkGenerated(builderType, buildValueField);
+ JavacNode keyFieldNode = injectFieldAndMarkGenerated(builderType, buildKeyField);
+
+ return Arrays.asList(keyFieldNode, valueFieldNode);
+ }
+
+ @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+ Symtab symbolTable = builderType.getSymbolTable();
+
+ JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent);
+
+ returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID));
+ returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source);
+ }
+
+ private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) {
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ List<JCVariableDecl> params = List.nil();
+ List<JCExpression> jceBlank = List.nil();
+
+ JCExpression thisDotKeyField = chainDots(builderType, "this", data.getPluralName() + "$key");
+ JCExpression thisDotKeyFieldDotClear = chainDots(builderType, "this", data.getPluralName() + "$key", "clear");
+ JCExpression thisDotValueFieldDotClear = chainDots(builderType, "this", data.getPluralName() + "$value", "clear");
+ JCStatement clearKeyCall = maker.Exec(maker.Apply(jceBlank, thisDotKeyFieldDotClear, jceBlank));
+ JCStatement clearValueCall = maker.Exec(maker.Apply(jceBlank, thisDotValueFieldDotClear, jceBlank));
+ JCExpression cond = maker.Binary(CTC_NOT_EQUAL, thisDotKeyField, maker.Literal(CTC_BOT, null));
+ JCBlock clearCalls = maker.Block(0, List.of(clearKeyCall, clearValueCall));
+ JCStatement ifSetCallClear = maker.If(cond, clearCalls, null);
+ List<JCStatement> statements = returnStatement != null ? List.of(ifSetCallClear, returnStatement) : List.of(ifSetCallClear);
+
+ JCBlock body = maker.Block(0, statements);
+ Name methodName = builderType.toName(HandlerUtil.buildAccessorName("clear", data.getPluralName().toString()));
+ JCMethodDecl method = maker.MethodDef(mods, methodName, returnType, typeParams, params, thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ private void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrown = List.nil();
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source));
+ Name keyName = builderType.toName(data.getSingularName().toString() + "Key");
+ Name valueName = builderType.toName(data.getSingularName().toString() + "Value");
+ /* this.pluralname$key.add(singularnameKey); */ {
+ JCExpression thisDotKeyFieldDotAdd = chainDots(builderType, "this", data.getPluralName() + "$key", "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotKeyFieldDotAdd, List.<JCExpression>of(maker.Ident(keyName)));
+ statements.append(maker.Exec(invokeAdd));
+ }
+ /* this.pluralname$value.add(singularnameValue); */ {
+ JCExpression thisDotValueFieldDotAdd = chainDots(builderType, "this", data.getPluralName() + "$value", "add");
+ JCExpression invokeAdd = maker.Apply(List.<JCExpression>nil(), thisDotValueFieldDotAdd, List.<JCExpression>of(maker.Ident(valueName)));
+ statements.append(maker.Exec(invokeAdd));
+ }
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+
+ Name name = data.getSingularName();
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("put", name.toString()));
+ JCExpression paramTypeKey = cloneParamType(0, maker, data.getTypeArgs(), builderType, source);
+ JCExpression paramTypeValue = cloneParamType(1, maker, data.getTypeArgs(), builderType, source);
+ JCVariableDecl paramKey = maker.VarDef(maker.Modifiers(paramFlags), keyName, paramTypeKey, null);
+ JCVariableDecl paramValue = maker.VarDef(maker.Modifiers(paramFlags), valueName, paramTypeValue, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(paramKey, paramValue), thrown, body, null);
+ injectMethod(builderType, method);
+ }
+
+ private void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> jceBlank = List.nil();
+ JCModifiers mods = makeMods(maker, builderType, deprecate);
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+ statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source));
+ long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext());
+ long baseFlags = JavacHandlerUtil.addFinalIfNeeded(0, builderType.getContext());
+ Name entryName = builderType.toName("$lombokEntry");
+
+ JCExpression forEachType = chainDots(builderType, "java", "util", "Map", "Entry");
+ forEachType = addTypeArgs(2, true, builderType, forEachType, data.getTypeArgs(), source);
+ JCExpression keyArg = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(entryName), builderType.toName("getKey")), List.<JCExpression>nil());
+ JCExpression valueArg = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(entryName), builderType.toName("getValue")), List.<JCExpression>nil());
+ JCExpression addKey = maker.Apply(List.<JCExpression>nil(), chainDots(builderType, "this", data.getPluralName() + "$key", "add"), List.of(keyArg));
+ JCExpression addValue = maker.Apply(List.<JCExpression>nil(), chainDots(builderType, "this", data.getPluralName() + "$value", "add"), List.of(valueArg));
+ JCBlock forEachBody = maker.Block(0, List.<JCStatement>of(maker.Exec(addKey), maker.Exec(addValue)));
+ JCExpression entrySetInvocation = maker.Apply(jceBlank, maker.Select(maker.Ident(data.getPluralName()), builderType.toName("entrySet")), jceBlank);
+ JCStatement forEach = maker.ForeachLoop(maker.VarDef(maker.Modifiers(baseFlags), entryName, forEachType, null), entrySetInvocation, forEachBody);
+ statements.append(forEach);
+
+ if (returnStatement != null) statements.append(returnStatement);
+ JCBlock body = maker.Block(0, statements.toList());
+ Name name = data.getPluralName();
+ if (!fluent) name = builderType.toName(HandlerUtil.buildAccessorName("putAll", name.toString()));
+ JCExpression paramType = chainDots(builderType, "java", "util", "Map");
+ paramType = addTypeArgs(2, true, builderType, paramType, data.getTypeArgs(), source);
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(paramFlags), data.getPluralName(), paramType, null);
+ JCMethodDecl method = maker.MethodDef(mods, name, returnType, typeParams, List.of(param), jceBlank, body, null);
+ injectMethod(builderType, method);
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ if (data.getTargetFqn().equals("java.util.Map")) {
+ statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source));
+ } else {
+ statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source));
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
new file mode 100644
index 00000000..317233cb
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.core.LombokImmutableList;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+@ProviderFor(JavacSingularizer.class)
+public class JavacJavaUtilSetSingularizer extends JavacJavaUtilListSetSingularizer {
+ @Override public LombokImmutableList<String> getSupportedTypes() {
+ return LombokImmutableList.of("java.util.Set", "java.util.SortedSet", "java.util.NavigableSet");
+ }
+
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ if (useGuavaInstead(builderType)) {
+ guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ return;
+ }
+
+ JavacTreeMaker maker = builderType.getTreeMaker();
+
+ if (data.getTargetFqn().equals("java.util.Set")) {
+ statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source));
+ } else {
+ statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source));
+ }
+ }
+}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
new file mode 100644
index 00000000..0589ac34
--- /dev/null
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers.singulars;
+
+import static lombok.javac.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+import lombok.ConfigurationKeys;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
+ protected final JavacSingularizer guavaListSetSingularizer = new JavacGuavaSetListSingularizer();
+ protected final JavacSingularizer guavaMapSingularizer = new JavacGuavaMapSingularizer();
+
+ protected boolean useGuavaInstead(JavacNode node) {
+ return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA));
+ }
+
+ protected List<JCStatement> createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
+
+ if (emptyCollectionMethod != null) { // case 0: (empty); break;
+ JCStatement assignStat; {
+ // pluralName = java.util.Collections.emptyCollectionMethod();
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", emptyCollectionMethod), jceBlank);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase emptyCase = maker.Case(maker.Literal(CTC_INT, 0), List.of(assignStat, breakStat));
+ cases.append(emptyCase);
+ }
+
+ if (singletonCollectionMethod != null) { // case 1: (singleton); break;
+ JCStatement assignStat; {
+ // !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0));
+ // mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0));
+ JCExpression zeroLiteral = maker.Literal(CTC_INT, 0);
+ JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral));
+ List<JCExpression> args;
+ if (mapMode) {
+ JCExpression zeroLiteralClone = maker.Literal(CTC_INT, 0);
+ JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$value" : ""), "get"), List.of(zeroLiteralClone));
+ args = List.of(arg, arg2);
+ } else {
+ args = List.of(arg);
+ }
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", singletonCollectionMethod), args);
+ assignStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+ JCStatement breakStat = maker.Break(null);
+ JCCase singletonCase = maker.Case(maker.Literal(CTC_INT, 1), List.of(assignStat, breakStat));
+ cases.append(singletonCase);
+ }
+
+ { // default:
+ List<JCStatement> statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source);
+ JCCase defaultCase = maker.Case(null, statements);
+ cases.append(defaultCase);
+ }
+
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false), cases.toList());
+ JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source);
+ JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
+ return List.of(varDefStat, switchStat);
+ }
+
+ protected JCStatement createConstructBuilderVarIfNeeded(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+
+ Name v1Name = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName();
+ Name v2Name = mapMode ? builderType.toName(data.getPluralName() + "$value") : null;
+ JCExpression thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v1Name);
+ JCExpression cond = maker.Binary(CTC_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+ thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v1Name);
+ JCExpression v1Type = chainDots(builderType, "java", "util", "ArrayList");
+ v1Type = addTypeArgs(1, false, builderType, v1Type, data.getTypeArgs(), source);
+ JCExpression constructArrayList = maker.NewClass(null, jceBlank, v1Type, jceBlank, null);
+ JCStatement initV1 = maker.Exec(maker.Assign(thisDotField, constructArrayList));
+ JCStatement thenPart;
+ if (mapMode) {
+ thisDotField = maker.Select(maker.Ident(builderType.toName("this")), v2Name);
+ JCExpression v2Type = chainDots(builderType, "java", "util", "ArrayList");
+ List<JCExpression> tArgs = data.getTypeArgs();
+ if (tArgs != null && tArgs.tail != null) tArgs = tArgs.tail;
+ else tArgs = List.nil();
+ v2Type = addTypeArgs(1, false, builderType, v2Type, tArgs, source);
+ constructArrayList = maker.NewClass(null, jceBlank, v2Type, jceBlank, null);
+ JCStatement initV2 = maker.Exec(maker.Assign(thisDotField, constructArrayList));
+ thenPart = maker.Block(0, List.of(initV1, initV2));
+ } else {
+ thenPart = initV1;
+ }
+ return maker.If(cond, thenPart, null);
+ }
+
+ protected List<JCStatement> createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source) {
+ List<JCExpression> jceBlank = List.nil();
+ Name thisName = builderType.toName("this");
+
+ JCStatement createStat; {
+ // pluralName = new java.util.TargetType(initialCap);
+ List<JCExpression> constructorArgs = List.nil();
+ if (addInitialCapacityArg) {
+ Name varName = mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName();
+ // this.varName.size() < MAX_POWER_OF_2 ? 1 + this.varName.size() + (this.varName.size() - 3) / 3 : Integer.MAX_VALUE;
+ // lessThanCutOff = this.varName.size() < MAX_POWER_OF_2
+ JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 0x40000000));
+ JCExpression integerMaxValue = genJavaLangTypeRef(builderType, "Integer", "MAX_VALUE");
+ JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard, true));
+ JCExpression sizeFormulaRightLeft = maker.Parens(maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 3)));
+ JCExpression sizeFormulaRight = maker.Binary(CTC_DIV, sizeFormulaRightLeft, maker.Literal(CTC_INT, 3));
+ JCExpression sizeFormula = maker.Binary(CTC_PLUS, sizeFormulaLeft, sizeFormulaRight);
+ constructorArgs = List.<JCExpression>of(maker.Conditional(lessThanCutoff, sizeFormula, integerMaxValue));
+ }
+
+ JCExpression targetTypeExpr = chainDots(builderType, "java", "util", targetType);
+ targetTypeExpr = addTypeArgs(mapMode ? 2 : 1, false, builderType, targetTypeExpr, data.getTypeArgs(), source);
+ JCExpression constructorCall = maker.NewClass(null, jceBlank, targetTypeExpr, constructorArgs, null);
+ if (defineVar) {
+ JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
+ localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source);
+ createStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, constructorCall);
+ } else {
+ createStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), constructorCall));
+ }
+ }
+
+ JCStatement fillStat; {
+ if (mapMode) {
+ // for (int $i = 0; $i < this.pluralname$key.size(); i++) pluralname.put(this.pluralname$key.get($i), this.pluralname$value.get($i));
+ Name ivar = builderType.toName("$i");
+ Name keyVarName = builderType.toName(data.getPluralName() + "$key");
+ JCExpression pluralnameDotPut = maker.Select(maker.Ident(data.getPluralName()), builderType.toName("put"));
+ JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$key", "get"), List.<JCExpression>of(maker.Ident(ivar)));
+ JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar)));
+ // [jdk9] We add an unneccessary (V) cast here. Not doing so gives an error in javac (build 9-ea+156-jigsaw-nightly-h6072-20170212):
+ // error: method put in interface Map<K#2,V#2> cannot be applied to given types;
+ arg2 = maker.TypeCast(createTypeArgs(2, false, builderType, data.getTypeArgs(), source).get(1), arg2);
+ JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2)));
+ JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0));
+ JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true));
+ JCExpression incrementExpr = maker.Unary(CTC_POSTINC, maker.Ident(ivar));
+ fillStat = maker.ForLoop(List.of(forInit), checkExpr, List.of(maker.Exec(incrementExpr)), putStatement);
+ } else {
+ // pluralname.addAll(this.pluralname);
+ JCExpression thisDotPluralName = maker.Select(maker.Ident(thisName), data.getPluralName());
+ fillStat = maker.Exec(maker.Apply(jceBlank, maker.Select(maker.Ident(data.getPluralName()), builderType.toName("addAll")), List.of(thisDotPluralName)));
+ }
+ if (nullGuard) {
+ JCExpression thisDotField = maker.Select(maker.Ident(thisName), mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName());
+ JCExpression nullCheck = maker.Binary(CTC_NOT_EQUAL, thisDotField, maker.Literal(CTC_BOT, null));
+ fillStat = maker.If(nullCheck, fillStat, null);
+ }
+ }
+ JCStatement unmodifiableStat; {
+ // pluralname = Collections.unmodifiableInterfaceType(pluralname);
+ JCExpression arg = maker.Ident(data.getPluralName());
+ JCExpression invoke = maker.Apply(jceBlank, chainDots(builderType, "java", "util", "Collections", "unmodifiable" + data.getTargetSimpleType()), List.of(arg));
+ unmodifiableStat = maker.Exec(maker.Assign(maker.Ident(data.getPluralName()), invoke));
+ }
+
+ return List.of(createStat, fillStat, unmodifiableStat);
+ }
+}
diff --git a/src/core/lombok/package-info.java b/src/core/lombok/package-info.java
index b5406a74..1c01dd0d 100644
--- a/src/core/lombok/package-info.java
+++ b/src/core/lombok/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -29,6 +29,6 @@
* <li>{@code lombok.experimental} – This package contains lombok features that are new or likely to change before committing to long-term support.
* </ul>
*
- * @see <a href="http://projectlombok.org/features/index.html">Lombok features</a>
+ * @see <a href="https://projectlombok.org/features/all">Lombok features</a>
*/
package lombok;
diff --git a/src/core/lombok/val.java b/src/core/lombok/val.java
index cd8652d6..a69d514c 100644
--- a/src/core/lombok/val.java
+++ b/src/core/lombok/val.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2017 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,7 +28,7 @@ package lombok;
* <p>
* Note that this is an annotation type because {@code val x = 10;} will be desugared to {@code @val final int x = 10;}
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/val.html">the project lombok features page for &#64;val</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/val">the project lombok features page for &#64;val</a>.
*/
public @interface val {
}
diff --git a/src/core/lombok/var.java b/src/core/lombok/var.java
new file mode 100644
index 00000000..63a70213
--- /dev/null
+++ b/src/core/lombok/var.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010-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
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok;
+
+/**
+ * Use {@code var} as the type of any local variable declaration (even in a {@code for} statement), and the type will be inferred from the initializing expression
+ * (any further assignments to the variable are not involved in this type inference).
+ * <p>
+ * For example: {@code var x = 10.0;} will infer {@code double}, and {@code var y = new ArrayList<String>();} will infer {@code ArrayList<String>}.
+ * <p>
+ * Note that this is an annotation type because {@code var x = 10;} will be desugared to {@code @var int x = 10;}
+ * <p>
+ * Complete documentation is found at <a href="https://projectlombok.org/features/var">the project lombok features page for &#64;var</a>.
+ */
+public @interface var {
+}
diff --git a/src/core9/module-info.java b/src/core9/module-info.java
new file mode 100644
index 00000000..87f819e2
--- /dev/null
+++ b/src/core9/module-info.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+module lombok {
+ requires java.compiler;
+ requires java.instrument;
+ requires jdk.unsupported;
+
+ exports lombok;
+ exports lombok.experimental;
+ exports lombok.extern.apachecommons;
+ exports lombok.extern.java;
+ exports lombok.extern.jbosslog;
+ exports lombok.extern.log4j;
+ exports lombok.extern.slf4j;
+
+ provides javax.annotation.processing.Processor with lombok.launch.AnnotationProcessorHider.AnnotationProcessor;
+ provides org.mapstruct.ap.spi.AstModifyingAnnotationProcessor with lombok.launch.AnnotationProcessorHider.AstModificationNotifier;
+}
+
diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java
index f64e36a1..0d887cb9 100644
--- a/src/delombok/lombok/delombok/Delombok.java
+++ b/src/delombok/lombok/delombok/Delombok.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2010 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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,12 +30,14 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
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.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
@@ -43,15 +45,23 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import lombok.Lombok;
import lombok.javac.CommentCatcher;
+import lombok.javac.Javac;
import lombok.javac.LombokOptions;
+import lombok.javac.apt.LombokProcessor;
+import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.comp.Todo;
+import com.sun.tools.javac.file.BaseFileManager;
+import com.sun.tools.javac.main.Arguments;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.util.Context;
@@ -204,7 +214,7 @@ public class Delombok {
System.out.println(indentAndWordbreak(e.getValue(), 4, 70));
}
System.out.println("Example: -f indent:4 -f emptyLines:indent");
- System.out.println("The '-f pretty' option is shorthand for '-f suppressWarnings:skip -f danceAroundIdeChecks:skip -f generateDelombokComment:skip -f javaLangAsFQN:skip'");
+ System.out.println("The '-f pretty' option is shorthand for '-f suppressWarnings:skip -f generated:skip -f danceAroundIdeChecks:skip -f generateDelombokComment:skip -f javaLangAsFQN:skip'");
System.exit(0);
return;
}
@@ -301,6 +311,7 @@ public class Delombok {
if (prettyEnabled) {
if (!formatPrefs.containsKey("suppresswarnings")) formatPrefs.put("suppresswarnings", "skip");
+ if (!formatPrefs.containsKey("generated")) formatPrefs.put("generated", "skip");
if (!formatPrefs.containsKey("dancearoundidechecks")) formatPrefs.put("dancearoundidechecks", "skip");
if (!formatPrefs.containsKey("generatedelombokcomment")) formatPrefs.put("generatedelombokcomment", "skip");
if (!formatPrefs.containsKey("javalangasfqn")) formatPrefs.put("javalangasfqn", "skip");
@@ -465,26 +476,83 @@ public class Delombok {
return out;
}
+ private static final Field MODULE_FIELD = getModuleField();
+ private static Field getModuleField() {
+ try {
+ return JCCompilationUnit.class.getField("modle");
+ } catch (NoSuchFieldException e) {
+ return null;
+ } catch (SecurityException e) {
+ return null;
+ }
+ }
+
public boolean delombok() throws IOException {
LombokOptions options = LombokOptionsFactory.getDelombokOptions(context);
options.deleteLombokAnnotations();
options.putJavacOption("ENCODING", charset.name());
- if (classpath != null) options.putJavacOption("CLASSPATH", classpath);
+ if (classpath != null) options.putJavacOption("CLASSPATH", unpackClasspath(classpath));
if (sourcepath != null) options.putJavacOption("SOURCEPATH", sourcepath);
- if (bootclasspath != null) options.putJavacOption("BOOTCLASSPATH", bootclasspath);
+ if (bootclasspath != null) options.putJavacOption("BOOTCLASSPATH", unpackClasspath(bootclasspath));
options.setFormatPreferences(new FormatPreferences(formatPrefs));
options.put("compilePolicy", "check");
+ if (Javac.getJavaCompilerVersion() >= 9) {
+ Arguments args = Arguments.instance(context);
+ List<String> argsList = new ArrayList<String>();
+ if (classpath != null) {
+ argsList.add("--class-path");
+ argsList.add(options.get("--class-path"));
+ }
+ if (sourcepath != null) {
+ argsList.add("--source-path");
+ argsList.add(options.get("--source-path"));
+ }
+ if (bootclasspath != null) {
+ argsList.add("--boot-class-path");
+ argsList.add(options.get("--boot-class-path"));
+ }
+ if (charset != null) {
+ argsList.add("-encoding");
+ argsList.add(charset.name());
+ }
+ String[] argv = argsList.toArray(new String[0]);
+ args.init("javac", argv);
+ }
+
CommentCatcher catcher = CommentCatcher.create(context);
JavaCompiler compiler = catcher.getCompiler();
List<JCCompilationUnit> roots = new ArrayList<JCCompilationUnit>();
Map<JCCompilationUnit, File> baseMap = new IdentityHashMap<JCCompilationUnit, File>();
- compiler.initProcessAnnotations(Collections.singleton(new lombok.javac.apt.Processor()));
+ Set<LombokProcessor> processors = Collections.singleton(new lombok.javac.apt.LombokProcessor());
+
+ if (Javac.getJavaCompilerVersion() >= 9) {
+ JavaFileManager jfm_ = context.get(JavaFileManager.class);
+ if (jfm_ instanceof BaseFileManager) {
+ Arguments args = Arguments.instance(context);
+ ((BaseFileManager) jfm_).setContext(context); // reinit with options
+ ((BaseFileManager) jfm_).handleOptions(args.getDeferredFileManagerOptions());
+ }
+ }
+
+ if (Javac.getJavaCompilerVersion() < 9) {
+ compiler.initProcessAnnotations(processors);
+ } else {
+ compiler.initProcessAnnotations(processors, Collections.<JavaFileObject>emptySet(), Collections.<String>emptySet());
+ }
+
+ Object unnamedModule = null;
+ if (Javac.getJavaCompilerVersion() >= 9) unnamedModule = Symtab.instance(context).unnamedModule;
for (File fileToParse : filesToParse) {
- @SuppressWarnings("deprecation") JCCompilationUnit unit = compiler.parse(fileToParse.getAbsolutePath());
+ JCCompilationUnit unit = compiler.parse(fileToParse.getAbsolutePath());
+ if (Javac.getJavaCompilerVersion() >= 9) try {
+ MODULE_FIELD.set(unit, unnamedModule);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
baseMap.put(unit, fileToBase.get(fileToParse));
roots.add(unit);
}
@@ -497,9 +565,19 @@ public class Delombok {
catcher.setComments(unit, new DocCommentIntegrator().integrate(catcher.getComments(unit), unit));
}
+ if (Javac.getJavaCompilerVersion() >= 9) {
+ compiler.initModules(com.sun.tools.javac.util.List.from(roots.toArray(new JCCompilationUnit[0])));
+ }
com.sun.tools.javac.util.List<JCCompilationUnit> trees = compiler.enterTrees(toJavacList(roots));
- JavaCompiler delegate = compiler.processAnnotations(trees);
+ JavaCompiler delegate;
+ if (Javac.getJavaCompilerVersion() < 9) {
+ delegate = compiler.processAnnotations(trees, com.sun.tools.javac.util.List.<String>nil());
+ } else {
+ delegate = compiler;
+ Collection<String> c = com.sun.tools.javac.util.List.nil();
+ compiler.processAnnotations(trees, c);
+ }
Object care = callAttributeMethodOnJavaCompiler(delegate, delegate.todo);
@@ -507,9 +585,9 @@ public class Delombok {
FormatPreferences fps = new FormatPreferences(formatPrefs);
for (JCCompilationUnit unit : roots) {
DelombokResult result = new DelombokResult(catcher.getComments(unit), unit, force || options.isChanged(unit), fps);
- if (verbose) feedback.printf("File: %s [%s]\n", unit.sourcefile.getName(), result.isChanged() ? "delomboked" : "unchanged");
+ if (verbose) feedback.printf("File: %s [%s%s]\n", unit.sourcefile.getName(), result.isChanged() ? "delomboked" : "unchanged", force && !options.isChanged(unit) ? " (forced)" : "");
Writer rawWriter;
- if (presetWriter != null) rawWriter = presetWriter;
+ if (presetWriter != null) rawWriter = createUnicodeEscapeWriter(presetWriter);
else if (output == null) rawWriter = createStandardOutWriter();
else rawWriter = createFileWriter(output, baseMap.get(unit), unit.sourcefile.toUri());
BufferedWriter writer = new BufferedWriter(rawWriter);
@@ -528,6 +606,29 @@ public class Delombok {
return true;
}
+ private String unpackClasspath(String cp) {
+ String[] parts = cp.split(Pattern.quote(File.pathSeparator));
+ StringBuilder out = new StringBuilder();
+ for (String p : parts) {
+ if (!p.endsWith("*")) {
+ if (out.length() > 0) out.append(File.pathSeparator);
+ out.append(p);
+ continue;
+ }
+ File f = new File(p.substring(0, p.length() - 2));
+ File[] files = f.listFiles();
+ if (files == null) continue;
+ for (File file : files) {
+ if (file.isFile()) {
+ if (out.length() > 0) out.append(File.pathSeparator);
+ out.append(p, 0, p.length() - 1);
+ out.append(file.getName());
+ }
+ }
+ }
+ return out.toString();
+ }
+
private static Method attributeMethod;
/** Method is needed because the call signature has changed between javac6 and javac7; no matter what we compile against, using delombok in the other means VerifyErrors. */
private static Object callAttributeMethodOnJavaCompiler(JavaCompiler compiler, Todo arg) {
@@ -605,6 +706,10 @@ public class Delombok {
return createUnicodeEscapeWriter(System.out);
}
+ private Writer createUnicodeEscapeWriter(Writer writer) {
+ return new UnicodeEscapeWriter(writer, charset);
+ }
+
private Writer createUnicodeEscapeWriter(OutputStream out) {
return new UnicodeEscapeWriter(new OutputStreamWriter(out, charset), charset);
}
diff --git a/src/delombok/lombok/delombok/DelombokApp.java b/src/delombok/lombok/delombok/DelombokApp.java
index 276bd7de..aa753fc8 100644
--- a/src/delombok/lombok/delombok/DelombokApp.java
+++ b/src/delombok/lombok/delombok/DelombokApp.java
@@ -88,7 +88,7 @@ public class DelombokApp extends LombokApp {
// Since we only read from it, not closing it should not be a problem.
@SuppressWarnings({"resource", "all"}) final JarFile toolsJarFile = new JarFile(toolsJar);
- ClassLoader loader = new ClassLoader() {
+ ClassLoader loader = new ClassLoader(DelombokApp.class.getClassLoader()) {
private Class<?> loadStreamAsClass(String name, boolean resolve, InputStream in) throws ClassNotFoundException {
try {
try {
@@ -107,16 +107,24 @@ public class DelombokApp extends LombokApp {
} finally {
in.close();
}
- } catch (IOException e2) {
+ } catch (Exception e2) {
throw new ClassNotFoundException(name, e2);
}
}
@Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- String rawName = name.replace(".", "/") + ".class";
+ String rawName, altName; {
+ String binName = name.replace(".", "/");
+ rawName = binName + ".class";
+ altName = binName + ".SCL.lombok";
+ }
JarEntry entry = toolsJarFile.getJarEntry(rawName);
if (entry == null) {
- if (name.startsWith("lombok.")) return loadStreamAsClass(name, resolve, super.getResourceAsStream(rawName));
+ if (name.startsWith("lombok.")) {
+ InputStream res = getParent().getResourceAsStream(rawName);
+ if (res == null) res = getParent().getResourceAsStream(altName);
+ return loadStreamAsClass(name, resolve, res);
+ }
return super.loadClass(name, resolve);
}
diff --git a/src/delombok/lombok/delombok/DelombokResult.java b/src/delombok/lombok/delombok/DelombokResult.java
index 84aeb68b..8985b257 100644
--- a/src/delombok/lombok/delombok/DelombokResult.java
+++ b/src/delombok/lombok/delombok/DelombokResult.java
@@ -65,7 +65,8 @@ public class DelombokResult {
else comments_ = com.sun.tools.javac.util.List.from(comments.toArray(new CommentInfo[0]));
FormatPreferences preferences = new FormatPreferenceScanner().scan(formatPreferences, getContent());
- compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments_, preferences));
+ //compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments_, preferences));
+ compilationUnit.accept(new PrettyPrinter(out, compilationUnit, comments_, preferences));
}
private CharSequence getContent() throws IOException {
diff --git a/src/delombok/lombok/delombok/DocCommentIntegrator.java b/src/delombok/lombok/delombok/DocCommentIntegrator.java
index c66ff0ec..bab0abd8 100644
--- a/src/delombok/lombok/delombok/DocCommentIntegrator.java
+++ b/src/delombok/lombok/delombok/DocCommentIntegrator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2010 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
@@ -29,6 +29,7 @@ import java.util.regex.Pattern;
import lombok.javac.CommentInfo;
import lombok.javac.Javac;
+import lombok.javac.PackageName;
import lombok.javac.handlers.JavacHandlerUtil;
import com.sun.tools.javac.parser.Tokens.Comment;
@@ -73,7 +74,7 @@ public class DocCommentIntegrator {
return out;
}
- private static final Pattern CONTENT_STRIPPER = Pattern.compile("^(?:\\s*\\*)?[ \\t]*(.*?)$", Pattern.MULTILINE);
+ private static final Pattern CONTENT_STRIPPER = Pattern.compile("^(?:\\s*\\*)?(.*?)$", Pattern.MULTILINE);
@SuppressWarnings("unchecked") private boolean attach(JCCompilationUnit top, final JCTree node, CommentInfo cmt) {
String docCommentContent = cmt.content;
if (docCommentContent.startsWith("/**")) docCommentContent = docCommentContent.substring(3);
@@ -120,7 +121,8 @@ public class DocCommentIntegrator {
}
private JCTree findJavadocableNodeOnOrAfter(JCCompilationUnit unit, int endPos) {
- if (unit.pid != null && endPos <= unit.pid.pos) return null;
+ JCTree pid = PackageName.getPackageNode(unit);
+ if (pid != null && endPos <= pid.pos) return null;
Iterator<JCTree> it = unit.defs.iterator();
while (it.hasNext()) {
diff --git a/src/delombok/lombok/delombok/FormatPreferences.java b/src/delombok/lombok/delombok/FormatPreferences.java
index 9bd668a5..3861685d 100644
--- a/src/delombok/lombok/delombok/FormatPreferences.java
+++ b/src/delombok/lombok/delombok/FormatPreferences.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Project Lombok Authors.
+ * Copyright (C) 2013-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -33,6 +33,7 @@ public final class FormatPreferences {
private final boolean generateConstructorProperties;
private final boolean generateSuppressWarnings, danceAroundIdeChecks, generateDelombokComment, javaLangAsFqn;
final Map<String, String> rawMap;
+ private final boolean generateGenerated;
static final Map<String, String> KEYS;
@@ -42,7 +43,8 @@ public final class FormatPreferences {
keys.put("emptyLines", "Either 'indent' or 'blank'. indent means: Indent an empty line to the right level. Default: 'blank'");
keys.put("finalParams", "Either 'generate' or 'skip'. generate means: All lombok-generated methods set all parameters to final. Default: 'generate'");
keys.put("constructorProperties", "Either 'generate' or 'skip'. generate means: All lombok-generated constructors with 1 or more arguments get an @ConstructorProperties annotation. Default: 'generate'");
- keys.put("suppressWarnings", "Either 'generate' or 'skip'. generate means: All lombok-generated methods get a @SuppressWarnings annotation. Default: 'generate'");
+ keys.put("suppressWarnings", "Either 'generate' or 'skip'. generate means: All lombok-generated methods, types, and fields get a @SuppressWarnings annotation. Default: 'generate'");
+ keys.put("generated", "Either 'generate' or 'skip'. generate means: All lombok-generated methods, types, and fields get a @javax.annotation.Generated(\"lombok\") annotation. Default: 'generate'");
keys.put("danceAroundIdeChecks", "Either 'generate' or 'skip'. generate means: Lombok will intentionally obfuscate some generated code to avoid IDE warnings. Default: 'generate'");
keys.put("generateDelombokComment", "Either 'generate' or 'skip'. generate means: Any file modified by delombok will have a comment stating this at the top. Default: 'generate'");
keys.put("javaLangAsFQN", "Either 'generate' or 'skip'. generate means: Any generated reference to java.lang classes are prefixed with `java.lang.`. Default: 'generate'");
@@ -82,6 +84,7 @@ public final class FormatPreferences {
this.generateFinalParams = unrollBoolean(preferences, "finalParams", "generate", "skip", true);
this.generateConstructorProperties = unrollBoolean(preferences, "constructorProperties", "generate", "skip", true);
this.generateSuppressWarnings = unrollBoolean(preferences, "suppressWarnings", "generate", "skip", true);
+ this.generateGenerated = unrollBoolean(preferences, "generated", "generate", "skip", true);
this.danceAroundIdeChecks = unrollBoolean(preferences, "danceAroundIdeChecks", "generate", "skip", true);
this.generateDelombokComment = unrollBoolean(preferences, "generateDelombokComment", "generate", "skip", true);
this.javaLangAsFqn = unrollBoolean(preferences, "javaLangAsFQN", "generate", "skip", true);
@@ -112,6 +115,10 @@ public final class FormatPreferences {
return generateSuppressWarnings;
}
+ public boolean generateGenerated() {
+ return generateGenerated;
+ }
+
public boolean generateFinalParams() {
return generateFinalParams;
}
@@ -131,4 +138,8 @@ public final class FormatPreferences {
public boolean generateConstructorProperties() {
return generateConstructorProperties;
}
+
+ @Override public String toString() {
+ return rawMap.toString();
+ }
}
diff --git a/src/delombok/lombok/delombok/LombokOptionsFactory.java b/src/delombok/lombok/delombok/LombokOptionsFactory.java
index 47921931..62dc953a 100644
--- a/src/delombok/lombok/delombok/LombokOptionsFactory.java
+++ b/src/delombok/lombok/delombok/LombokOptionsFactory.java
@@ -24,6 +24,7 @@ package lombok.delombok;
import lombok.javac.Javac;
import lombok.javac.Javac6BasedLombokOptions;
import lombok.javac.Javac8BasedLombokOptions;
+import lombok.javac.Javac9BasedLombokOptions;
import lombok.javac.LombokOptions;
import com.sun.tools.javac.util.Context;
@@ -41,9 +42,15 @@ public class LombokOptionsFactory {
@Override LombokOptions createAndRegisterOptions(Context context) {
return Javac8BasedLombokOptions.replaceWithDelombokOptions(context);
}
+ },
+
+ JDK9 {
+ @Override LombokOptions createAndRegisterOptions(Context context) {
+ return Javac9BasedLombokOptions.replaceWithDelombokOptions(context);
+ }
};
- abstract LombokOptions createAndRegisterOptions(Context context);
+ abstract LombokOptions createAndRegisterOptions(Context context);
}
public static LombokOptions getDelombokOptions(Context context) {
@@ -53,8 +60,10 @@ public class LombokOptionsFactory {
LombokOptions options;
if (Javac.getJavaCompilerVersion() < 8) {
options = LombokOptionCompilerVersion.JDK7_AND_LOWER.createAndRegisterOptions(context);
- } else {
+ } else if (Javac.getJavaCompilerVersion() == 8) {
options = LombokOptionCompilerVersion.JDK8.createAndRegisterOptions(context);
+ } else {
+ options = LombokOptionCompilerVersion.JDK9.createAndRegisterOptions(context);
}
return options;
}
diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
deleted file mode 100644
index 9dd7e40e..00000000
--- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
+++ /dev/null
@@ -1,1680 +0,0 @@
-/*
- * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-/*
- * Code derived from com.sun.tools.javac.tree.Pretty, from the langtools project.
- * A version can be found at, for example, http://hg.openjdk.java.net/jdk7/build/langtools
- */
-package lombok.delombok;
-
-import static com.sun.tools.javac.code.Flags.*;
-import static lombok.javac.Javac.*;
-import static lombok.javac.JavacTreeMaker.TreeTag.treeTag;
-import static lombok.javac.JavacTreeMaker.TypeTag.typeTag;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Map;
-
-import lombok.javac.CommentInfo;
-import lombok.javac.CommentInfo.EndConnection;
-import lombok.javac.CommentInfo.StartConnection;
-import lombok.javac.JavacTreeMaker.TreeTag;
-import lombok.javac.JavacTreeMaker.TypeTag;
-
-import com.sun.source.tree.Tree;
-import com.sun.tools.javac.code.Flags;
-import com.sun.tools.javac.code.Symbol;
-import com.sun.tools.javac.tree.DocCommentTable;
-import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.JCAnnotation;
-import com.sun.tools.javac.tree.JCTree.JCArrayAccess;
-import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
-import com.sun.tools.javac.tree.JCTree.JCAssert;
-import com.sun.tools.javac.tree.JCTree.JCAssign;
-import com.sun.tools.javac.tree.JCTree.JCAssignOp;
-import com.sun.tools.javac.tree.JCTree.JCBinary;
-import com.sun.tools.javac.tree.JCTree.JCBlock;
-import com.sun.tools.javac.tree.JCTree.JCBreak;
-import com.sun.tools.javac.tree.JCTree.JCCase;
-import com.sun.tools.javac.tree.JCTree.JCCatch;
-import com.sun.tools.javac.tree.JCTree.JCClassDecl;
-import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
-import com.sun.tools.javac.tree.JCTree.JCConditional;
-import com.sun.tools.javac.tree.JCTree.JCContinue;
-import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
-import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
-import com.sun.tools.javac.tree.JCTree.JCErroneous;
-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.JCForLoop;
-import com.sun.tools.javac.tree.JCTree.JCIdent;
-import com.sun.tools.javac.tree.JCTree.JCIf;
-import com.sun.tools.javac.tree.JCTree.JCImport;
-import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
-import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
-import com.sun.tools.javac.tree.JCTree.JCLiteral;
-import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
-import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
-import com.sun.tools.javac.tree.JCTree.JCModifiers;
-import com.sun.tools.javac.tree.JCTree.JCNewArray;
-import com.sun.tools.javac.tree.JCTree.JCNewClass;
-import com.sun.tools.javac.tree.JCTree.JCParens;
-import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
-import com.sun.tools.javac.tree.JCTree.JCReturn;
-import com.sun.tools.javac.tree.JCTree.JCSkip;
-import com.sun.tools.javac.tree.JCTree.JCStatement;
-import com.sun.tools.javac.tree.JCTree.JCSwitch;
-import com.sun.tools.javac.tree.JCTree.JCSynchronized;
-import com.sun.tools.javac.tree.JCTree.JCThrow;
-import com.sun.tools.javac.tree.JCTree.JCTry;
-import com.sun.tools.javac.tree.JCTree.JCTypeApply;
-import com.sun.tools.javac.tree.JCTree.JCTypeCast;
-import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
-import com.sun.tools.javac.tree.JCTree.JCUnary;
-import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
-import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
-import com.sun.tools.javac.tree.JCTree.JCWildcard;
-import com.sun.tools.javac.tree.JCTree.LetExpr;
-import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
-import com.sun.tools.javac.tree.TreeInfo;
-import com.sun.tools.javac.tree.TreeScanner;
-import com.sun.tools.javac.util.Convert;
-import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.util.Name;
-import com.sun.tools.javac.util.Position;
-//import com.sun.tools.javac.code.TypeTags;
-
-/** Prints out a tree as an indented Java source program.
- *
- * <p><b>This is NOT part of any API supported by Sun Microsystems. If
- * you write code that depends on this, you do so at your own risk.
- * This code and its internal interfaces are subject to change or
- * deletion without notice.</b>
- */
-@SuppressWarnings("all") // Mainly sun code that has other warning settings
-public class PrettyCommentsPrinter extends JCTree.Visitor {
- private static final TreeTag PARENS = treeTag("PARENS");
- private static final TreeTag IMPORT = treeTag("IMPORT");
- private static final TreeTag VARDEF = treeTag("VARDEF");
- private static final TreeTag SELECT = treeTag("SELECT");
-
- private static final Map<TreeTag, String> OPERATORS;
-
- // StandardFlags | DEFAULT
- private static final long EXTENDED_STANDARD_FLAGS = 0x0fffL | 1L<<43;
-
- static {
- Map<TreeTag, String> map = new HashMap<TreeTag, String>();
-
- map.put(treeTag("POS"), "+");
- map.put(treeTag("NEG"), "-");
- map.put(treeTag("NOT"), "!");
- map.put(treeTag("COMPL"), "~");
- map.put(treeTag("PREINC"), "++");
- map.put(treeTag("PREDEC"), "--");
- map.put(treeTag("POSTINC"), "++");
- map.put(treeTag("POSTDEC"), "--");
- map.put(treeTag("NULLCHK"), "<*nullchk*>");
- map.put(treeTag("OR"), "||");
- map.put(treeTag("AND"), "&&");
- map.put(treeTag("EQ"), "==");
- map.put(treeTag("NE"), "!=");
- map.put(treeTag("LT"), "<");
- map.put(treeTag("GT"), ">");
- map.put(treeTag("LE"), "<=");
- map.put(treeTag("GE"), ">=");
- map.put(treeTag("BITOR"), "|");
- map.put(treeTag("BITXOR"), "^");
- map.put(treeTag("BITAND"), "&");
- map.put(treeTag("SL"), "<<");
- map.put(treeTag("SR"), ">>");
- map.put(treeTag("USR"), ">>>");
- map.put(treeTag("PLUS"), "+");
- map.put(treeTag("MINUS"), "-");
- map.put(treeTag("MUL"), "*");
- map.put(treeTag("DIV"), "/");
- map.put(treeTag("MOD"), "%");
-
- map.put(treeTag("BITOR_ASG"), "|=");
- map.put(treeTag("BITXOR_ASG"), "^=");
- map.put(treeTag("BITAND_ASG"), "&=");
- map.put(treeTag("SL_ASG"), "<<=");
- map.put(treeTag("SR_ASG"), ">>=");
- map.put(treeTag("USR_ASG"), ">>>=");
- map.put(treeTag("PLUS_ASG"), "+=");
- map.put(treeTag("MINUS_ASG"), "-=");
- map.put(treeTag("MUL_ASG"), "*=");
- map.put(treeTag("DIV_ASG"), "/=");
- map.put(treeTag("MOD_ASG"), "%=");
-
- OPERATORS = map;
- }
-
- private List<CommentInfo> comments;
- private final JCCompilationUnit cu;
- private boolean onNewLine = true;
- private boolean aligned = false;
- private boolean inParams = false;
-
- private boolean needsSpace = false;
- private boolean needsNewLine = false;
- private boolean needsAlign = false;
-
- // Flag for try-with-resources to make them not final and not print the last semicolon.
- // This flag is set just before printing the vardef and cleared when printing its modifiers.
- private boolean suppressFinalAndSemicolonsInTry = false;
-
- private final FormatPreferences formatPreferences;
-
- public PrettyCommentsPrinter(Writer out, JCCompilationUnit cu, List<CommentInfo> comments, FormatPreferences preferences) {
- this.out = out;
- this.comments = comments;
- this.cu = cu;
- this.formatPreferences = preferences;
- }
-
- private int endPos(JCTree tree) {
- return getEndPosition(tree, cu);
- }
-
- private void consumeComments(int until) throws IOException {
- consumeComments(until, null);
- }
-
- private void consumeComments(int until, JCTree tree) throws IOException {
- boolean prevNewLine = onNewLine;
- boolean found = false;
- CommentInfo head = comments.head;
- while (comments.nonEmpty() && head.pos < until) {
- printComment(head);
- comments = comments.tail;
- head = comments.head;
- }
- if (!onNewLine && prevNewLine) {
- println();
- }
- }
-
- private void consumeTrailingComments(int from) throws IOException {
- boolean prevNewLine = onNewLine;
- CommentInfo head = comments.head;
- boolean stop = false;
- while (comments.nonEmpty() && head.prevEndPos == from && !stop && !(head.start == StartConnection.ON_NEXT_LINE || head.start == StartConnection.START_OF_LINE)) {
- from = head.endPos;
- printComment(head);
- stop = (head.end == EndConnection.ON_NEXT_LINE);
- comments = comments.tail;
- head = comments.head;
- }
- if (!onNewLine && prevNewLine) {
- println();
- }
- }
-
- private void printComment(CommentInfo comment) throws IOException {
- prepareComment(comment.start);
- print(comment.content);
- switch (comment.end) {
- case ON_NEXT_LINE:
- if (!aligned) {
- needsNewLine = true;
- needsAlign = true;
- }
- break;
- case AFTER_COMMENT:
- needsSpace = true;
- break;
- case DIRECT_AFTER_COMMENT:
- // do nothing
- break;
- }
- }
-
- private void prepareComment(StartConnection start) throws IOException {
- switch (start) {
- case DIRECT_AFTER_PREVIOUS:
- needsSpace = false;
- break;
- case AFTER_PREVIOUS:
- needsSpace = true;
- break;
- case START_OF_LINE:
- needsNewLine = true;
- needsAlign = false;
- break;
- case ON_NEXT_LINE:
- if (!aligned) {
- needsNewLine = true;
- needsAlign = true;
- }
- break;
- }
- }
-
- /** The output stream on which trees are printed.
- */
- Writer out;
-
- /** The current left margin.
- */
- int lmargin = 0;
-
- /** The enclosing class name.
- */
- Name enclClassName;
-
- /** A hashtable mapping trees to their documentation comments
- * (can be null)
- */
- Map<JCTree, String> docComments = null;
- DocCommentTable docTable = null;
-
- String getJavadocFor(JCTree node) {
- if (docComments != null) return docComments.get(node);
- if (docTable != null) return docTable.getCommentText(node);
- return null;
- }
-
- /** Align code to be indented to left margin.
- */
- void align() throws IOException {
- onNewLine = false;
- aligned = true;
- needsAlign = false;
- for (int i = 0; i < lmargin; i++) out.write(formatPreferences.indent());
- }
-
- /** Increase left margin by indentation width.
- */
- void indent() {
- lmargin++;
- }
-
- /** Decrease left margin by indentation width.
- */
- void undent() {
- lmargin--;
- }
-
- /** Enter a new precedence level. Emit a `(' if new precedence level
- * is less than precedence level so far.
- * @param contextPrec The precedence level in force so far.
- * @param ownPrec The new precedence level.
- */
- void open(int contextPrec, int ownPrec) throws IOException {
- if (ownPrec < contextPrec) out.write("(");
- }
-
- /** Leave precedence level. Emit a `(' if inner precedence level
- * is less than precedence level we revert to.
- * @param contextPrec The precedence level we revert to.
- * @param ownPrec The inner precedence level.
- */
- void close(int contextPrec, int ownPrec) throws IOException {
- if (ownPrec < contextPrec) out.write(")");
- }
-
- /** Print string, replacing all non-ascii character with unicode escapes.
- */
- public void print(Object s) throws IOException {
- boolean align = needsAlign;
- if (needsNewLine && !onNewLine) {
- println();
- }
- if (align && !aligned) {
- align();
- }
- if (needsSpace && !onNewLine && !aligned) {
- out.write(' ');
- }
- needsSpace = false;
-
- out.write(Convert.escapeUnicode(s.toString()));
-
- onNewLine = false;
- aligned = false;
- }
-
- /** Print new line.
- */
- public void println() throws IOException {
- onNewLine = true;
- aligned = false;
- needsNewLine = false;
- out.write(lineSep);
- }
-
- String lineSep = System.getProperty("line.separator");
-
- /**************************************************************************
- * Traversal methods
- *************************************************************************/
-
- /** Exception to propagate IOException through visitXXX methods */
- private static class UncheckedIOException extends Error {
- static final long serialVersionUID = -4032692679158424751L;
- UncheckedIOException(IOException e) {
- super(e.getMessage(), e);
- }
- }
-
- /** Visitor argument: the current precedence level.
- */
- int prec;
-
- /** Visitor method: print expression tree.
- * @param prec The current precedence level.
- */
- public void printExpr(JCTree tree, int prec) throws IOException {
-
- int prevPrec = this.prec;
- try {
- this.prec = prec;
- if (tree == null) print("/*missing*/");
- else {
- consumeComments(tree.pos, tree);
- tree.accept(this);
- int endPos = endPos(tree);
- consumeTrailingComments(endPos);
- }
- } catch (UncheckedIOException ex) {
- IOException e = new IOException(ex.getMessage());
- e.initCause(ex);
- throw e;
- } finally {
- this.prec = prevPrec;
- }
- }
-
- /** Derived visitor method: print expression tree at minimum precedence level
- * for expression.
- */
- public void printExpr(JCTree tree) throws IOException {
- printExpr(tree, TreeInfo.noPrec);
- }
-
- /** Derived visitor method: print statement tree.
- */
- public void printStat(JCTree tree) throws IOException {
- if (isEmptyStat(tree)) {
- // printEmptyStat(); // -- starting in java 7, these get lost, so to be consistent, we never print them.
- } else {
- printExpr(tree, TreeInfo.notExpression);
- }
- }
-
- public void printEmptyStat() throws IOException {
- print(";");
- }
-
- public boolean isEmptyStat(JCTree tree) {
- if (!(tree instanceof JCBlock)) return false;
- JCBlock block = (JCBlock) tree;
- return (Position.NOPOS == block.pos) && block.stats.isEmpty();
- }
-
- /** Derived visitor method: print list of expression trees, separated by given string.
- * @param sep the separator string
- */
- public <T extends JCTree> void printExprs(List<T> trees, String sep) throws IOException {
- if (trees.nonEmpty()) {
- printExpr(trees.head);
- for (List<T> l = trees.tail; l.nonEmpty(); l = l.tail) {
- print(sep);
- printExpr(l.head);
- }
- }
- }
-
- /** Derived visitor method: print list of expression trees, separated by commas.
- */
- public <T extends JCTree> void printExprs(List<T> trees) throws IOException {
- printExprs(trees, ", ");
- }
-
- /** Derived visitor method: print list of statements, each on a separate line.
- */
- public void printStats(List<? extends JCTree> trees) throws IOException {
- for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) {
- if (isSuppressed(l.head)) continue;
- if (!suppressAlignmentForEmptyLines(l.head)) align();
- printStat(l.head);
- println();
- }
- }
-
- private boolean suppressAlignmentForEmptyLines(JCTree tree) {
- return !formatPreferences.fillEmpties() && startsWithNewLine(tree);
- }
-
- private boolean startsWithNewLine(JCTree tree) {
- return tree instanceof JCMethodDecl || tree instanceof JCClassDecl;
- }
-
- private boolean isSuppressed(JCTree tree) {
- if (isEmptyStat(tree)) {
- return true;
- }
- if (tree instanceof JCExpressionStatement) {
- return isNoArgsSuperCall(((JCExpressionStatement)tree).expr);
- }
- return false;
- }
-
- /** Print a set of modifiers.
- */
- public void printFlags(long flags) throws IOException {
- if ((flags & SYNTHETIC) != 0) print("/*synthetic*/ ");
- if (suppressFinalAndSemicolonsInTry) {
- flags = flags & ~FINAL;
- suppressFinalAndSemicolonsInTry = false;
- }
- print(TreeInfo.flagNames(flags));
- if ((flags & EXTENDED_STANDARD_FLAGS) != 0) print(" ");
- if ((flags & ANNOTATION) != 0) print("@");
- }
-
- public void printAnnotations(List<JCAnnotation> trees) throws IOException {
- for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
- printStat(l.head);
- if (inParams) {
- print(" ");
- }
- else {
- println();
- align();
- }
- }
- }
-
- /** Print documentation comment, if it exists
- * @param tree The tree for which a documentation comment should be printed.
- */
- public void printDocComment(JCTree tree) throws IOException {
- String dc = getJavadocFor(tree);
- if (dc == null) return;
- print("/**"); println();
- int pos = 0;
- int endpos = lineEndPos(dc, pos);
- boolean atStart = true;
- while (pos < dc.length()) {
- String line = dc.substring(pos, endpos);
- if (line.trim().isEmpty() && atStart) {
- atStart = false;
- continue;
- }
- atStart = false;
- align();
- print(" *");
- if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
- print(dc.substring(pos, endpos)); println();
- pos = endpos + 1;
- endpos = lineEndPos(dc, pos);
- }
- align(); print(" */"); println();
- align();
- }
-//where
- static int lineEndPos(String s, int start) {
- int pos = s.indexOf('\n', start);
- if (pos < 0) pos = s.length();
- return pos;
- }
-
- /** If type parameter list is non-empty, print it enclosed in "<...>" brackets.
- */
- public void printTypeParameters(List<JCTypeParameter> trees) throws IOException {
- if (trees.nonEmpty()) {
- print("<");
- printExprs(trees);
- print(">");
- }
- }
-
- /** Print a block.
- */
- public void printBlock(List<? extends JCTree> stats, JCTree container) throws IOException {
- print("{");
- println();
- indent();
- printStats(stats);
- consumeComments(endPos(container));
- undent();
- align();
- print("}");
- }
-
- /** Print a block.
- */
- public void printEnumBody(List<JCTree> stats) throws IOException {
- print("{");
- println();
- indent();
- boolean first = true;
- for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
- if (isEnumerator(l.head)) {
- if (!first) {
- print(",");
- println();
- }
- align();
- printStat(l.head);
- first = false;
- }
- }
- print(";");
- println();
- int x = 0;
- for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
- x++;
- if (!isEnumerator(l.head)) {
- if (!suppressAlignmentForEmptyLines(l.head)) align();
- printStat(l.head);
- println();
- }
- }
- undent();
- align();
- print("}");
- }
-
- public void printEnumMember(JCVariableDecl tree) throws IOException {
- printAnnotations(tree.mods.annotations);
- print(tree.name);
- if (tree.init instanceof JCNewClass) {
- JCNewClass constructor = (JCNewClass) tree.init;
- if (constructor.args != null && constructor.args.nonEmpty()) {
- print("(");
- printExprs(constructor.args);
- print(")");
- }
- if (constructor.def != null && constructor.def.defs != null) {
- print(" ");
- printBlock(constructor.def.defs, constructor.def);
- }
- }
- }
-
- /** Is the given tree an enumerator definition? */
- boolean isEnumerator(JCTree t) {
- return VARDEF.equals(treeTag(t)) && (((JCVariableDecl) t).mods.flags & ENUM) != 0;
- }
-
- /** Print unit consisting of package clause and import statements in toplevel,
- * followed by class definition. if class definition == null,
- * print all definitions in toplevel.
- * @param tree The toplevel tree
- * @param cdef The class definition, which is assumed to be part of the
- * toplevel tree.
- */
- public void printUnit(JCCompilationUnit tree, JCClassDecl cdef) throws IOException {
- Object dc = getDocComments(tree);
- loadDocCommentsTable(dc);
- printDocComment(tree);
- if (tree.pid != null) {
- consumeComments(tree.pos, tree);
- print("package ");
- printExpr(tree.pid);
- print(";");
- println();
- }
- boolean firstImport = true;
- for (List<JCTree> l = tree.defs;
- l.nonEmpty() && (cdef == null || IMPORT.equals(treeTag(l.head)));
- l = l.tail) {
- if (IMPORT.equals(treeTag(l.head))) {
- JCImport imp = (JCImport)l.head;
- Name name = TreeInfo.name(imp.qualid);
- if (name == name.table.fromChars(new char[] {'*'}, 0, 1) ||
- cdef == null ||
- isUsed(TreeInfo.symbol(imp.qualid), cdef)) {
- if (firstImport) {
- firstImport = false;
- println();
- }
- printStat(imp);
- }
- } else {
- printStat(l.head);
- }
- }
- if (cdef != null) {
- printStat(cdef);
- println();
- }
- }
- // where
- @SuppressWarnings("unchecked")
- private void loadDocCommentsTable(Object dc) {
- if (dc instanceof Map<?, ?>) this.docComments = (Map) dc;
- else if (dc instanceof DocCommentTable) this.docTable = (DocCommentTable) dc;
- }
-
- boolean isUsed(final Symbol t, JCTree cdef) {
- class UsedVisitor extends TreeScanner {
- public void scan(JCTree tree) {
- if (tree!=null && !result) tree.accept(this);
- }
- boolean result = false;
- public void visitIdent(JCIdent tree) {
- if (tree.sym == t) result = true;
- }
- }
- UsedVisitor v = new UsedVisitor();
- v.scan(cdef);
- return v.result;
- }
-
- /**************************************************************************
- * Visitor methods
- *************************************************************************/
-
- public void visitTopLevel(JCCompilationUnit tree) {
- try {
- printUnit(tree, null);
- consumeComments(Integer.MAX_VALUE);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitImport(JCImport tree) {
- try {
- print("import ");
- if (tree.staticImport) print("static ");
- printExpr(tree.qualid);
- print(";");
- println();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitClassDef(JCClassDecl tree) {
- try {
- consumeComments(tree.pos, tree);
- println(); align();
- printDocComment(tree);
- printAnnotations(tree.mods.annotations);
- printFlags(tree.mods.flags & ~INTERFACE);
- Name enclClassNamePrev = enclClassName;
- enclClassName = tree.name;
- if ((tree.mods.flags & INTERFACE) != 0) {
- print("interface " + tree.name);
- printTypeParameters(tree.typarams);
- if (tree.implementing.nonEmpty()) {
- print(" extends ");
- printExprs(tree.implementing);
- }
- } else {
- if ((tree.mods.flags & ENUM) != 0)
- print("enum " + tree.name);
- else
- print("class " + tree.name);
- printTypeParameters(tree.typarams);
- if (getExtendsClause(tree) != null) {
- print(" extends ");
- printExpr(getExtendsClause(tree));
- }
- if (tree.implementing.nonEmpty()) {
- print(" implements ");
- printExprs(tree.implementing);
- }
- }
- print(" ");
- // <Added for delombok by Reinier Zwitserloot>
- if ((tree.mods.flags & INTERFACE) != 0) {
- removeImplicitModifiersForInterfaceMembers(tree.defs);
- }
- // </Added for delombok by Reinier Zwitserloot>
- if ((tree.mods.flags & ENUM) != 0) {
- printEnumBody(tree.defs);
- } else {
- printBlock(tree.defs, tree);
- }
- enclClassName = enclClassNamePrev;
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- // Added for delombok by Reinier Zwitserloot
- private void removeImplicitModifiersForInterfaceMembers(List<JCTree> defs) {
- for (JCTree def :defs) {
- if (def instanceof JCVariableDecl) {
- ((JCVariableDecl) def).mods.flags &= ~(Flags.PUBLIC | Flags.STATIC | Flags.FINAL);
- }
- if (def instanceof JCMethodDecl) {
- ((JCMethodDecl) def).mods.flags &= ~(Flags.PUBLIC | Flags.ABSTRACT);
- }
- if (def instanceof JCClassDecl) {
- ((JCClassDecl) def).mods.flags &= ~(Flags.PUBLIC | Flags.STATIC);
- }
- }
- }
-
- public void visitMethodDef(JCMethodDecl tree) {
- try {
- boolean isConstructor = tree.name == tree.name.table.fromChars("<init>".toCharArray(), 0, 6);
- // when producing source output, omit anonymous constructors
- if (isConstructor && enclClassName == null) return;
- boolean isGeneratedConstructor = isConstructor && ((tree.mods.flags & Flags.GENERATEDCONSTR) != 0);
- if (isGeneratedConstructor) return;
- println(); align();
- printDocComment(tree);
- printExpr(tree.mods);
- printTypeParameters(tree.typarams);
- if (tree.typarams != null && tree.typarams.length() > 0) print(" ");
- if (tree.name == tree.name.table.fromChars("<init>".toCharArray(), 0, 6)) {
- print(enclClassName != null ? enclClassName : tree.name);
- } else {
- printExpr(tree.restype);
- print(" " + tree.name);
- }
- print("(");
- inParams = true;
- printExprs(tree.params);
- inParams = false;
- print(")");
- if (tree.thrown.nonEmpty()) {
- print(" throws ");
- printExprs(tree.thrown);
- }
- if (tree.defaultValue != null) {
- print(" default ");
- print(tree.defaultValue);
- }
- if (tree.body != null) {
- print(" ");
- printBlock(tree.body.stats, tree.body);
- } else {
- print(";");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitVarDef(JCVariableDecl tree) {
- try {
- boolean suppressSemi = suppressFinalAndSemicolonsInTry;
- if (getJavadocFor(tree) != null) {
- println(); align();
- }
- printDocComment(tree);
- if ((tree.mods.flags & ENUM) != 0) {
- printEnumMember(tree);
- } else {
- printExpr(tree.mods);
- if ((tree.mods.flags & VARARGS) != 0) {
- printExpr(((JCArrayTypeTree) tree.vartype).elemtype);
- print("... " + tree.name);
- } else {
- printExpr(tree.vartype);
- print(" " + tree.name);
- }
- if (tree.init != null) {
- print(" = ");
- printExpr(tree.init);
- }
- if (prec == TreeInfo.notExpression && !suppressSemi) print(";");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitSkip(JCSkip tree) {
- try {
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitBlock(JCBlock tree) {
- try {
- consumeComments(tree.pos);
- printFlags(tree.flags);
- printBlock(tree.stats, tree);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitDoLoop(JCDoWhileLoop tree) {
- try {
- print("do ");
- printStat(tree.body);
- align();
- print(" while ");
- if (PARENS.equals(treeTag(tree.cond))) {
- printExpr(tree.cond);
- } else {
- print("(");
- printExpr(tree.cond);
- print(")");
- }
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitWhileLoop(JCWhileLoop tree) {
- try {
- print("while ");
- if (PARENS.equals(treeTag(tree.cond))) {
- printExpr(tree.cond);
- } else {
- print("(");
- printExpr(tree.cond);
- print(")");
- }
- print(" ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitForLoop(JCForLoop tree) {
- try {
- print("for (");
- if (tree.init.nonEmpty()) {
- if (VARDEF.equals(treeTag(tree.init.head))) {
- printExpr(tree.init.head);
- for (List<JCStatement> l = tree.init.tail; l.nonEmpty(); l = l.tail) {
- JCVariableDecl vdef = (JCVariableDecl)l.head;
- print(", " + vdef.name + " = ");
- printExpr(vdef.init);
- }
- } else {
- printExprs(tree.init);
- }
- }
- print("; ");
- if (tree.cond != null) printExpr(tree.cond);
- print("; ");
- printExprs(tree.step);
- print(") ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitForeachLoop(JCEnhancedForLoop tree) {
- try {
- print("for (");
- printExpr(tree.var);
- print(" : ");
- printExpr(tree.expr);
- print(") ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitLabelled(JCLabeledStatement tree) {
- try {
- print(tree.label + ":");
- if (isEmptyStat(tree.body) || tree.body instanceof JCSkip) {
- print(" ;");
- } else if (tree.body instanceof JCBlock) {
- print(" ");
- printStat(tree.body);
- } else {
- println();
- align();
- printStat(tree.body);
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitSwitch(JCSwitch tree) {
- try {
- print("switch ");
- if (PARENS.equals(treeTag(tree.selector))) {
- printExpr(tree.selector);
- } else {
- print("(");
- printExpr(tree.selector);
- print(")");
- }
- print(" {");
- println();
- printStats(tree.cases);
- align();
- print("}");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitCase(JCCase tree) {
- try {
- if (tree.pat == null) {
- print("default");
- } else {
- print("case ");
- printExpr(tree.pat);
- }
- print(": ");
- println();
- indent();
- printStats(tree.stats);
- undent();
- align();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitSynchronized(JCSynchronized tree) {
- try {
- print("synchronized ");
- if (PARENS.equals(treeTag(tree.lock))) {
- printExpr(tree.lock);
- } else {
- print("(");
- printExpr(tree.lock);
- print(")");
- }
- print(" ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTry(JCTry tree) {
- try {
- print("try ");
- List<?> resources = null;
- try {
- Field f = JCTry.class.getField("resources");
- resources = (List<?>) f.get(tree);
- } catch (Exception ignore) {
- // In JDK6 and down this field does not exist; resources will retain its initializer value which is what we want.
- }
-
- if (resources != null && resources.nonEmpty()) {
- print("(");
- int remaining = resources.size();
- if (remaining == 1) {
- JCTree var = (JCTree) resources.get(0);
- suppressFinalAndSemicolonsInTry = true;
- printStat(var);
- print(") ");
- } else {
- indent(); indent();
- for (Object var0 : resources) {
- println();
- align();
- JCTree var = (JCTree) var0;
- suppressFinalAndSemicolonsInTry = true;
- printStat(var);
- remaining--;
- if (remaining > 0) print(";");
- }
- print(") ");
- undent(); undent();
- }
- }
-
- printStat(tree.body);
- for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
- printStat(l.head);
- }
- if (tree.finalizer != null) {
- print(" finally ");
- printStat(tree.finalizer);
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitCatch(JCCatch tree) {
- try {
- print(" catch (");
- printExpr(tree.param);
- print(") ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitConditional(JCConditional tree) {
- try {
- open(prec, TreeInfo.condPrec);
- printExpr(tree.cond, TreeInfo.condPrec);
- print(" ? ");
- printExpr(tree.truepart, TreeInfo.condPrec);
- print(" : ");
- printExpr(tree.falsepart, TreeInfo.condPrec);
- close(prec, TreeInfo.condPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitIf(JCIf tree) {
- try {
- print("if ");
- if (PARENS.equals(treeTag(tree.cond))) {
- printExpr(tree.cond);
- } else {
- print("(");
- printExpr(tree.cond);
- print(")");
- }
- print(" ");
- printStat(tree.thenpart);
- if (tree.elsepart != null) {
- print(" else ");
- printStat(tree.elsepart);
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private boolean isNoArgsSuperCall(JCExpression expr) {
- if (!(expr instanceof JCMethodInvocation)) return false;
- JCMethodInvocation tree = (JCMethodInvocation) expr;
- if (!tree.typeargs.isEmpty() || !tree.args.isEmpty()) return false;
- if (!(tree.meth instanceof JCIdent)) return false;
- return ((JCIdent) tree.meth).name.toString().equals("super");
- }
-
- public void visitExec(JCExpressionStatement tree) {
- if (isNoArgsSuperCall(tree.expr)) return;
- try {
- printExpr(tree.expr);
- if (prec == TreeInfo.notExpression) print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitBreak(JCBreak tree) {
- try {
- print("break");
- if (tree.label != null) print(" " + tree.label);
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitContinue(JCContinue tree) {
- try {
- print("continue");
- if (tree.label != null) print(" " + tree.label);
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitReturn(JCReturn tree) {
- try {
- print("return");
- if (tree.expr != null) {
- print(" ");
- printExpr(tree.expr);
- }
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitThrow(JCThrow tree) {
- try {
- print("throw ");
- printExpr(tree.expr);
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitAssert(JCAssert tree) {
- try {
- print("assert ");
- printExpr(tree.cond);
- if (tree.detail != null) {
- print(" : ");
- printExpr(tree.detail);
- }
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitApply(JCMethodInvocation tree) {
- try {
- if (!tree.typeargs.isEmpty()) {
- if (SELECT.equals(treeTag(tree.meth))) {
- JCFieldAccess left = (JCFieldAccess)tree.meth;
- printExpr(left.selected);
- print(".<");
- printExprs(tree.typeargs);
- print(">" + left.name);
- } else {
- print("<");
- printExprs(tree.typeargs);
- print(">");
- printExpr(tree.meth);
- }
- } else {
- printExpr(tree.meth);
- }
- print("(");
- printExprs(tree.args);
- print(")");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitNewClass(JCNewClass tree) {
- try {
- if (tree.encl != null) {
- printExpr(tree.encl);
- print(".");
- }
- print("new ");
- if (!tree.typeargs.isEmpty()) {
- print("<");
- printExprs(tree.typeargs);
- print(">");
- }
- printExpr(tree.clazz);
- print("(");
- printExprs(tree.args);
- print(")");
- if (tree.def != null) {
- Name enclClassNamePrev = enclClassName;
- enclClassName =
- tree.def.name != null ? tree.def.name :
- tree.type != null && tree.type.tsym.name != tree.type.tsym.name.table.fromChars(new char[0], 0, 0) ? tree.type.tsym.name :
- null;
- if ((tree.def.mods.flags & Flags.ENUM) != 0) print("/*enum*/");
- printBlock(tree.def.defs, tree.def);
- enclClassName = enclClassNamePrev;
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitNewArray(JCNewArray tree) {
- try {
- if (tree.elemtype != null) {
- print("new ");
- JCTree elem = tree.elemtype;
- if (elem instanceof JCArrayTypeTree)
- printBaseElementType((JCArrayTypeTree) elem);
- else
- printExpr(elem);
- for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
- print("[");
- printExpr(l.head);
- print("]");
- }
- if (elem instanceof JCArrayTypeTree)
- printBrackets((JCArrayTypeTree) elem);
- }
- if (tree.elems != null) {
- if (tree.elemtype != null) print("[]");
- print("{");
- printExprs(tree.elems);
- print("}");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitParens(JCParens tree) {
- try {
- print("(");
- printExpr(tree.expr);
- print(")");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitAssign(JCAssign tree) {
- try {
- open(prec, TreeInfo.assignPrec);
- printExpr(tree.lhs, TreeInfo.assignPrec + 1);
- print(" = ");
- printExpr(tree.rhs, TreeInfo.assignPrec);
- close(prec, TreeInfo.assignPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public String operatorName(TreeTag tag) {
- String result = OPERATORS.get(tag);
- if (result == null) throw new Error();
- return result;
- }
-
- public void visitAssignop(JCAssignOp tree) {
- try {
- open(prec, TreeInfo.assignopPrec);
- printExpr(tree.lhs, TreeInfo.assignopPrec + 1);
- String opname = operatorName(treeTag(tree));
- print(" " + opname + " ");
- printExpr(tree.rhs, TreeInfo.assignopPrec);
- close(prec, TreeInfo.assignopPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitUnary(JCUnary tree) {
- try {
- int ownprec = isOwnPrec(tree);
- String opname = operatorName(treeTag(tree));
- open(prec, ownprec);
- if (isPrefixUnary(tree)) {
- print(opname);
- printExpr(tree.arg, ownprec);
- } else {
- printExpr(tree.arg, ownprec);
- print(opname);
- }
- close(prec, ownprec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private int isOwnPrec(JCExpression tree) {
- return treeTag(tree).getOperatorPrecedenceLevel();
- }
-
- private boolean isPrefixUnary(JCUnary tree) {
- return treeTag(tree).isPrefixUnaryOp();
- }
-
- public void visitBinary(JCBinary tree) {
- try {
- int ownprec = isOwnPrec(tree);
- String opname = operatorName(treeTag(tree));
- open(prec, ownprec);
- printExpr(tree.lhs, ownprec);
- print(" " + opname + " ");
- printExpr(tree.rhs, ownprec + 1);
- close(prec, ownprec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeCast(JCTypeCast tree) {
- try {
- open(prec, TreeInfo.prefixPrec);
- print("(");
- printExpr(tree.clazz);
- print(")");
- printExpr(tree.expr, TreeInfo.prefixPrec);
- close(prec, TreeInfo.prefixPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeTest(JCInstanceOf tree) {
- try {
- open(prec, TreeInfo.ordPrec);
- printExpr(tree.expr, TreeInfo.ordPrec);
- print(" instanceof ");
- printExpr(tree.clazz, TreeInfo.ordPrec + 1);
- close(prec, TreeInfo.ordPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitIndexed(JCArrayAccess tree) {
- try {
- printExpr(tree.indexed, TreeInfo.postfixPrec);
- print("[");
- printExpr(tree.index);
- print("]");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitSelect(JCFieldAccess tree) {
- try {
- printExpr(tree.selected, TreeInfo.postfixPrec);
- print("." + tree.name);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitIdent(JCIdent tree) {
- try {
- print(tree.name);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitLiteral(JCLiteral tree) {
- TypeTag typeTag = typeTag(tree);
- try {
- if (CTC_INT.equals(typeTag)) print(tree.value.toString());
- else if (CTC_LONG.equals(typeTag)) print(tree.value + "L");
- else if (CTC_FLOAT.equals(typeTag)) print(tree.value + "F");
- else if (CTC_DOUBLE.equals(typeTag)) print(tree.value.toString());
- else if (CTC_CHAR.equals(typeTag)) {
- print("\'" + Convert.quote(String.valueOf((char)((Number)tree.value).intValue())) + "\'");
- }
- else if (CTC_BOOLEAN.equals(typeTag)) print(((Number)tree.value).intValue() == 1 ? "true" : "false");
- else if (CTC_BOT.equals(typeTag)) print("null");
- else print("\"" + Convert.quote(tree.value.toString()) + "\"");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeIdent(JCPrimitiveTypeTree tree) {
- TypeTag typetag = typeTag(tree);
- try {
- if (CTC_BYTE.equals(typetag)) print ("byte");
- else if (CTC_CHAR.equals(typetag)) print ("char");
- else if (CTC_SHORT.equals(typetag)) print ("short");
- else if (CTC_INT.equals(typetag)) print ("int");
- else if (CTC_LONG.equals(typetag)) print ("long");
- else if (CTC_FLOAT.equals(typetag)) print ("float");
- else if (CTC_DOUBLE.equals(typetag)) print ("double");
- else if (CTC_BOOLEAN.equals(typetag)) print ("boolean");
- else if (CTC_VOID.equals(typetag)) print ("void");
- else print("error");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeArray(JCArrayTypeTree tree) {
- try {
- printBaseElementType(tree);
- printBrackets(tree);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- // Prints the inner element type of a nested array
- private void printBaseElementType(JCArrayTypeTree tree) throws IOException {
- JCTree elem = tree.elemtype;
- while (elem instanceof JCWildcard)
- elem = ((JCWildcard) elem).inner;
- if (elem instanceof JCArrayTypeTree)
- printBaseElementType((JCArrayTypeTree) elem);
- else
- printExpr(elem);
- }
-
- // prints the brackets of a nested array in reverse order
- private void printBrackets(JCArrayTypeTree tree) throws IOException {
- JCTree elem;
- while (true) {
- elem = tree.elemtype;
- print("[]");
- if (!(elem instanceof JCArrayTypeTree)) break;
- tree = (JCArrayTypeTree) elem;
- }
- }
-
- public void visitTypeApply(JCTypeApply tree) {
- try {
- printExpr(tree.clazz);
- print("<");
- printExprs(tree.arguments);
- print(">");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeParameter(JCTypeParameter tree) {
- try {
- print(tree.name);
- if (tree.bounds.nonEmpty()) {
- print(" extends ");
- printExprs(tree.bounds, " & ");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- @Override
- public void visitWildcard(JCWildcard tree) {
- try {
- Object kind = tree.getClass().getField("kind").get(tree);
- print(kind);
- if (kind != null && kind.getClass().getSimpleName().equals("TypeBoundKind")) {
- kind = kind.getClass().getField("kind").get(kind);
- }
-
- if (tree.getKind() != Tree.Kind.UNBOUNDED_WILDCARD)
- printExpr(tree.inner);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- } catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void visitTypeBoundKind(TypeBoundKind tree) {
- try {
- print(String.valueOf(tree.kind));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitErroneous(JCErroneous tree) {
- try {
- print("(ERROR)");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitLetExpr(LetExpr tree) {
- try {
- print("(let " + tree.defs + " in " + tree.expr + ")");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitModifiers(JCModifiers mods) {
- try {
- printAnnotations(mods.annotations);
- printFlags(mods.flags);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitAnnotation(JCAnnotation tree) {
- try {
- print("@");
- printExpr(tree.annotationType);
- if (tree.args.nonEmpty()) {
- print("(");
- if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) {
- JCExpression lhs = ((JCAssign)tree.args.get(0)).lhs;
- if (lhs instanceof JCIdent && ((JCIdent)lhs).name.toString().equals("value")) tree.args = List.of(((JCAssign)tree.args.get(0)).rhs);
- }
- printExprs(tree.args);
- print(")");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTree(JCTree tree) {
- try {
- String simpleName = tree.getClass().getSimpleName();
- if ("JCTypeUnion".equals(simpleName)) {
- printExprs(readExpressionList(tree, "alternatives"), " | ");
- return;
- } else if ("JCTypeIntersection".equals(simpleName)) {
- printExprs(readExpressionList(tree, "bounds"), " & ");
- return;
- } else if ("JCLambda".equals(simpleName)) {
- visitLambda0(tree);
- return;
- } else if ("JCMemberReference".equals(simpleName)) {
- visitReference0(tree);
- return;
- } else {
- print("(UNKNOWN[" + tree.getClass().getSimpleName() + "]: " + tree + ")");
- println();
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private void visitLambda0(JCTree tree) {
- try {
- print("(");
- @SuppressWarnings("unchecked")
- List<JCVariableDecl> params = (List<JCVariableDecl>) readTreeList(tree, "params");
- boolean explicit = true;
- try {
- explicit = readObject(tree, "paramKind").toString().equals("EXPLICIT");
- } catch (Exception e) {}
- if (explicit) {
- printExprs(params);
- } else {
- String sep = "";
- for (JCVariableDecl param : params) {
- print(sep);
- print(param.name);
- sep = ", ";
- }
- }
- print(") -> ");
- printExpr(readTree(tree, "body"));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
-
- public void visitReference0(JCTree tree) {
- try {
- printExpr(readTree(tree, "expr"));
- print("::");
- List<JCExpression> typeArgs = readExpressionList(tree, "typeargs");
- if (typeArgs != null) {
- print("<");
- printExprs(typeArgs);
- print(">");
- }
- ;
- print(readObject(tree, "mode").toString().equals("INVOKE") ? readObject(tree, "name") : "new");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private JCTree readTree(JCTree tree, String fieldName) {
- try {
- return (JCTree) readObject0(tree, fieldName);
- } catch (Exception e) {
- return null;
- }
- }
-
- @SuppressWarnings("unchecked")
- private List<? extends JCTree> readTreeList(JCTree tree, String fieldName) throws IOException {
- try {
- return (List<? extends JCTree>) readObject0(tree, fieldName);
- } catch (Exception e) {
- return List.nil();
- }
- }
-
- @SuppressWarnings("unchecked")
- private List<JCExpression> readExpressionList(JCTree tree, String fieldName) throws IOException {
- try {
- return (List<JCExpression>) readObject0(tree, fieldName);
- } catch (Exception e) {
- return List.nil();
- }
- }
-
- private Object readObject(JCTree tree, String fieldName) {
- try {
- return readObject0(tree, fieldName);
- } catch (Exception e) {
- return null;
- }
- }
-
- @SuppressWarnings("unchecked")
- private Object readObject0(JCTree tree, String fieldName) throws Exception {
- try {
- return tree.getClass().getDeclaredField(fieldName).get(tree);
- } catch (Exception e) {
- print("ERROR_READING_FIELD");
- throw e;
- }
- }
-}
diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java
new file mode 100644
index 00000000..4261a558
--- /dev/null
+++ b/src/delombok/lombok/delombok/PrettyPrinter.java
@@ -0,0 +1,1524 @@
+/*
+ * Copyright (C) 2016 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.delombok;
+
+import static com.sun.tools.javac.code.Flags.*;
+import static lombok.javac.Javac.*;
+import static lombok.javac.JavacTreeMaker.TreeTag.treeTag;
+import static lombok.javac.JavacTreeMaker.TypeTag.typeTag;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.sun.tools.javac.tree.DocCommentTable;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCArrayAccess;
+import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
+import com.sun.tools.javac.tree.JCTree.JCAssert;
+import com.sun.tools.javac.tree.JCTree.JCAssign;
+import com.sun.tools.javac.tree.JCTree.JCAssignOp;
+import com.sun.tools.javac.tree.JCTree.JCBinary;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCBreak;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCCatch;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCConditional;
+import com.sun.tools.javac.tree.JCTree.JCContinue;
+import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
+import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
+import com.sun.tools.javac.tree.JCTree.JCErroneous;
+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.JCForLoop;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCIf;
+import com.sun.tools.javac.tree.JCTree.JCImport;
+import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
+import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCNewArray;
+import com.sun.tools.javac.tree.JCTree.JCNewClass;
+import com.sun.tools.javac.tree.JCTree.JCParens;
+import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
+import com.sun.tools.javac.tree.JCTree.JCReturn;
+import com.sun.tools.javac.tree.JCTree.JCSkip;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCSwitch;
+import com.sun.tools.javac.tree.JCTree.JCSynchronized;
+import com.sun.tools.javac.tree.JCTree.JCThrow;
+import com.sun.tools.javac.tree.JCTree.JCTry;
+import com.sun.tools.javac.tree.JCTree.JCTypeApply;
+import com.sun.tools.javac.tree.JCTree.JCTypeCast;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCUnary;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
+import com.sun.tools.javac.tree.JCTree.JCWildcard;
+import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Position;
+
+import lombok.javac.CommentInfo;
+import lombok.javac.PackageName;
+import lombok.javac.CommentInfo.EndConnection;
+import lombok.javac.CommentInfo.StartConnection;
+import lombok.javac.JavacTreeMaker.TreeTag;
+import lombok.javac.JavacTreeMaker.TypeTag;
+
+public class PrettyPrinter extends JCTree.Visitor {
+ private static final String LINE_SEP = System.getProperty("line.separator");
+ private static final Map<TreeTag, String> OPERATORS;
+
+ static {
+ Map<TreeTag, String> map = new HashMap<TreeTag, String>();
+
+ map.put(treeTag("POS"), "+");
+ map.put(treeTag("NEG"), "-");
+ map.put(treeTag("NOT"), "!");
+ map.put(treeTag("COMPL"), "~");
+ map.put(treeTag("PREINC"), "++");
+ map.put(treeTag("PREDEC"), "--");
+ map.put(treeTag("POSTINC"), "++");
+ map.put(treeTag("POSTDEC"), "--");
+ map.put(treeTag("NULLCHK"), "<*nullchk*>");
+ map.put(treeTag("OR"), "||");
+ map.put(treeTag("AND"), "&&");
+ map.put(treeTag("EQ"), "==");
+ map.put(treeTag("NE"), "!=");
+ map.put(treeTag("LT"), "<");
+ map.put(treeTag("GT"), ">");
+ map.put(treeTag("LE"), "<=");
+ map.put(treeTag("GE"), ">=");
+ map.put(treeTag("BITOR"), "|");
+ map.put(treeTag("BITXOR"), "^");
+ map.put(treeTag("BITAND"), "&");
+ map.put(treeTag("SL"), "<<");
+ map.put(treeTag("SR"), ">>");
+ map.put(treeTag("USR"), ">>>");
+ map.put(treeTag("PLUS"), "+");
+ map.put(treeTag("MINUS"), "-");
+ map.put(treeTag("MUL"), "*");
+ map.put(treeTag("DIV"), "/");
+ map.put(treeTag("MOD"), "%");
+
+ map.put(treeTag("BITOR_ASG"), "|=");
+ map.put(treeTag("BITXOR_ASG"), "^=");
+ map.put(treeTag("BITAND_ASG"), "&=");
+ map.put(treeTag("SL_ASG"), "<<=");
+ map.put(treeTag("SR_ASG"), ">>=");
+ map.put(treeTag("USR_ASG"), ">>>=");
+ map.put(treeTag("PLUS_ASG"), "+=");
+ map.put(treeTag("MINUS_ASG"), "-=");
+ map.put(treeTag("MUL_ASG"), "*=");
+ map.put(treeTag("DIV_ASG"), "/=");
+ map.put(treeTag("MOD_ASG"), "%=");
+
+ OPERATORS = map;
+ }
+
+ private final Writer out;
+ private final JCCompilationUnit compilationUnit;
+ private List<CommentInfo> comments;
+ private final FormatPreferences formatPreferences;
+
+ private final Map<JCTree, String> docComments;
+ private final DocCommentTable docTable;
+ private int indent = 0;
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public PrettyPrinter(Writer out, JCCompilationUnit cu, List<CommentInfo> comments, FormatPreferences preferences) {
+ this.out = out;
+ this.comments = comments;
+ this.compilationUnit = cu;
+ this.formatPreferences = preferences;
+
+ /* load doc comments */ {
+ Object dc = getDocComments(compilationUnit);
+ if (dc instanceof Map<?, ?>) {
+ this.docComments = (Map) dc;
+ this.docTable = null;
+ } else if (dc instanceof DocCommentTable) {
+ this.docComments = null;
+ this.docTable = (DocCommentTable) dc;
+ } else {
+ this.docComments = null;
+ this.docTable = null;
+ }
+ }
+ }
+
+ private int endPos(JCTree tree) {
+ return getEndPosition(tree, compilationUnit);
+ }
+
+ private static int lineEndPos(String s, int start) {
+ int pos = s.indexOf('\n', start);
+ if (pos < 0) pos = s.length();
+ return pos;
+ }
+
+ private boolean needsAlign, needsNewLine, onNewLine = true, needsSpace, aligned;
+
+ public static final class UncheckedIOException extends RuntimeException {
+ UncheckedIOException(IOException source) {
+ super(toMsg(source));
+ setStackTrace(source.getStackTrace());
+ }
+
+ private static String toMsg(Throwable t) {
+ String msg = t.getMessage();
+ String n = t.getClass().getSimpleName();
+ if (msg == null || msg.isEmpty()) return n;
+ return n + ": " + msg;
+ }
+ }
+
+ private void align() {
+ if (!onNewLine) return;
+ try {
+ for (int i = 0; i < indent; i++) out.write(formatPreferences.indent());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ onNewLine = false;
+ aligned = true;
+ needsAlign = false;
+ }
+
+ private void print(JCTree tree) {
+ if (tree == null) {
+ print("/*missing*/");
+ return;
+ }
+
+ consumeComments(tree);
+ tree.accept(this);
+ consumeTrailingComments(endPos(tree));
+ }
+
+ private void print(List<? extends JCTree> trees, String infix) {
+ boolean first = true;
+ JCTree prev = null;
+ for (JCTree tree : trees) {
+ if (suppress(tree)) continue;
+ if (!first && infix != null && !infix.isEmpty()) {
+ if ("\n".equals(infix)) println(prev);
+ else print(infix);
+ }
+ first = false;
+ print(tree);
+ prev = tree;
+ }
+ }
+
+ private boolean suppress(JCTree tree) {
+ if (tree instanceof JCBlock) {
+ JCBlock block = (JCBlock) tree;
+ return (Position.NOPOS == block.pos) && block.stats.isEmpty();
+ }
+
+ if (tree instanceof JCExpressionStatement) {
+ JCExpression expr = ((JCExpressionStatement)tree).expr;
+ if (expr instanceof JCMethodInvocation) {
+ JCMethodInvocation inv = (JCMethodInvocation) expr;
+ if (!inv.typeargs.isEmpty() || !inv.args.isEmpty()) return false;
+ if (!(inv.meth instanceof JCIdent)) return false;
+ return ((JCIdent) inv.meth).name.toString().equals("super");
+ }
+ }
+
+ return false;
+ }
+
+ private void print(CharSequence s) {
+ boolean align = needsAlign;
+ if (needsNewLine && !onNewLine) println();
+ if (align && !aligned) align();
+ try {
+ if (needsSpace && !onNewLine && !aligned) out.write(' ');
+ out.write(s.toString());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ needsSpace = false;
+ onNewLine = false;
+ aligned = false;
+ }
+
+
+ private void println() {
+ try {
+ out.write(LINE_SEP);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ onNewLine = true;
+ aligned = false;
+ needsNewLine = false;
+ }
+
+ private void println(JCTree completed) {
+ if (completed != null) {
+ int endPos = endPos(completed);
+ consumeTrailingComments(endPos);
+ }
+ try {
+ out.write(LINE_SEP);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ onNewLine = true;
+ aligned = false;
+ needsNewLine = false;
+ }
+
+ private void println(CharSequence s) {
+ print(s);
+ println();
+ }
+
+ private void println(CharSequence s, JCTree completed) {
+ print(s);
+ println(completed);
+ }
+
+ private void aPrint(CharSequence s) {
+ align();
+ print(s);
+ }
+
+ private void aPrintln(CharSequence s) {
+ align();
+ print(s);
+ println();
+ }
+
+ private void aPrintln(CharSequence s, JCTree completed) {
+ align();
+ print(s);
+ println(completed);
+ }
+
+ private void consumeComments(int until) {
+ CommentInfo head = comments.head;
+ while (comments.nonEmpty() && head.pos < until) {
+ printComment(head);
+ comments = comments.tail;
+ head = comments.head;
+ }
+ }
+
+ private void consumeComments(JCTree tree) {
+ consumeComments(tree.pos);
+ }
+
+ private void consumeTrailingComments(int from) {
+ boolean prevNewLine = onNewLine;
+ CommentInfo head = comments.head;
+ boolean stop = false;
+
+ while (comments.nonEmpty() && head.prevEndPos == from && !stop && !(head.start == StartConnection.ON_NEXT_LINE || head.start == StartConnection.START_OF_LINE)) {
+ from = head.endPos;
+ printComment(head);
+ stop = (head.end == EndConnection.ON_NEXT_LINE);
+ comments = comments.tail;
+ head = comments.head;
+ }
+
+ if (!onNewLine && prevNewLine) {
+ println();
+ }
+ }
+
+ private String getJavadocFor(JCTree node) {
+ if (docComments != null) return docComments.get(node);
+ if (docTable != null) return docTable.getCommentText(node);
+ return null;
+ }
+
+ private int dims(JCExpression vartype) {
+ if (vartype instanceof JCArrayTypeTree) {
+ return 1 + dims(((JCArrayTypeTree) vartype).elemtype);
+ }
+
+ return 0;
+ }
+
+ private void printComment(CommentInfo comment) {
+ switch (comment.start) {
+ case DIRECT_AFTER_PREVIOUS:
+ needsSpace = false;
+ break;
+ case AFTER_PREVIOUS:
+ needsSpace = true;
+ break;
+ case START_OF_LINE:
+ needsNewLine = true;
+ needsAlign = false;
+ break;
+ case ON_NEXT_LINE:
+ if (!onNewLine) {
+ needsNewLine = true;
+ needsAlign = true;
+ } else if (!aligned) {
+ needsAlign = true;
+ }
+ break;
+ }
+
+ if (onNewLine && !aligned && comment.start != StartConnection.START_OF_LINE) needsAlign = true;
+
+ print(comment.content);
+
+ switch (comment.end) {
+ case ON_NEXT_LINE:
+ if (!aligned) {
+ needsNewLine = true;
+ needsAlign = true;
+ }
+ break;
+ case AFTER_COMMENT:
+ needsSpace = true;
+ break;
+ case DIRECT_AFTER_COMMENT:
+ // do nothing
+ break;
+ }
+ }
+
+ private void printDocComment(JCTree tree) {
+ String dc = getJavadocFor(tree);
+ if (dc == null) return;
+ aPrintln("/**");
+ int pos = 0;
+ int endpos = lineEndPos(dc, pos);
+ boolean atStart = true;
+ while (pos < dc.length()) {
+ String line = dc.substring(pos, endpos);
+ if (line.trim().isEmpty() && atStart) {
+ atStart = false;
+ continue;
+ }
+ atStart = false;
+ aPrint(" *");
+ if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
+ println(dc.substring(pos, endpos));
+ pos = endpos + 1;
+ endpos = lineEndPos(dc, pos);
+ }
+ aPrintln(" */");
+ }
+
+ private Name __INIT__, __VALUE__;
+ private Name name_init(Name someName) {
+ if (__INIT__ == null) __INIT__ = someName.table.fromChars("<init>".toCharArray(), 0, 6);
+ return __INIT__;
+ }
+ private Name name_value(Name someName) {
+ if (__VALUE__ == null) __VALUE__ = someName.table.fromChars("value".toCharArray(), 0, 5);
+ return __VALUE__;
+ }
+
+ @Override public void visitTopLevel(JCCompilationUnit tree) {
+ printDocComment(tree);
+ JCTree n = PackageName.getPackageNode(tree);
+ if (n != null) {
+ consumeComments(tree);
+ aPrint("package ");
+ print(n);
+ println(";", n);
+ }
+
+ boolean first = true;
+
+ for (JCTree child : tree.defs) {
+ if (!(child instanceof JCImport)) continue;
+ if (first) println();
+ first = false;
+ print(child);
+ }
+
+ for (JCTree child : tree.defs) {
+ if (child instanceof JCImport) continue;
+ print(child);
+ }
+ consumeComments(Integer.MAX_VALUE);
+ }
+
+ @Override public void visitImport(JCImport tree) {
+ aPrint("import ");
+ if (tree.staticImport) print("static ");
+ print(tree.qualid);
+ println(";", tree);
+ }
+
+ private Name currentTypeName;
+ @Override public void visitClassDef(JCClassDecl tree) {
+ println();
+ printDocComment(tree);
+ align();
+ print(tree.mods);
+
+ boolean isInterface = (tree.mods.flags & INTERFACE) != 0;
+ boolean isAnnotationInterface = isInterface && (tree.mods.flags & ANNOTATION) != 0;
+ boolean isEnum = (tree.mods.flags & ENUM) != 0;
+
+ if (isAnnotationInterface) print("@interface ");
+ else if (isInterface) print("interface ");
+ else if (isEnum) print("enum ");
+ else print("class ");
+
+ print(tree.name);
+ Name prevTypeName = currentTypeName;
+ currentTypeName = tree.name;
+
+ if (tree.typarams.nonEmpty()) {
+ print("<");
+ print(tree.typarams, ", ");
+ print(">");
+ }
+ JCTree extendsClause = getExtendsClause(tree);
+ if (extendsClause != null) {
+ print(" extends ");
+ print(extendsClause);
+ }
+
+ if (tree.implementing.nonEmpty()) {
+ print(isInterface ? " extends " : " implements ");
+ print(tree.implementing, ", ");
+ }
+
+ println(" {");
+ indent++;
+ printClassMembers(tree.defs, isEnum, isInterface);
+ consumeComments(endPos(tree));
+ indent--;
+ aPrintln("}", tree);
+ currentTypeName = prevTypeName;
+ }
+
+ private void printClassMembers(List<JCTree> members, boolean isEnum, boolean isInterface) {
+ Class<?> prefType = null;
+ int typeOfPrevEnumMember = isEnum ? 3 : 0; // 1 = normal, 2 = with body, 3 = no enum field yet.
+ boolean prevWasEnumMember = isEnum;
+
+ for (JCTree member : members) {
+ if (typeOfPrevEnumMember == 3 && member instanceof JCMethodDecl && (((JCMethodDecl) member).mods.flags & GENERATEDCONSTR) != 0) continue;
+ boolean isEnumVar = isEnum && member instanceof JCVariableDecl && (((JCVariableDecl) member).mods.flags & ENUM) != 0;
+ if (!isEnumVar && prevWasEnumMember) {
+ prevWasEnumMember = false;
+ if (typeOfPrevEnumMember == 3) align();
+ println(";");
+ }
+
+ if (isEnumVar) {
+ if (prefType != null && prefType != JCVariableDecl.class) println();
+ switch (typeOfPrevEnumMember) {
+ case 1:
+ print(", ");
+ break;
+ case 2:
+ println(",");
+ align();
+ break;
+ }
+ print(member);
+ JCTree init = ((JCVariableDecl) member).init;
+ typeOfPrevEnumMember = init instanceof JCNewClass && ((JCNewClass) init).def != null ? 2 : 1;
+ } else if (member instanceof JCVariableDecl) {
+ if (prefType != null && prefType != JCVariableDecl.class) println();
+ if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC | FINAL);
+ print(member);
+ } else if (member instanceof JCMethodDecl) {
+ if ((((JCMethodDecl) member).mods.flags & GENERATEDCONSTR) != 0) continue;
+ if (prefType != null) println();
+ if (isInterface) flagMod = -1L & ~(PUBLIC | ABSTRACT);
+ print(member);
+ } else if (member instanceof JCClassDecl) {
+ if (prefType != null) println();
+ if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC);
+ print(member);
+ } else {
+ if (prefType != null) println();
+ print(member);
+ }
+
+ prefType = member.getClass();
+ }
+
+ if (prevWasEnumMember) {
+ prevWasEnumMember = false;
+ if (typeOfPrevEnumMember == 3) align();
+ println(";");
+ }
+ }
+
+ @Override public void visitTypeParameter(JCTypeParameter tree) {
+ List<JCExpression> annotations = readObject(tree, "annotations", List.<JCExpression>nil());
+ if (!annotations.isEmpty()) {
+ print(annotations, " ");
+ print(" ");
+ }
+ print(tree.name);
+ if (tree.bounds.nonEmpty()) {
+ print(" extends ");
+ print(tree.bounds, " & ");
+ }
+ consumeComments(tree);
+ }
+
+ @Override public void visitVarDef(JCVariableDecl tree) {
+ printDocComment(tree);
+ align();
+ if ((tree.mods.flags & ENUM) != 0) {
+ printEnumMember(tree);
+ return;
+ }
+ printAnnotations(tree.mods.annotations, true);
+ printModifierKeywords(tree.mods);
+ printVarDef0(tree);
+ println(";", tree);
+ }
+
+ private void printVarDefInline(JCVariableDecl tree) {
+ printAnnotations(tree.mods.annotations, false);
+ printModifierKeywords(tree.mods);
+ printVarDef0(tree);
+ }
+
+ private void printVarDef0(JCVariableDecl tree) {
+ boolean varargs = (tree.mods.flags & VARARGS) != 0;
+ if (varargs && tree.vartype instanceof JCArrayTypeTree) {
+ print(((JCArrayTypeTree) tree.vartype).elemtype);
+ print("...");
+ } else {
+ print(tree.vartype);
+ }
+ print(" ");
+ print(tree.name);
+ if (tree.init != null) {
+ print(" = ");
+ print(tree.init);
+ }
+ }
+
+ private void printEnumMember(JCVariableDecl tree) {
+ printAnnotations(tree.mods.annotations, true);
+ print(tree.name);
+ if (tree.init instanceof JCNewClass) {
+ JCNewClass constructor = (JCNewClass) tree.init;
+ if (constructor.args != null && constructor.args.nonEmpty()) {
+ print("(");
+ print(constructor.args, ", ");
+ print(")");
+ }
+
+ if (constructor.def != null && constructor.def.defs != null) {
+ println(" {");
+ indent++;
+ printClassMembers(constructor.def.defs, false, false);
+ consumeComments(endPos(tree));
+ indent--;
+ aPrint("}");
+ }
+ }
+ }
+
+ // TODO: Test postfix syntax for methods (?), for decls. Multiline vardefs, possibly with comments. enums with bodies. constructor-local generics, method-local generics, also do/while, finally, try-with-resources, lambdas, annotations in java8 places...
+ // TODO: Whatever is JCAnnotatedType? We handle it in the 7+ bucket in the old one...
+
+ @Override public void visitTypeApply(JCTypeApply tree) {
+ print(tree.clazz);
+ print("<");
+ print(tree.arguments, ", ");
+ print(">");
+ }
+
+ @Override public void visitWildcard(JCWildcard tree) {
+ switch (tree.getKind()) {
+ default:
+ case UNBOUNDED_WILDCARD:
+ print("?");
+ return;
+ case EXTENDS_WILDCARD:
+ print("? extends ");
+ print(tree.inner);
+ return;
+ case SUPER_WILDCARD:
+ print("? super ");
+ print(tree.inner);
+ return;
+ }
+ }
+
+ @Override public void visitLiteral(JCLiteral tree) {
+ TypeTag typeTag = typeTag(tree);
+ if (CTC_INT.equals(typeTag)) print("" + tree.value);
+ else if (CTC_LONG.equals(typeTag)) print(tree.value + "L");
+ else if (CTC_FLOAT.equals(typeTag)) print(tree.value + "F");
+ else if (CTC_DOUBLE.equals(typeTag)) print("" + tree.value);
+ else if (CTC_CHAR.equals(typeTag)) {
+ print("\'" + quoteChar((char)((Number)tree.value).intValue()) + "\'");
+ }
+ else if (CTC_BOOLEAN.equals(typeTag)) print(((Number)tree.value).intValue() == 1 ? "true" : "false");
+ else if (CTC_BOT.equals(typeTag)) print("null");
+ else print("\"" + quoteChars(tree.value.toString()) + "\"");
+ }
+
+ @Override public void visitMethodDef(JCMethodDecl tree) {
+ boolean isConstructor = tree.name == name_init(tree.name);
+ if (isConstructor && (tree.mods.flags & GENERATEDCONSTR) != 0) return;
+ printDocComment(tree);
+ align();
+ print(tree.mods);
+ if (tree.typarams != null && tree.typarams.nonEmpty()) {
+ print("<");
+ print(tree.typarams, ", ");
+ print("> ");
+ }
+
+ if (isConstructor) {
+ print(currentTypeName == null ? "<init>" : currentTypeName);
+ } else {
+ print(tree.restype);
+ print(" ");
+ print(tree.name);
+ }
+
+ print("(");
+ boolean first = true;
+ for (JCVariableDecl param : tree.params) {
+ if (!first) print(", ");
+ first = false;
+ printVarDefInline(param);
+ }
+ print(")");
+
+ if (tree.thrown.nonEmpty()) {
+ print(" throws ");
+ print(tree.thrown, ", ");
+ }
+
+ if (tree.defaultValue != null) {
+ print(" default ");
+ print(tree.defaultValue);
+ }
+
+ if (tree.body != null) {
+ print(" ");
+ print(tree.body);
+ } else println(";", tree);
+ }
+
+ @Override public void visitSkip(JCSkip that) {
+ if (onNewLine && !aligned) {
+ align();
+ }
+ println(";");
+ }
+
+ @Override public void visitAnnotation(JCAnnotation tree) {
+ print("@");
+ print(tree.annotationType);
+ if (tree.args.isEmpty()) return;
+ print("(");
+ boolean done = false;
+ if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) {
+ JCAssign arg1 = (JCAssign) tree.args.get(0);
+ JCIdent arg1Name = arg1.lhs instanceof JCIdent ? ((JCIdent) arg1.lhs) : null;
+ if (arg1Name != null && arg1Name.name == name_value(arg1Name.name)) {
+ print(arg1.rhs);
+ done = true;
+ }
+ }
+ if (!done) print(tree.args, ", ");
+ print(")");
+ }
+
+ @Override public void visitTypeArray(JCArrayTypeTree tree) {
+ JCTree elem = tree.elemtype;
+ while (elem instanceof JCWildcard) elem = ((JCWildcard) elem).inner;
+ print(elem);
+ print("[]");
+ }
+
+ @Override public void visitNewArray(JCNewArray tree) {
+ JCTree elem = tree.elemtype;
+ int dims = 0;
+ if (elem != null) {
+ print("new ");
+
+ while (elem instanceof JCArrayTypeTree) {
+ dims++;
+ elem = ((JCArrayTypeTree) elem).elemtype;
+ }
+ print(elem);
+
+ for (JCExpression expr : tree.dims) {
+ print("[");
+ print(expr);
+ print("]");
+ }
+ }
+
+ for (int i = 0; i < dims; i++) print("[]");
+
+ if (tree.elems != null) {
+ if (elem != null) print("[] ");
+ print("{");
+ print(tree.elems, ", ");
+ print("}");
+ }
+ }
+
+ @Override public void visitNewClass(JCNewClass tree) {
+ if (tree.encl != null) {
+ print(tree.encl);
+ print(".");
+ }
+ boolean moveFirstParameter = tree.args.nonEmpty() && tree.args.head instanceof JCUnary && tree.args.head.toString().startsWith("<*nullchk*>");
+ if (moveFirstParameter) {
+ print(((JCUnary) tree.args.head).arg);
+ print(".");
+ }
+
+ print("new ");
+ if (!tree.typeargs.isEmpty()) {
+ print("<");
+ print(tree.typeargs, ", ");
+ print(">");
+ }
+ print(tree.clazz);
+ print("(");
+ if (moveFirstParameter) {
+ print(tree.args.tail, ", ");
+ } else {
+ print(tree.args, ", ");
+ }
+ print(")");
+ if (tree.def != null) {
+ Name previousTypeName = currentTypeName;
+ currentTypeName = null;
+ println(" {");
+ indent++;
+ print(tree.def.defs, "");
+ indent--;
+ aPrint("}");
+ currentTypeName = previousTypeName;
+ }
+ }
+
+ @Override public void visitIndexed(JCArrayAccess tree) {
+ print(tree.indexed);
+ print("[");
+ print(tree.index);
+ print("]");
+ }
+
+ @Override public void visitTypeIdent(JCPrimitiveTypeTree tree) {
+ TypeTag typeTag = typeTag(tree);
+
+ if (CTC_BYTE.equals(typeTag)) print("byte");
+ else if (CTC_CHAR.equals(typeTag)) print("char");
+ else if (CTC_SHORT.equals(typeTag)) print("short");
+ else if (CTC_INT.equals(typeTag)) print("int");
+ else if (CTC_LONG.equals(typeTag)) print("long");
+ else if (CTC_FLOAT.equals(typeTag)) print("float");
+ else if (CTC_DOUBLE.equals(typeTag)) print("double");
+ else if (CTC_BOOLEAN.equals(typeTag)) print("boolean");
+ else if (CTC_VOID.equals(typeTag)) print("void");
+ else print("error");
+ }
+
+ @Override public void visitLabelled(JCLabeledStatement tree) {
+ aPrint(tree.label);
+ print(":");
+ if (tree.body instanceof JCSkip || suppress(tree)) {
+ println(" ;", tree);
+ } else if (tree.body instanceof JCBlock) {
+ print(" ");
+ print(tree.body);
+ } else {
+ println(tree);
+ print(tree.body);
+ }
+ }
+
+ private long flagMod = -1L;
+ private static final long DEFAULT = 1L<<43;
+
+ @Override public void visitModifiers(JCModifiers tree) {
+ printAnnotations(tree.annotations, true);
+ printModifierKeywords(tree);
+ }
+
+ private void printAnnotations(List<JCAnnotation> annotations, boolean newlines) {
+ for (JCAnnotation ann : annotations) {
+ print(ann);
+ if (newlines) {
+ println();
+ align();
+ } else print(" ");
+ }
+ }
+
+ private void printModifierKeywords(JCModifiers tree) {
+ long v = flagMod & tree.flags;
+ flagMod = -1L;
+
+ if ((v & SYNTHETIC) != 0) print("/* synthetic */ ");
+ if ((v & PUBLIC) != 0) print("public ");
+ if ((v & PRIVATE) != 0) print("private ");
+ if ((v & PROTECTED) != 0) print("protected ");
+ if ((v & STATIC) != 0) print("static ");
+ if ((v & FINAL) != 0) print("final ");
+ if ((v & SYNCHRONIZED) != 0) print("synchronized ");
+ if ((v & VOLATILE) != 0) print("volatile ");
+ if ((v & TRANSIENT) != 0) print("transient ");
+ if ((v & NATIVE) != 0) print("native ");
+ if ((v & ABSTRACT) != 0) print("abstract ");
+ if ((v & STRICTFP) != 0) print("strictfp ");
+ if ((v & DEFAULT) != 0 && (v & INTERFACE) == 0) print("default ");
+ }
+
+ @Override public void visitSelect(JCFieldAccess tree) {
+ print(tree.selected);
+ print(".");
+ print(tree.name);
+ }
+
+ @Override public void visitIdent(JCIdent tree) {
+ print(tree.name);
+ }
+
+ @Override public void visitApply(JCMethodInvocation tree) {
+ if (tree.typeargs.nonEmpty()) {
+ if (tree.meth instanceof JCFieldAccess) {
+ JCFieldAccess fa = (JCFieldAccess) tree.meth;
+ print(fa.selected);
+ print(".<");
+ print(tree.typeargs, ", ");
+ print(">");
+ print(fa.name);
+ } else {
+ print("<");
+ print(tree.typeargs, ", ");
+ print(">");
+ print(tree.meth);
+ }
+ } else {
+ print(tree.meth);
+ }
+
+ print("(");
+ print(tree.args, ", ");
+ print(")");
+ }
+
+ @Override public void visitAssert(JCAssert tree) {
+ aPrint("assert ");
+ print(tree.cond);
+ if (tree.detail != null) {
+ print(" : ");
+ print(tree.detail);
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitAssign(JCAssign tree) {
+ print(tree.lhs);
+ print(" = ");
+ print(tree.rhs);
+ }
+
+ @Override public void visitAssignop(JCAssignOp tree) {
+ print(tree.lhs);
+ String opname = operator(treeTag(tree));
+ print(" " + opname + " ");
+ print(tree.rhs);
+ }
+
+ private static final int PREFIX = 14;
+
+ @Override public void visitUnary(JCUnary tree) {
+ String op = operator(treeTag(tree));
+ if (treeTag(tree).getOperatorPrecedenceLevel() == PREFIX) {
+ print(op);
+ print(tree.arg);
+ } else {
+ print(tree.arg);
+ print(op);
+ }
+ }
+
+ @Override public void visitBinary(JCBinary tree) {
+ String op = operator(treeTag(tree));
+ print(tree.lhs);
+ print(" ");
+ print(op);
+ print(" ");
+ print(tree.rhs);
+ }
+
+ @Override public void visitTypeTest(JCInstanceOf tree) {
+ print(tree.expr);
+ print(" instanceof ");
+ print(tree.clazz);
+ }
+
+ @Override public void visitTypeCast(JCTypeCast tree) {
+ print("(");
+ print(tree.clazz);
+ print(") ");
+ print(tree.expr);
+ }
+
+ @Override public void visitBlock(JCBlock tree) {
+ if (tree.pos == Position.NOPOS && tree.stats.isEmpty()) return;
+ if (onNewLine) align();
+ if ((tree.flags & STATIC) != 0) print("static ");
+ println("{");
+ indent++;
+ print(tree.stats, "");
+ consumeComments(endPos(tree));
+ indent--;
+ aPrintln("}", tree);
+ }
+
+ @Override public void visitBreak(JCBreak tree) {
+ aPrint("break");
+ if (tree.label != null) {
+ print(" ");
+ print(tree.label);
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitContinue(JCContinue tree) {
+ aPrint("continue");
+ if (tree.label != null) {
+ print(" ");
+ print(tree.label);
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitConditional(JCConditional tree) {
+ print(tree.cond);
+ print(" ? ");
+ print(tree.truepart);
+ print(" : ");
+ print(tree.falsepart);
+ }
+
+ @Override public void visitParens(JCParens tree) {
+ print("(");
+ print(tree.expr);
+ print(")");
+ }
+
+ @Override public void visitReturn(JCReturn tree) {
+ aPrint("return");
+ if (tree.expr != null) {
+ print(" ");
+ print(tree.expr);
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitThrow(JCThrow tree) {
+ aPrint("throw ");
+ print(tree.expr);
+ println(";", tree);
+ }
+
+ @Override public void visitWhileLoop(JCWhileLoop tree) {
+ aPrint("while ");
+ if (tree.cond instanceof JCParens) {
+ print(tree.cond);
+ } else {
+ print("(");
+ print(tree.cond);
+ print(")");
+ }
+ print(" ");
+ print(tree.body);
+ // make sure to test while (true) ; and while(true){} and while(true) x = 5;
+ }
+
+ @Override public void visitForLoop(JCForLoop tree) {
+ aPrint("for (");
+ if (tree.init.nonEmpty()) {
+ // ForInit is either a StatementExpressionList or a LocalVariableDeclaration
+ if (tree.init.head instanceof JCVariableDecl) {
+ boolean first = true;
+ int dims = 0;
+ for (JCStatement i : tree.init) {
+ JCVariableDecl vd = (JCVariableDecl) i;
+ if (first) {
+ printVarDefInline(vd);
+ dims = dims(vd.vartype);
+ } else {
+ print(", ");
+ print(vd.name);
+ int dimDiff = dims(vd.vartype) - dims;
+ for (int j = 0; j < dimDiff; j++) print("[]");
+ if (vd.init != null) {
+ print(" = ");
+ print(vd.init);
+ }
+ }
+ first = false;
+ }
+ } else {
+ boolean first = true;
+ for (JCStatement exprStatement : tree.init) {
+ if (!first) print(", ");
+ first = false;
+ print(((JCExpressionStatement) exprStatement).expr);
+ }
+ }
+ }
+ print("; ");
+ if (tree.cond != null) print(tree.cond);
+ print("; ");
+ boolean first = true;
+ for (JCExpressionStatement exprStatement : tree.step) {
+ if (!first) print(", ");
+ first = false;
+ print(exprStatement.expr);
+ }
+ print(") ");
+ print(tree.body);
+ }
+
+ @Override public void visitForeachLoop(JCEnhancedForLoop tree) {
+ aPrint("for (");
+ printVarDefInline(tree.var);
+ print(" : ");
+ print(tree.expr);
+ print(") ");
+ print(tree.body);
+ }
+
+ @Override public void visitIf(JCIf tree) {
+ aPrint("if ");
+ if (tree.cond instanceof JCParens) {
+ print(tree.cond);
+ } else {
+ print("(");
+ print(tree.cond);
+ print(")");
+ }
+ print(" ");
+ if (tree.thenpart instanceof JCBlock) {
+ println("{");
+ indent++;
+ print(((JCBlock) tree.thenpart).stats, "");
+ indent--;
+ if (tree.elsepart == null) {
+ aPrintln("}", tree);
+ } else {
+ aPrint("}");
+ }
+ } else {
+ print(tree.thenpart);
+ }
+ if (tree.elsepart != null) {
+ aPrint(" else ");
+ print(tree.elsepart);
+ }
+ }
+
+ @Override public void visitExec(JCExpressionStatement tree) {
+ align();
+ print(tree.expr);
+ println(";", tree);
+ }
+
+ @Override public void visitDoLoop(JCDoWhileLoop tree) {
+ aPrint("do ");
+ if (tree.body instanceof JCBlock) {
+ println("{");
+ indent++;
+ print(((JCBlock) tree.body).stats, "");
+ indent--;
+ aPrint("}");
+
+ } else print(tree.body);
+ print(" while ");
+ if (tree.cond instanceof JCParens) {
+ print(tree.cond);
+ } else {
+ print("(");
+ print(tree.cond);
+ print(")");
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitSynchronized(JCSynchronized tree) {
+ aPrint("synchronized ");
+ if (tree.lock instanceof JCParens) {
+ print(tree.lock);
+ } else {
+ print("(");
+ print(tree.lock);
+ print(")");
+ }
+ print(" ");
+ print(tree.body);
+ }
+
+ @Override public void visitCase(JCCase tree) {
+ if (tree.pat == null) {
+ aPrint("default");
+ } else {
+ aPrint("case ");
+ print(tree.pat);
+ }
+ println(": ");
+ indent++;
+ print(tree.stats, "");
+ indent--;
+ }
+
+ @Override public void visitCatch(JCCatch tree) {
+ print(" catch (");
+ print(tree.param);
+ print(") ");
+ print(tree.body);
+ }
+
+ @Override public void visitSwitch(JCSwitch tree) {
+ aPrint("switch ");
+ if (tree.selector instanceof JCParens) {
+ print(tree.selector);
+ } else {
+ print("(");
+ print(tree.selector);
+ print(")");
+ }
+ println(" {");
+ print(tree.cases, "\n");
+ aPrintln("}", tree);
+ }
+
+ @Override public void visitTry(JCTry tree) {
+ aPrint("try ");
+ List<?> resources = readObject(tree, "resources", List.nil());
+ int len = resources.length();
+ switch (len) {
+ case 0:
+ break;
+ case 1:
+ print("(");
+ JCVariableDecl decl = (JCVariableDecl) resources.get(0);
+ flagMod = -1L & ~FINAL;
+ printVarDefInline(decl);
+ print(") ");
+ break;
+ default:
+ println("(");
+ indent++;
+ int c = 0;
+ for (Object i : resources) {
+ align();
+ flagMod = -1L & ~FINAL;
+ printVarDefInline((JCVariableDecl) i);
+ if (++c == len) {
+ print(") ");
+ } else {
+ println(";", (JCTree) i);
+ }
+ }
+ indent--;
+ }
+ println("{");
+ indent++;
+ for (JCStatement stat : tree.body.stats) print(stat);
+ indent--;
+ aPrint("}");
+ for (JCCatch catchBlock : tree.catchers) {
+ printCatch(catchBlock);
+ }
+ if (tree.finalizer != null) {
+ println(" finally {");
+ indent++;
+ for (JCStatement stat : tree.finalizer.stats) print(stat);
+ indent--;
+ aPrint("}");
+ }
+ println(tree);
+ }
+
+ private void printCatch(JCCatch catchBlock) {
+ print(" catch (");
+ printVarDefInline(catchBlock.param); // ExprType1 | ExprType2 handled via JCTypeUnion.
+ println(") {");
+ indent++;
+ for (JCStatement stat : catchBlock.body.stats) print(stat);
+ indent--;
+ aPrint("}");
+ }
+
+ public void visitErroneous(JCErroneous tree) {
+ print("(ERROR)");
+ }
+
+ private static String operator(TreeTag tag) {
+ String op = OPERATORS.get(tag);
+ if (op == null) return "(?op?)";
+ return op;
+ }
+
+ private static String quoteChars(String s) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < s.length(); i++) sb.append(quoteChar(s.charAt(i)));
+ return sb.toString();
+ }
+
+ private static String quoteChar(char ch) {
+ switch (ch) {
+ case '\b': return "\\b";
+ case '\f': return "\\f";
+ case '\n': return "\\n";
+ case '\r': return "\\r";
+ case '\t': return "\\t";
+ case '\'': return "\\'";
+ case '\"': return "\\\"";
+ case '\\': return "\\\\";
+ default:
+ if (ch < 32) return String.format("\\%03o", (int) ch);
+ return String.valueOf(ch);
+ }
+ }
+
+ private static final Method getExtendsClause, getEndPosition, storeEnd;
+
+ static {
+ getExtendsClause = getMethod(JCClassDecl.class, "getExtendsClause", new Class<?>[0]);
+ getExtendsClause.setAccessible(true);
+
+ if (getJavaCompilerVersion() < 8) {
+ getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class);
+ storeEnd = getMethod(java.util.Map.class, "put", Object.class, Object.class);
+ } else {
+ getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", "com.sun.tools.javac.tree.EndPosTable");
+ Method storeEndMethodTemp;
+ Class<?> endPosTable;
+ try {
+ endPosTable = Class.forName("com.sun.tools.javac.tree.EndPosTable");
+ } catch (ClassNotFoundException ex) {
+ throw sneakyThrow(ex);
+ }
+ try {
+ storeEndMethodTemp = endPosTable.getMethod("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);
+ } catch (NoSuchMethodException ex) {
+ throw sneakyThrow(ex);
+ } catch (ClassNotFoundException ex) {
+ throw sneakyThrow(ex);
+ }
+ }
+ storeEnd = storeEndMethodTemp;
+ }
+ getEndPosition.setAccessible(true);
+ storeEnd.setAccessible(true);
+ }
+
+ private static Method getMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
+ try {
+ return clazz.getMethod(name, paramTypes);
+ } catch (NoSuchMethodException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ private static Method getMethod(Class<?> clazz, String name, String... paramTypes) {
+ 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);
+ } catch (NoSuchMethodException e) {
+ throw sneakyThrow(e);
+ } catch (ClassNotFoundException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ public static JCTree getExtendsClause(JCClassDecl decl) {
+ try {
+ return (JCTree) getExtendsClause.invoke(decl);
+ } catch (IllegalAccessException e) {
+ throw sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw sneakyThrow(e.getCause());
+ }
+ }
+
+ static RuntimeException sneakyThrow(Throwable t) {
+ if (t == null) throw new NullPointerException("t");
+ PrettyPrinter.<RuntimeException>sneakyThrow0(t);
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
+ throw (T)t;
+ }
+
+ private static final Map<Class<?>, Map<String, Field>> reflectionCache = new HashMap<Class<?>, Map<String, Field>>();
+
+ @SuppressWarnings("unchecked")
+ private <T> T readObject(JCTree tree, String fieldName, T defaultValue) {
+ Class<?> tClass = tree.getClass();
+ Map<String, Field> c = reflectionCache.get(tClass);
+ if (c == null) reflectionCache.put(tClass, c = new HashMap<String, Field>());
+ Field f = c.get(fieldName);
+ if (f == null) {
+ try {
+ f = tClass.getDeclaredField(fieldName);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ f.setAccessible(true);
+ c.put(fieldName, f);
+ }
+
+ try {
+ return (T) f.get(tree);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ public void visitTypeBoundKind(TypeBoundKind tree) {
+ print(String.valueOf(tree.kind));
+ }
+
+ @Override public void visitTree(JCTree tree) {
+ String simpleName = tree.getClass().getSimpleName();
+ if ("JCTypeUnion".equals(simpleName)) {
+ List<JCExpression> types = readObject(tree, "alternatives", List.<JCExpression>nil());
+ print(types, " | ");
+ } else if ("JCTypeIntersection".equals(simpleName)) {
+ print(readObject(tree, "bounds", List.<JCExpression>nil()), " & ");
+ } else if ("JCMemberReference".equals(simpleName)) {
+ printMemberReference0(tree);
+ } else if ("JCLambda".equals(simpleName)) {
+ printLambda0(tree);
+ } else if ("JCAnnotatedType".equals(simpleName)) {
+ printAnnotatedType0(tree);
+ } else if ("JCPackageDecl".equals(simpleName)) {
+ // Starting with JDK9, this is inside the import list, but we've already printed it. Just ignore it.
+ } else {
+ throw new AssertionError("Unhandled tree type: " + tree.getClass() + ": " + tree);
+ }
+ }
+
+ private void printMemberReference0(JCTree tree) {
+ print(readObject(tree, "expr", (JCExpression) null));
+ print("::");
+ List<JCExpression> typeArgs = readObject(tree, "typeargs", List.<JCExpression>nil());
+ if (typeArgs != null && !typeArgs.isEmpty()) {
+ print("<");
+ print(typeArgs, ", ");
+ print(">");
+ }
+ print(readObject(tree, "mode", new Object()).toString().equals("INVOKE") ? readObject(tree, "name", (Name) null) : "new");
+ }
+
+ private void printLambda0(JCTree tree) {
+ List<JCVariableDecl> params = readObject(tree, "params", List.<JCVariableDecl>nil());
+ boolean explicit = true;
+ int paramLength = params.size();
+ try {
+ explicit = readObject(tree, "paramKind", new Object()).toString().equals("EXPLICIT");
+ } catch (Exception e) {}
+ boolean useParens = paramLength != 1 || explicit;
+ if (useParens) print("(");
+ if (explicit) {
+ boolean first = true;
+ for (JCVariableDecl vd : params) {
+ if (!first) print(", ");
+ first = false;
+ printVarDefInline(vd);
+ }
+ } else {
+ String sep = "";
+ for (JCVariableDecl param : params) {
+ print(sep);
+ print(param.name);
+ sep = ", ";
+ }
+ }
+ if (useParens) print(")");
+ print(" -> ");
+ JCTree body = readObject(tree, "body", (JCTree) null);
+ if (body instanceof JCBlock) {
+ println("{");
+ indent++;
+ print(((JCBlock) body).stats, "");
+ indent--;
+ aPrint("}");
+ } else {
+ print(body);
+ }
+ }
+
+ private void printAnnotatedType0(JCTree tree) {
+ JCTree underlyingType = readObject(tree, "underlyingType", (JCTree) null);
+ if (underlyingType instanceof JCFieldAccess) {
+ print(((JCFieldAccess) underlyingType).selected);
+ print(".");
+ print(readObject(tree, "annotations", List.<JCExpression>nil()), " ");
+ print(" ");
+ print(((JCFieldAccess) underlyingType).name);
+ } else {
+ print(readObject(tree, "annotations", List.<JCExpression>nil()), " ");
+ print(" ");
+ print(underlyingType);
+ }
+ }
+}
diff --git a/src/delombok/lombok/delombok/UnicodeEscapeWriter.java b/src/delombok/lombok/delombok/UnicodeEscapeWriter.java
index 95b55078..9c2ef190 100644
--- a/src/delombok/lombok/delombok/UnicodeEscapeWriter.java
+++ b/src/delombok/lombok/delombok/UnicodeEscapeWriter.java
@@ -63,6 +63,6 @@ public class UnicodeEscapeWriter extends Writer {
}
protected void writeUnicodeEscape(char c) throws IOException {
- writer.write("\\u" + Integer.toHexString(c));
+ writer.write(String.format("\\u%04x", (int) c));
}
} \ No newline at end of file
diff --git a/src/delombok/lombok/delombok/ant/DelombokTask.java b/src/delombok/lombok/delombok/ant/DelombokTask.java
index d5c17fe7..06bbe3e0 100644
--- a/src/delombok/lombok/delombok/ant/DelombokTask.java
+++ b/src/delombok/lombok/delombok/ant/DelombokTask.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,142 +22,189 @@
package lombok.delombok.ant;
import java.io.File;
-import java.io.IOException;
-import java.nio.charset.UnsupportedCharsetException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
-import lombok.delombok.Delombok;
-import lombok.delombok.Delombok.InvalidFormatOptionException;
+import lombok.Lombok;
import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
-import org.apache.tools.ant.types.resources.FileResource;
-public class DelombokTask extends Task {
- private File fromDir, toDir;
- private Path classpath;
- private Path sourcepath;
- private boolean verbose;
- private String encoding;
- private Path path;
- private List<Format> formatOptions = new ArrayList<Format>();
-
- public void setClasspath(Path classpath) {
- if (this.classpath == null) {
- this.classpath = classpath;
- } else {
- this.classpath.append(classpath);
+@SuppressWarnings("unused") // we use reflection to transfer fields.
+class Tasks {
+ public static class Format {
+ private String value;
+
+ @Override public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
}
- }
-
- public Path createClasspath() {
- if (classpath == null) {
- classpath = new Path(getProject());
+
+ @Override public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Format other = (Format) obj;
+ if (value == null) {
+ if (other.value != null) return false;
+ } else if (!value.equals(other.value)) return false;
+ return true;
}
- return classpath.createPath();
- }
-
- public void setClasspathRef(Reference r) {
- createClasspath().setRefid(r);
- }
-
- public void setSourcepath(Path sourcepath) {
- if (this.sourcepath == null) {
- this.sourcepath = sourcepath;
- } else {
- this.sourcepath.append(sourcepath);
+
+ @Override public String toString() {
+ return "FormatOption [value=" + value + "]";
}
- }
-
- public Path createSourcepath() {
- if (sourcepath == null) {
- sourcepath = new Path(getProject());
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
}
- return sourcepath.createPath();
- }
-
- public void setSourcepathRef(Reference r) {
- createSourcepath().setRefid(r);
- }
-
- public void setFrom(File dir) {
- this.fromDir = dir;
- }
-
- public void setTo(File dir) {
- this.toDir = dir;
- }
-
- public void setVerbose(boolean verbose) {
- this.verbose = verbose;
- }
-
- public void setEncoding(String encoding) {
- this.encoding = encoding;
- }
-
- public void addFileset(FileSet set) {
- if (path == null) path = new Path(getProject());
- path.add(set);
- }
-
- public void addFormat(Format format) {
- formatOptions.add(format);
}
- @Override
- public void execute() throws BuildException {
- if (fromDir == null && path == null) throw new BuildException("Either 'from' attribute, or nested <fileset> tags are required.");
- if (fromDir != null && path != null) throw new BuildException("You can't specify both 'from' attribute and nested filesets. You need one or the other.");
- if (toDir == null) throw new BuildException("The to attribute is required.");
-
- Delombok delombok = new Delombok();
- if (verbose) delombok.setVerbose(true);
- try {
- if (encoding != null) delombok.setCharset(encoding);
- } catch (UnsupportedCharsetException e) {
- throw new BuildException("Unknown charset: " + encoding, getLocation());
- }
-
- if (classpath != null) delombok.setClasspath(classpath.toString());
- if (sourcepath != null) delombok.setSourcepath(sourcepath.toString());
-
- try {
- List<String> fo = new ArrayList<String>();
- for (Format f : formatOptions) {
- String v = f.getValue();
- if (v == null) throw new BuildException("'value' property required for <format>");
- fo.add(v);
+ public static class Delombok extends Task {
+ private File fromDir, toDir;
+ private Path classpath;
+ private Path sourcepath;
+ private boolean verbose;
+ private String encoding;
+ private Path path;
+ private List<Format> formatOptions = new ArrayList<Format>();
+
+ public void setClasspath(Path classpath) {
+ if (this.classpath == null) {
+ this.classpath = classpath;
+ } else {
+ this.classpath.append(classpath);
+ }
+ }
+
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(getProject());
}
- delombok.setFormatPreferences(Delombok.formatOptionsToMap(fo));
- } catch (InvalidFormatOptionException e) {
- throw new BuildException(e.getMessage() + " Run java -jar lombok.jar --format-help for detailed format help.");
- }
-
- delombok.setOutput(toDir);
- try {
- if (fromDir != null) delombok.addDirectory(fromDir);
- else {
- Iterator<?> it = path.iterator();
- while (it.hasNext()) {
- FileResource fileResource = (FileResource) it.next();
- File baseDir = fileResource.getBaseDir();
- if (baseDir == null) {
- File file = fileResource.getFile();
- delombok.addFile(file.getParentFile(), file.getName());
+ return classpath.createPath();
+ }
+
+ public void setClasspathRef(Reference r) {
+ createClasspath().setRefid(r);
+ }
+
+ public void setSourcepath(Path sourcepath) {
+ if (this.sourcepath == null) {
+ this.sourcepath = sourcepath;
+ } else {
+ this.sourcepath.append(sourcepath);
+ }
+ }
+
+ public Path createSourcepath() {
+ if (sourcepath == null) {
+ sourcepath = new Path(getProject());
+ }
+ return sourcepath.createPath();
+ }
+
+ public void setSourcepathRef(Reference r) {
+ createSourcepath().setRefid(r);
+ }
+
+ public void setFrom(File dir) {
+ this.fromDir = dir;
+ }
+
+ public void setTo(File dir) {
+ this.toDir = dir;
+ }
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public void addFileset(FileSet set) {
+ if (path == null) path = new Path(getProject());
+ path.add(set);
+ }
+
+ public Format createFormat() {
+ return new Format();
+ }
+
+ public void addFormat(Format format) {
+ formatOptions.add(format);
+ }
+
+ private static ClassLoader shadowLoader;
+
+ public static Class<?> shadowLoadClass(String name) {
+ try {
+ if (shadowLoader == null) {
+ try {
+ Class.forName("lombok.core.LombokNode");
+ // If we get here, then lombok is already available.
+ shadowLoader = Delombok.class.getClassLoader();
+ } 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);
+ shadowLoader = (ClassLoader) m.invoke(null);
+ }
+ }
+
+ return Class.forName(name, true, shadowLoader);
+ } catch (Exception e) {
+ throw Lombok.sneakyThrow(e);
+ }
+ }
+
+ @Override
+ public void execute() throws BuildException {
+ Location loc = getLocation();
+
+ try {
+ Object instance = shadowLoadClass("lombok.delombok.ant.DelombokTaskImpl").newInstance();
+ for(Field selfField : getClass().getDeclaredFields()) {
+ if (selfField.isSynthetic() || Modifier.isStatic(selfField.getModifiers())) continue;
+ Field otherField = instance.getClass().getDeclaredField(selfField.getName());
+ otherField.setAccessible(true);
+ selfField.setAccessible(true);
+ if (selfField.getName().equals("formatOptions")) {
+ List<String> rep = new ArrayList<String>();
+ for (Format f : formatOptions) {
+ if (f.getValue() == null) throw new BuildException("'value' property required for <format>");
+ rep.add(f.getValue());
+ }
+ otherField.set(instance, rep);
} else {
- delombok.addFile(baseDir, fileResource.getName());
+ otherField.set(instance, selfField.get(this));
}
}
+
+ Method m = instance.getClass().getMethod("execute", Location.class);
+ m.setAccessible(true);
+ m.invoke(instance, loc);
+ } catch (InvocationTargetException e) {
+ throw Lombok.sneakyThrow(e.getCause());
+ } catch (Exception e) {
+ throw Lombok.sneakyThrow(e);
}
- delombok.delombok();
- } catch (IOException e) {
- throw new BuildException("I/O problem during delombok", e, getLocation());
}
}
}
diff --git a/src/delombok/lombok/delombok/ant/DelombokTaskImpl.java b/src/delombok/lombok/delombok/ant/DelombokTaskImpl.java
new file mode 100644
index 00000000..470819cd
--- /dev/null
+++ b/src/delombok/lombok/delombok/ant/DelombokTaskImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.delombok.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import lombok.delombok.Delombok;
+import lombok.delombok.Delombok.InvalidFormatOptionException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.resources.FileResource;
+
+public class DelombokTaskImpl {
+ private File fromDir, toDir;
+ private Path classpath;
+ private Path sourcepath;
+ private boolean verbose;
+ private String encoding;
+ private Path path;
+ private List<String> formatOptions = new ArrayList<String>();
+
+ public void execute(Location location) throws BuildException {
+ if (fromDir == null && path == null) throw new BuildException("Either 'from' attribute, or nested <fileset> tags are required.");
+ if (fromDir != null && path != null) throw new BuildException("You can't specify both 'from' attribute and nested filesets. You need one or the other.");
+ if (toDir == null) throw new BuildException("The to attribute is required.");
+
+ Delombok delombok = new Delombok();
+ if (verbose) delombok.setVerbose(true);
+ try {
+ if (encoding != null) delombok.setCharset(encoding);
+ } catch (UnsupportedCharsetException e) {
+ throw new BuildException("Unknown charset: " + encoding, location);
+ }
+
+ if (classpath != null) delombok.setClasspath(classpath.toString());
+ if (sourcepath != null) delombok.setSourcepath(sourcepath.toString());
+
+ try {
+ delombok.setFormatPreferences(Delombok.formatOptionsToMap(formatOptions));
+ } catch (InvalidFormatOptionException e) {
+ throw new BuildException(e.getMessage() + " Run java -jar lombok.jar --format-help for detailed format help.");
+ }
+
+ delombok.setOutput(toDir);
+ try {
+ if (fromDir != null) delombok.addDirectory(fromDir);
+ else {
+ Iterator<?> it = path.iterator();
+ while (it.hasNext()) {
+ FileResource fileResource = (FileResource) it.next();
+ File baseDir = fileResource.getBaseDir();
+ if (baseDir == null) {
+ File file = fileResource.getFile();
+ delombok.addFile(file.getParentFile(), file.getName());
+ } else {
+ delombok.addFile(baseDir, fileResource.getName());
+ }
+ }
+ }
+ delombok.delombok();
+ } catch (IOException e) {
+ throw new BuildException("I/O problem during delombok", e, location);
+ }
+ }
+}
diff --git a/src/delombok/lombok/delombok/ant/Format.java b/src/delombok/lombok/delombok/ant/Format.java
deleted file mode 100644
index bb2b5e87..00000000
--- a/src/delombok/lombok/delombok/ant/Format.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package lombok.delombok.ant;
-
-public class Format {
- private String value;
-
- @Override public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((value == null) ? 0 : value.hashCode());
- return result;
- }
-
- @Override public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
- Format other = (Format) obj;
- if (value == null) {
- if (other.value != null) return false;
- } else if (!value.equals(other.value)) return false;
- return true;
- }
-
- @Override public String toString() {
- return "FormatOption [value=" + value + "]";
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java
new file mode 100644
index 00000000..aa01c13d
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.agent;
+
+import lombok.patcher.ClassRootFinder;
+import lombok.patcher.Hook;
+import lombok.patcher.MethodTarget;
+import lombok.patcher.ScriptManager;
+import lombok.patcher.StackRequest;
+import lombok.patcher.scripts.ScriptBuilder;
+
+public class EclipseLoaderPatcher {
+ private static final String TRANSPLANTS_CLASS_NAME = "lombok.eclipse.agent.EclipseLoaderPatcherTransplants";
+
+ public static void patchEquinoxLoaders(ScriptManager sm, Class<?> launchingContext) {
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader", "loadClass",
+ "java.lang.Class", "java.lang.String", "boolean"))
+ .target(new MethodTarget("org.eclipse.osgi.framework.adapter.core.AbstractClassLoader", "loadClass",
+ "java.lang.Class", "java.lang.String", "boolean"))
+ .target(new MethodTarget("org.eclipse.osgi.internal.loader.ModuleClassLoader", "loadClass",
+ "java.lang.Class", "java.lang.String", "boolean"))
+ .decisionMethod(new Hook(TRANSPLANTS_CLASS_NAME, "overrideLoadDecide", "boolean", "java.lang.ClassLoader", "java.lang.String", "boolean"))
+ .valueMethod(new Hook(TRANSPLANTS_CLASS_NAME, "overrideLoadResult", "java.lang.Class", "java.lang.ClassLoader", "java.lang.String", "boolean"))
+ .transplant()
+ .request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
+
+ sm.addScript(ScriptBuilder.addField().setPublic().setVolatile()
+ .fieldType("Ljava/lang/ClassLoader;")
+ .fieldName("lombok$shadowLoader")
+ .targetClass("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader")
+ .targetClass("org.eclipse.osgi.framework.adapter.core.AbstractClassLoader")
+ .targetClass("org.eclipse.osgi.internal.loader.ModuleClassLoader")
+ .build());
+
+ sm.addScript(ScriptBuilder.addField().setPublic().setVolatile().setStatic()
+ .fieldType("Ljava/lang/Class;")
+ .fieldName("lombok$shadowLoaderClass")
+ .targetClass("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader")
+ .targetClass("org.eclipse.osgi.framework.adapter.core.AbstractClassLoader")
+ .targetClass("org.eclipse.osgi.internal.loader.ModuleClassLoader")
+ .build());
+
+ sm.addScript(ScriptBuilder.addField().setPublic().setStatic().setFinal()
+ .fieldType("Ljava/lang/String;")
+ .fieldName("lombok$location")
+ .targetClass("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader")
+ .targetClass("org.eclipse.osgi.framework.adapter.core.AbstractClassLoader")
+ .targetClass("org.eclipse.osgi.internal.loader.ModuleClassLoader")
+ .value(ClassRootFinder.findClassRootOfClass(launchingContext))
+ .build());
+ }
+}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java
new file mode 100644
index 00000000..ea72f56a
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.agent;
+
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+/**
+ * Contains all the code to be transplanted into eclipse.
+ *
+ * Do not use:
+ *
+ * * Annotations
+ * * Generics
+ * * Varargs
+ * * Auto (un)boxing
+ * * class literals
+ *
+ * The above because this code is compiled with -source 1.4, and is transplanted.
+ *
+ * NB: The suppress warnings will be stripped out before compilation.
+ */
+@SuppressWarnings("all")
+public class EclipseLoaderPatcherTransplants {
+ public static boolean overrideLoadDecide(ClassLoader original, String name, boolean resolve) {
+ return name.startsWith("lombok.");
+ }
+
+ public static Class overrideLoadResult(ClassLoader original, String name, boolean resolve) throws ClassNotFoundException {
+ try {
+ Field shadowLoaderField = original.getClass().getField("lombok$shadowLoader");
+ ClassLoader shadowLoader = (ClassLoader) shadowLoaderField.get(original);
+ if (shadowLoader == null) {
+ synchronized ("lombok$shadowLoader$globalLock".intern()) {
+ shadowLoader = (ClassLoader) shadowLoaderField.get(original);
+ if (shadowLoader == null) {
+ Class shadowClassLoaderClass = (Class) original.getClass().getField("lombok$shadowLoaderClass").get(null);
+ Class classLoaderClass = Class.forName("java.lang.ClassLoader");
+ String jarLoc = (String) original.getClass().getField("lombok$location").get(null);
+ if (shadowClassLoaderClass == null) {
+ JarFile jf = new JarFile(jarLoc);
+ InputStream in = null;
+ try {
+ ZipEntry entry = jf.getEntry("lombok/launch/ShadowClassLoader.class");
+ in = jf.getInputStream(entry);
+ byte[] bytes = new byte[65536];
+ int len = 0;
+ while (true) {
+ int r = in.read(bytes, len, bytes.length - len);
+ if (r == -1) break;
+ len += r;
+ if (len == bytes.length) throw new IllegalStateException("lombok.launch.ShadowClassLoader too large.");
+ }
+ in.close();
+ {
+ Class[] paramTypes = new Class[4];
+ paramTypes[0] = "".getClass();
+ paramTypes[1] = new byte[0].getClass();
+ paramTypes[2] = Integer.TYPE;
+ paramTypes[3] = paramTypes[2];
+ Method defineClassMethod = classLoaderClass.getDeclaredMethod("defineClass", paramTypes);
+ defineClassMethod.setAccessible(true);
+ shadowClassLoaderClass = (Class) defineClassMethod.invoke(original, new Object[] {"lombok.launch.ShadowClassLoader", bytes, new Integer(0), new Integer(len)});
+ original.getClass().getField("lombok$shadowLoaderClass").set(null, shadowClassLoaderClass);
+ }
+ } finally {
+ if (in != null) in.close();
+ jf.close();
+ }
+ }
+ Class[] paramTypes = new Class[5];
+ paramTypes[0] = classLoaderClass;
+ paramTypes[1] = "".getClass();
+ paramTypes[2] = paramTypes[1];
+ paramTypes[3] = Class.forName("java.util.List");
+ paramTypes[4] = paramTypes[3];
+ Constructor constructor = shadowClassLoaderClass.getDeclaredConstructor(paramTypes);
+ constructor.setAccessible(true);
+ shadowLoader = (ClassLoader) constructor.newInstance(new Object[] {original, "lombok", jarLoc, Arrays.asList(new Object[] {"lombok."}), Arrays.asList(new Object[] {"lombok.patcher.Symbols"})});
+ shadowLoaderField.set(original, shadowLoader);
+ }
+ }
+ }
+
+ if (resolve) {
+ Class[] paramTypes = new Class[2];
+ paramTypes[0] = "".getClass();
+ paramTypes[1] = Boolean.TYPE;
+ Method m = shadowLoader.getClass().getDeclaredMethod("loadClass", new Class[] {String.class, boolean.class});
+ m.setAccessible(true);
+ return (Class) m.invoke(shadowLoader, new Object[] {name, Boolean.TRUE});
+ } else {
+ return shadowLoader.loadClass(name);
+ }
+ } catch (Exception ex) {
+ Throwable t = ex;
+ if (t instanceof InvocationTargetException) t = t.getCause();
+ if (t instanceof RuntimeException) throw (RuntimeException) t;
+ if (t instanceof Error) throw (Error) t;
+ throw new RuntimeException(t);
+ }
+ }
+}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
index e14d1367..a6d745b6 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,18 +23,22 @@ package lombok.eclipse.agent;
import static lombok.patcher.scripts.ScriptBuilder.*;
+import java.io.File;
import java.lang.instrument.Instrumentation;
+import java.net.URLClassLoader;
+import java.security.ProtectionDomain;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import lombok.core.Agent;
+import lombok.core.AgentLauncher;
+import lombok.patcher.Filter;
import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
import lombok.patcher.ScriptManager;
import lombok.patcher.StackRequest;
import lombok.patcher.TargetMatcher;
-import lombok.patcher.equinox.EquinoxClassLoader;
+import lombok.patcher.TransplantMapper;
import lombok.patcher.scripts.ScriptBuilder;
/**
@@ -44,13 +48,12 @@ import lombok.patcher.scripts.ScriptBuilder;
* classes in this package for more information about which classes are transformed and how they are
* transformed.
*/
-public class EclipsePatcher extends Agent {
+public class EclipsePatcher implements AgentLauncher.AgentLaunchable {
// At some point I'd like the agent to be capable of auto-detecting if its on eclipse or on ecj. This class is a sure sign we're not in ecj but in eclipse. -ReinierZ
@SuppressWarnings("unused")
private static final String ECLIPSE_SIGNATURE_CLASS = "org/eclipse/core/runtime/adaptor/EclipseStarter";
- @Override
- public void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected) throws Exception {
+ @Override public void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Exception {
String[] args = agentArgs == null ? new String[0] : agentArgs.split(":");
boolean forceEcj = false;
boolean forceEclipse = false;
@@ -69,18 +72,31 @@ public class EclipsePatcher extends Agent {
else if (forceEclipse) ecj = false;
else ecj = injected;
- registerPatchScripts(instrumentation, injected, ecj);
+ registerPatchScripts(instrumentation, injected, ecj, launchingContext);
}
- private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly) {
+ private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly, Class<?> launchingContext) {
ScriptManager sm = new ScriptManager();
sm.registerTransformer(instrumentation);
- if (!ecjOnly) {
- EquinoxClassLoader.addPrefix("lombok.");
- EquinoxClassLoader.registerScripts(sm);
- }
+ sm.setFilter(new Filter() {
+ @Override public boolean shouldTransform(ClassLoader loader, String className, Class<?> classBeingDefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
+ if (!(loader instanceof URLClassLoader)) return true;
+ ClassLoader parent = loader.getParent();
+ if (parent == null) return true;
+ return !parent.getClass().getName().startsWith("org.eclipse.jdt.apt.core.internal.AnnotationProcessorFactoryLoader");
+ }
+ });
+
+ final boolean forceBaseResourceNames = shouldForceBaseResourceNames();
+ sm.setTransplantMapper(new TransplantMapper() {
+ public String mapResourceName(int classFileFormatVersion, String resourceName) {
+ if (classFileFormatVersion < 50 || forceBaseResourceNames) return resourceName;
+ return "Class50/" + resourceName;
+ }
+ });
if (!ecjOnly) {
+ EclipseLoaderPatcher.patchEquinoxLoaders(sm, launchingContext);
patchCatchReparse(sm);
patchIdentifierEndReparse(sm);
patchRetrieveEllipsisStartPosition(sm);
@@ -90,7 +106,7 @@ public class EclipsePatcher extends Agent {
patchHideGeneratedNodes(sm);
patchPostCompileHookEclipse(sm);
patchFixSourceTypeConverter(sm);
- patchDisableLombokForCodeFormatterAndCleanup(sm);
+ patchDisableLombokForCodeCleanup(sm);
patchListRewriteHandleGeneratedMethods(sm);
patchSyntaxAndOccurrencesHighlighting(sm);
patchSortMembersOperation(sm);
@@ -105,16 +121,43 @@ public class EclipsePatcher extends Agent {
patchLombokizeAST(sm);
patchEcjTransformers(sm, ecjOnly);
patchExtensionMethod(sm, ecjOnly);
+ patchRenameField(sm);
if (reloadExistingClasses) sm.reloadClasses(instrumentation);
}
+ private static boolean shouldForceBaseResourceNames() {
+ String shadowOverride = System.getProperty("shadow.override.lombok", "");
+ if (shadowOverride == null || shadowOverride.length() == 0) return false;
+ for (String part : shadowOverride.split("\\s*" + (File.pathSeparatorChar == ';' ? ";" : ":") + "\\s*")) {
+ if (part.equalsIgnoreCase("lombok.jar")) return false;
+ }
+ return true;
+ }
+
+ private static void patchRenameField(ScriptManager sm) {
+ /* RefactoringSearchEngine.search will not return results when renaming field and Data Annotation is present. Return a fake Element to make checks pass */
+ sm.addScript(ScriptBuilder.wrapMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameFieldProcessor", "checkAccessorDeclarations", "org.eclipse.ltk.core.refactoring.RefactoringStatus", "org.eclipse.core.runtime.IProgressMonitor", "org.eclipse.jdt.core.IMethod"))
+ .methodToWrap(new Hook("org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine", "search", "org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup[]", "org.eclipse.jdt.core.search.SearchPattern","org.eclipse.jdt.core.search.IJavaSearchScope","org.eclipse.core.runtime.IProgressMonitor","org.eclipse.ltk.core.refactoring.RefactoringStatus"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "createFakeSearchResult", "org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup[]", "org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup[]", "java.lang.Object"))
+ .requestExtra(StackRequest.THIS)
+ .transplant().build());
+
+ /* Filter search results which are Generated and based on Fields, e.g. Generated getters/setters */
+ sm.addScript(ScriptBuilder.wrapMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameFieldProcessor", "addAccessorOccurrences", "void", "org.eclipse.core.runtime.IProgressMonitor", "org.eclipse.jdt.core.IMethod", "java.lang.String","java.lang.String","org.eclipse.ltk.core.refactoring.RefactoringStatus"))
+ .methodToWrap(new Hook("org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup", "getSearchResults", "org.eclipse.jdt.core.search.SearchMatch[]"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGenerated", "org.eclipse.jdt.core.search.SearchMatch[]", "org.eclipse.jdt.core.search.SearchMatch[]"))
+ .transplant().build());
+ }
+
private static void patchExtractInterface(ScriptManager sm) {
/* Fix sourceEnding for generated nodes to avoid null pointer */
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.compiler.SourceElementNotifier", "notifySourceElementRequestor", "void", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration", "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration", "org.eclipse.jdt.internal.compiler.ast.ImportReference"))
.methodToWrap(new Hook("org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt", "get", "int", "java.lang.Object"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "getSourceEndFixed", "int", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getSourceEndFixed", "int", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.requestExtra(StackRequest.PARAM1)
.transplant().build());
@@ -127,7 +170,7 @@ public class EclipsePatcher extends Agent {
"org.eclipse.jdt.core.dom.MethodDeclaration"
))
.methodToWrap(new Hook("org.eclipse.jface.text.IDocument", "get", "java.lang.String", "int", "int"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "getRealMethodDeclarationSource", "java.lang.String", "java.lang.String", "java.lang.Object", "org.eclipse.jdt.core.dom.MethodDeclaration"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getRealMethodDeclarationSource", "java.lang.String", "java.lang.String", "java.lang.Object", "org.eclipse.jdt.core.dom.MethodDeclaration"))
.requestExtra(StackRequest.THIS, StackRequest.PARAM4)
.transplant().build());
@@ -136,20 +179,20 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMemberDeclarations"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodComments"))
.methodToReplace(new Hook("org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil", "getMethodDeclarationNode", "org.eclipse.jdt.core.dom.MethodDeclaration", "org.eclipse.jdt.core.IMethod", "org.eclipse.jdt.core.dom.CompilationUnit"))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchFixes", "getRealMethodDeclarationNode", "org.eclipse.jdt.core.dom.MethodDeclaration", "org.eclipse.jdt.core.IMethod", "org.eclipse.jdt.core.dom.CompilationUnit"))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getRealMethodDeclarationNode", "org.eclipse.jdt.core.dom.MethodDeclaration", "org.eclipse.jdt.core.IMethod", "org.eclipse.jdt.core.dom.CompilationUnit"))
.transplant().build());
/* Do not add @Override's for generated methods */
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ListRewrite", "insertFirst"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isListRewriteOnGeneratedNode", "boolean", "org.eclipse.jdt.core.dom.rewrite.ListRewrite"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isListRewriteOnGeneratedNode", "boolean", "org.eclipse.jdt.core.dom.rewrite.ListRewrite"))
.request(StackRequest.THIS)
.transplant().build());
/* Do not add comments for generated methods */
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.structure.ExtractInterfaceProcessor", "createMethodComment"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
.request(StackRequest.PARAM2)
.transplant().build());
}
@@ -162,7 +205,7 @@ public class EclipsePatcher extends Agent {
*/
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.core.internal.runtime.Product", "getProperty", "java.lang.String", "java.lang.String"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "addLombokNotesToEclipseAboutDialog", "java.lang.String", "java.lang.String", "java.lang.String"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "addLombokNotesToEclipseAboutDialog", "java.lang.String", "java.lang.String", "java.lang.String"))
.request(StackRequest.RETURN_VALUE, StackRequest.PARAM1)
.transplant().build());
}
@@ -175,19 +218,13 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.internal.ui.search.OccurrencesFinder", "addUsage"))
.target(new MethodTarget("org.eclipse.jdt.internal.ui.search.OccurrencesFinder", "addWrite"))
.target(new MethodTarget("org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingReconciler$PositionCollector", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
- .valueMethod(new Hook("lombok.eclipse.agent.PatchFixes", "returnFalse", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
+ .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnFalse", "boolean", "java.lang.Object"))
.request(StackRequest.PARAM1)
.build());
}
- private static void patchDisableLombokForCodeFormatterAndCleanup(ScriptManager sm) {
- sm.addScript(ScriptBuilder.setSymbolDuringMethodCall()
- .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "formatCompilationUnit"))
- .callToWrap(new Hook("org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil", "parseCompilationUnit", "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "char[]", "java.util.Map", "boolean"))
- .symbol("lombok.disable")
- .build());
-
+ private static void patchDisableLombokForCodeCleanup(ScriptManager sm) {
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.DoStatement"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.ControlStatementsFix$ControlStatementFinder", "visit", "boolean", "org.eclipse.jdt.core.dom.EnhancedForStatement"))
@@ -201,9 +238,9 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.QualifiedName"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeStyleFix$CodeStyleVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName"))
// if a generated node has children we can just ignore them as well;
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
.request(StackRequest.PARAM1)
- .valueMethod(new Hook("lombok.eclipse.agent.PatchFixes", "returnFalse", "boolean", "java.lang.Object"))
+ .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnFalse", "boolean", "java.lang.Object"))
.build());
}
@@ -211,7 +248,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.replaceMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer$ListRewriter", "rewriteList"))
.methodToReplace(new Hook("org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent", "getChildren", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]"))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchFixes", "listRewriteHandleGeneratedMethods", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent"))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "listRewriteHandleGeneratedMethods", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent[]", "org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent"))
.build());
}
@@ -223,37 +260,37 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.CompilationUnit"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.CompilationUnit", "types", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.AnnotationTypeDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.AnnotationTypeDeclaration", "bodyDeclarations", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.AnonymousClassDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.AnonymousClassDeclaration", "bodyDeclarations", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.TypeDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.TypeDeclaration", "bodyDeclarations", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.EnumDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.EnumDeclaration", "bodyDeclarations", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.SortElementsOperation$2", "visit", "boolean", "org.eclipse.jdt.core.dom.EnumDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.EnumDeclaration", "enumConstants", "java.util.List"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedNodes", "java.util.List", "java.util.List"))
.transplant().build());
}
@@ -261,7 +298,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.replaceMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteAnalyzer", "visit"))
.methodToReplace(new Hook("org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "getTokenEndOffset", "int", "int", "int"))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchFixes", "getTokenEndOffsetFixed", "int", "org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "int", "int", "java.lang.Object"))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "getTokenEndOffsetFixed", "int", "org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner", "int", "int", "java.lang.Object"))
.requestExtra(StackRequest.PARAM1)
.transplant()
.build());
@@ -273,7 +310,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.internal.core.builder.IncrementalImageBuilder", "writeClassFileContents"))
.target(new MethodTarget("org.eclipse.jdt.internal.core.builder.AbstractImageBuilder", "writeClassFileContents"))
.methodToWrap(new Hook("org.eclipse.jdt.internal.compiler.ClassFile", "getBytes", "byte[]"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "runPostCompiler", "byte[]", "byte[]", "java.lang.String"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "byte[]", "byte[]", "java.lang.String"))
.requestExtra(StackRequest.PARAM3)
.build());
}
@@ -282,24 +319,22 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.compiler.tool.EclipseCompilerImpl", "outputClassFiles"))
.methodToWrap(new Hook("javax.tools.JavaFileObject", "openOutputStream", "java.io.OutputStream"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "runPostCompiler", "java.io.OutputStream", "java.io.OutputStream"))
- .transplant()
- .build());
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "java.io.OutputStream", "java.io.OutputStream"))
+ .transplant().build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.compiler.util.Util", "writeToDisk"))
.methodToWrap(new Hook("java.io.BufferedOutputStream", "<init>", "void", "java.io.OutputStream", "int"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "runPostCompiler", "java.io.BufferedOutputStream", "java.io.BufferedOutputStream", "java.lang.String", "java.lang.String"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$LombokDeps", "runPostCompiler", "java.io.BufferedOutputStream", "java.io.BufferedOutputStream", "java.lang.String", "java.lang.String"))
.requestExtra(StackRequest.PARAM2, StackRequest.PARAM3)
- .transplant()
- .build());
+ .transplant().build());
}
private static void patchHideGeneratedNodes(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder", "findByNode"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder", "findByBinding"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedSimpleNames", "org.eclipse.jdt.core.dom.SimpleName[]",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedSimpleNames", "org.eclipse.jdt.core.dom.SimpleName[]",
"org.eclipse.jdt.core.dom.SimpleName[]"))
.request(StackRequest.RETURN_VALUE).build());
@@ -308,33 +343,41 @@ public class EclipsePatcher extends Agent {
}
private static void patchFormatters(ScriptManager sm) {
+ // before Eclipse Mars
sm.addScript(ScriptBuilder.setSymbolDuringMethodCall()
- .target(new MethodTarget("org.eclipse.jdt.internal.ui.text.java.JavaFormattingStrategy", "format", "void"))
- .callToWrap(new Hook("org.eclipse.jdt.internal.corext.util.CodeFormatterUtil", "reformat", "org.eclipse.text.edits.TextEdit",
- "int", "java.lang.String", "int", "int", "int", "java.lang.String", "java.util.Map"))
- .symbol("lombok.disable").build());
+ .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "formatCompilationUnit"))
+ .callToWrap(new Hook("org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil", "parseCompilationUnit", "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "char[]", "java.util.Map", "boolean"))
+ .symbol("lombok.disable")
+ .build());
+
+ // Eclipse Mars and beyond
+ sm.addScript(ScriptBuilder.setSymbolDuringMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.internal.formatter.DefaultCodeFormatter", "parseSourceCode"))
+ .callToWrap(new Hook("org.eclipse.jdt.core.dom.ASTParser", "createAST", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.core.runtime.IProgressMonitor"))
+ .symbol("lombok.disable")
+ .build());
}
private static void patchRefactorScripts(ScriptManager sm) {
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ASTRewrite", "replace"))
.target(new MethodTarget("org.eclipse.jdt.core.dom.rewrite.ASTRewrite", "remove"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "skipRewritingGeneratedNodes", "boolean",
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "skipRewritingGeneratedNodes", "boolean",
"org.eclipse.jdt.core.dom.ASTNode"))
.transplant().request(StackRequest.PARAM1).build());
sm.addScript(ScriptBuilder.wrapMethodCall()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameTypeProcessor", "addConstructorRenames"))
.methodToWrap(new Hook("org.eclipse.jdt.core.IType", "getMethods", "org.eclipse.jdt.core.IMethod[]"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "removeGeneratedMethods", "org.eclipse.jdt.core.IMethod[]",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "removeGeneratedMethods", "org.eclipse.jdt.core.IMethod[]",
"org.eclipse.jdt.core.IMethod[]"))
.transplant().build());
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.TempOccurrenceAnalyzer", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName"))
.target(new MethodTarget("org.eclipse.jdt.internal.corext.refactoring.rename.RenameAnalyzeUtil$ProblemNodeFinder$NameNodeVisitor", "visit", "boolean", "org.eclipse.jdt.core.dom.SimpleName"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
- .valueMethod(new Hook("lombok.eclipse.agent.PatchFixes", "returnTrue", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "isGenerated", "boolean", "org.eclipse.jdt.core.dom.ASTNode"))
+ .valueMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "returnTrue", "boolean", "java.lang.Object"))
.request(StackRequest.PARAM1)
.transplant().build());
}
@@ -342,30 +385,54 @@ public class EclipsePatcher extends Agent {
private static void patchCatchReparse(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveStartingCatchPosition"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "fixRetrieveStartingCatchPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveStartingCatchPosition", "int", "int", "int"))
.transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM1).build());
}
private static void patchIdentifierEndReparse(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveIdentifierEndPosition"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "fixRetrieveIdentifierEndPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveIdentifierEndPosition", "int", "int", "int"))
.transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
}
private static void patchRetrieveEllipsisStartPosition(ScriptManager sm) {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveEllipsisStartPosition"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "fixRetrieveEllipsisStartPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveEllipsisStartPosition", "int", "int", "int"))
.transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
}
private static void patchRetrieveRightBraceOrSemiColonPosition(ScriptManager sm) {
- sm.addScript(ScriptBuilder.wrapReturnValue()
- .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition"))
- .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace"))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "int"))
- .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
+ sm.addScript(ScriptBuilder.wrapMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
+ .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
+ .requestExtra(StackRequest.PARAM2)
+ .transplant()
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
+ .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
+ .requestExtra(StackRequest.PARAM2)
+ .transplant()
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration"))
+ .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration"))
+ .requestExtra(StackRequest.PARAM1)
+ .transplant()
+ .build());
+
+// sm.addScript(ScriptBuilder.wrapReturnValue()
+// .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition"))
+// .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace"))
+// .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "int"))
+// .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
}
private static void patchSetGeneratedFlag(ScriptManager sm) {
@@ -396,14 +463,14 @@ public class EclipsePatcher extends Agent {
return Collections.singleton("org.eclipse.jdt.core.dom.ASTConverter");
}
}).request(StackRequest.PARAM1, StackRequest.RETURN_VALUE)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlag", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void",
"org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.transplant().build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
.request(StackRequest.PARAM2, StackRequest.RETURN_VALUE)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlag", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void",
"org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.transplant().build());
@@ -421,7 +488,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convertToVariableDeclarationStatement", "org.eclipse.jdt.core.dom.VariableDeclarationStatement", "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration"))
/* Targets above are only patched because the resulting dom nodes should be marked if generated. */
.request(StackRequest.PARAM1, StackRequest.RETURN_VALUE)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlag", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlag", "void",
"org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.transplant().build());
@@ -443,7 +510,7 @@ public class EclipsePatcher extends Agent {
}
}).methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM1)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -451,7 +518,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM2)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -460,7 +527,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM4)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -468,7 +535,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "int", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM4)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -476,7 +543,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.SimpleName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM3)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
@@ -484,7 +551,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "setQualifiedNameNameAndSourceRanges", "org.eclipse.jdt.core.dom.QualifiedName", "char[][]", "long[]", "org.eclipse.jdt.internal.compiler.ast.ASTNode"))
.methodToWrap(new Hook("org.eclipse.jdt.core.dom.QualifiedName", "<init>", "void", "org.eclipse.jdt.core.dom.AST"))
.requestExtra(StackRequest.PARAM3)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "setIsGeneratedFlagForName", "void",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "setIsGeneratedFlagForName", "void",
"org.eclipse.jdt.core.dom.Name", "java.lang.Object"))
.transplant().build());
}
@@ -495,7 +562,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget(PARSER_SIG, "parse", "void",
"org.eclipse.jdt.internal.compiler.ast.MethodDeclaration",
"org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
.transplant()
.request(StackRequest.PARAM1).build());
@@ -503,7 +570,7 @@ public class EclipsePatcher extends Agent {
.target(new MethodTarget(PARSER_SIG, "parse", "void",
"org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration",
"org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration", "boolean"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
.transplant()
.request(StackRequest.PARAM1).build());
@@ -512,7 +579,7 @@ public class EclipsePatcher extends Agent {
"org.eclipse.jdt.internal.compiler.ast.Initializer",
"org.eclipse.jdt.internal.compiler.ast.TypeDeclaration",
"org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration"))
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "checkBit24", "boolean", "java.lang.Object"))
.transplant()
.request(StackRequest.PARAM1).build());
}
@@ -528,12 +595,12 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(PARSER_SIG, "getMethodBodies", "void", CUD_SIG))
- .wrapMethod(new Hook("lombok.eclipse.TransformEclipseAST", "transform", "void", PARSER_SIG, CUD_SIG))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform", "void", PARSER_SIG, CUD_SIG))
.request(StackRequest.THIS, StackRequest.PARAM1).build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(PARSER_SIG, "endParse", CUD_SIG, "int"))
- .wrapMethod(new Hook("lombok.eclipse.TransformEclipseAST", "transform_swapped", "void", CUD_SIG, PARSER_SIG))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$Transform", "transform_swapped", "void", CUD_SIG, PARSER_SIG))
.request(StackRequest.THIS, StackRequest.RETURN_VALUE).build());
}
@@ -549,7 +616,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget(CLASSSCOPE_SIG, "buildFieldsAndMethods", "void"))
.request(StackRequest.THIS)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchDelegatePortal", "handleDelegateForType", "boolean", "java.lang.Object"))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Delegate", "handleDelegateForType", "boolean", "java.lang.Object"))
.build());
}
@@ -579,24 +646,24 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(PARSER_SIG, "consumeExitVariableWithInitialization", "void"))
.request(StackRequest.THIS)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchValEclipsePortal", "copyInitializationOfLocalDeclaration", "void", "java.lang.Object"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "copyInitializationOfLocalDeclaration", "void", "java.lang.Object"))
.build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(PARSER_SIG, "consumeEnhancedForStatementHeader", "void"))
.request(StackRequest.THIS)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchValEclipsePortal", "copyInitializationOfForEachIterable", "void", "java.lang.Object"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "copyInitializationOfForEachIterable", "void", "java.lang.Object"))
.build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", VARIABLEDECLARATIONSTATEMENT_SIG, LOCALDECLARATION_SIG))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchValEclipsePortal", "addFinalAndValAnnotationToVariableDeclarationStatement",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "addFinalAndValAnnotationToVariableDeclarationStatement",
"void", "java.lang.Object", "java.lang.Object", "java.lang.Object"))
.request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(ASTCONVERTER_SIG, "setModifiers", "void", SINGLEVARIABLEDECLARATION_SIG, LOCALDECLARATION_SIG))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchValEclipsePortal", "addFinalAndValAnnotationToSingleVariableDeclaration",
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$ValPortal", "addFinalAndValAnnotationToSingleVariableDeclaration",
"void", "java.lang.Object", "java.lang.Object", "java.lang.Object"))
.request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
}
@@ -611,26 +678,26 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
.request(StackRequest.THIS, StackRequest.PARAM1)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG))
.build());
sm.addScript(ScriptBuilder.replaceMethodCall()
.target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
.methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
.requestExtra(StackRequest.THIS)
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Val", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG))
.build());
sm.addScript(ScriptBuilder.replaceMethodCall()
.target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
.methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG))
+ .replacementMethod(new Hook("lombok.launch.PatchFixesHider$Val", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG))
.build());
sm.addScript(ScriptBuilder.exitEarly()
.target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
.request(StackRequest.THIS, StackRequest.PARAM1)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG))
+ .decisionMethod(new Hook("lombok.launch.PatchFixesHider$Val", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG))
.build());
}
@@ -641,7 +708,7 @@ public class EclipsePatcher extends Agent {
sm.addScript(ScriptBuilder.wrapReturnValue()
.target(new MethodTarget(SOURCE_TYPE_CONVERTER_SIG, "convertAnnotations", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG))
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "convertAnnotations", ANNOTATION_SIG + "[]", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "convertAnnotations", ANNOTATION_SIG + "[]", ANNOTATION_SIG + "[]", I_ANNOTATABLE_SIG))
.request(StackRequest.PARAM1, StackRequest.RETURN_VALUE).build());
}
@@ -659,10 +726,11 @@ public class EclipsePatcher extends Agent {
}
private static void patchExtensionMethod(ScriptManager sm, boolean ecj) {
- final String PATCH_EXTENSIONMETHOD = "lombok.eclipse.agent.PatchExtensionMethod";
+ final String PATCH_EXTENSIONMETHOD = "lombok.launch.PatchFixesHider$ExtensionMethod";
final String PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL = "lombok.eclipse.agent.PatchExtensionMethodCompletionProposalPortal";
final String MESSAGE_SEND_SIG = "org.eclipse.jdt.internal.compiler.ast.MessageSend";
final String TYPE_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
+ final String SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.Scope";
final String BLOCK_SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
final String TYPE_BINDINGS_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding[]";
final String PROBLEM_REPORTER_SIG = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter";
@@ -690,6 +758,13 @@ public class EclipsePatcher extends Agent {
.replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG))
.build());
+ // Since eclipse mars; they added a param.
+ sm.addScript(replaceMethodCall()
+ .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG))
+ .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "invalidMethod", "void", MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG))
+ .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG))
+ .build());
+
if (!ecj) {
sm.addScript(wrapReturnValue()
.target(new MethodTarget(COMPLETION_PROPOSAL_COLLECTOR_SIG, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG))
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
index b1f5a43a..02760e35 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
@@ -699,7 +699,10 @@ public class PatchDelegate {
}
private static void addAllMethodBindings0(List<BindingTuple> list, TypeBinding binding, Set<String> banList, char[] fieldName, ASTNode responsible) throws DelegateRecursion {
- if (binding instanceof SourceTypeBinding) ((SourceTypeBinding) binding).scope.environment().globalOptions.storeAnnotations = true;
+ if (binding instanceof SourceTypeBinding) {
+ ClassScope scope = ((SourceTypeBinding) binding).scope;
+ if (scope != null) scope.environment().globalOptions.storeAnnotations = true;
+ }
if (binding == null) return;
TypeBinding inner;
@@ -721,42 +724,44 @@ public class PatchDelegate {
}
}
- if (binding instanceof ReferenceBinding) {
- ReferenceBinding rb = (ReferenceBinding) binding;
- MethodBinding[] availableMethods = rb.availableMethods();
- FieldBinding[] availableFields = rb.availableFields();
- failIfContainsAnnotation(binding, availableMethods);
- failIfContainsAnnotation(binding, availableFields);
-
- MethodBinding[] parameterizedSigs = availableMethods;
- MethodBinding[] baseSigs = parameterizedSigs;
- if (binding instanceof ParameterizedTypeBinding) {
- baseSigs = ((ParameterizedTypeBinding)binding).genericType().availableMethods();
- if (baseSigs.length != parameterizedSigs.length) {
- // The last known state of eclipse source says this can't happen, so we rely on it,
- // but if this invariant is broken, better to go with 'arg0' naming instead of crashing.
- baseSigs = parameterizedSigs;
- }
- }
- for (int i = 0; i < parameterizedSigs.length; i++) {
- MethodBinding mb = parameterizedSigs[i];
- String sig = printSig(mb);
- if (mb.isStatic()) continue;
- if (mb.isBridge()) continue;
- if (mb.isConstructor()) continue;
- if (mb.isDefaultAbstract()) continue;
- if (!mb.isPublic()) continue;
- if (mb.isSynthetic()) continue;
- if (!banList.add(sig)) continue; // If add returns false, it was already in there.
- BindingTuple pair = new BindingTuple(mb, baseSigs[i], fieldName, responsible);
- list.add(pair);
- }
- addAllMethodBindings0(list, rb.superclass(), banList, fieldName, responsible);
- ReferenceBinding[] interfaces = rb.superInterfaces();
- if (interfaces != null) {
- for (ReferenceBinding iface : interfaces) addAllMethodBindings0(list, iface, banList, fieldName, responsible);
+ if (!(binding instanceof ReferenceBinding)) {
+ return;
+ }
+
+ ReferenceBinding rb = (ReferenceBinding) binding;
+ MethodBinding[] availableMethods = rb.availableMethods();
+ FieldBinding[] availableFields = rb.availableFields();
+ failIfContainsAnnotation(binding, availableMethods);
+ failIfContainsAnnotation(binding, availableFields);
+
+ MethodBinding[] parameterizedSigs = availableMethods;
+ MethodBinding[] baseSigs = parameterizedSigs;
+ if (binding instanceof ParameterizedTypeBinding) {
+ baseSigs = ((ParameterizedTypeBinding)binding).genericType().availableMethods();
+ if (baseSigs.length != parameterizedSigs.length) {
+ // The last known state of eclipse source says this can't happen, so we rely on it,
+ // but if this invariant is broken, better to go with 'arg0' naming instead of crashing.
+ baseSigs = parameterizedSigs;
}
}
+ for (int i = 0; i < parameterizedSigs.length; i++) {
+ MethodBinding mb = parameterizedSigs[i];
+ String sig = printSig(mb);
+ if (mb.isStatic()) continue;
+ if (mb.isBridge()) continue;
+ if (mb.isConstructor()) continue;
+ if (mb.isDefaultAbstract()) continue;
+ if (!mb.isPublic()) continue;
+ if (mb.isSynthetic()) continue;
+ if (!banList.add(sig)) continue; // If add returns false, it was already in there.
+ BindingTuple pair = new BindingTuple(mb, baseSigs[i], fieldName, responsible);
+ list.add(pair);
+ }
+ addAllMethodBindings0(list, rb.superclass(), banList, fieldName, responsible);
+ ReferenceBinding[] interfaces = rb.superInterfaces();
+ if (interfaces != null) {
+ for (ReferenceBinding iface : interfaces) addAllMethodBindings0(list, iface, banList, fieldName, responsible);
+ }
}
private static final char[] STRING_LOMBOK = new char[] {'l', 'o', 'm', 'b', 'o', 'k'};
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
index 8eec27fb..5d586dff 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
@@ -24,6 +24,8 @@ package lombok.eclipse.agent;
import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation;
import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -56,6 +58,7 @@ import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
@@ -89,16 +92,47 @@ public class PatchExtensionMethod {
private final ProblemReporter problemReporter;
private final WeakReference<MessageSend> messageSendRef;
private final MethodBinding method;
+ private final Scope scope;
- PostponedInvalidMethodError(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
+ private static final Method shortMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class);
+ private static final Method longMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class, Scope.class);
+
+ private static Method getMethod(String name, Class<?>... types) {
+ try {
+ Method m = ProblemReporter.class.getMethod(name, types);
+ m.setAccessible(true);
+ return m;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ PostponedInvalidMethodError(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) {
this.problemReporter = problemReporter;
this.messageSendRef = new WeakReference<MessageSend>(messageSend);
this.method = method;
+ this.scope = scope;
+ }
+
+ static void invoke(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) {
+ if (messageSend != null) {
+ try {
+ if (shortMethod != null) shortMethod.invoke(problemReporter, messageSend, method);
+ else if (longMethod != null) longMethod.invoke(problemReporter, messageSend, method, scope);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof Error) throw (Error) t;
+ if (t instanceof RuntimeException) throw (RuntimeException) t;
+ throw new RuntimeException(t);
+ }
+ }
}
public void fire() {
MessageSend messageSend = messageSendRef.get();
- if (messageSend != null) problemReporter.invalidMethod(messageSend, method);
+ invoke(problemReporter, messageSend, method, scope);
}
}
@@ -185,7 +219,11 @@ public class PatchExtensionMethod {
}
public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
- MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method));
+ MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method, null));
+ }
+
+ public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) {
+ MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method, scope));
}
public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) {
@@ -224,12 +262,16 @@ public class PatchExtensionMethod {
if (methodCall.arguments != null) arguments.addAll(Arrays.asList(methodCall.arguments));
List<TypeBinding> argumentTypes = new ArrayList<TypeBinding>();
for (Expression argument : arguments) {
- argumentTypes.add(argument.resolvedType);
+ if (argument.resolvedType != null) argumentTypes.add(argument.resolvedType);
+ // TODO: Instead of just skipping nulls entirely, there is probably a 'unresolved type' placeholder. THAT is what we ought to be adding here!
}
+ Expression[] originalArgs = methodCall.arguments;
+ methodCall.arguments = arguments.toArray(new Expression[0]);
MethodBinding fixedBinding = scope.getMethod(extensionMethod.declaringClass, methodCall.selector, argumentTypes.toArray(new TypeBinding[0]), methodCall);
if (fixedBinding instanceof ProblemMethodBinding) {
+ methodCall.arguments = originalArgs;
if (fixedBinding.declaringClass != null) {
- scope.problemReporter().invalidMethod(methodCall, fixedBinding);
+ PostponedInvalidMethodError.invoke(scope.problemReporter(), methodCall, fixedBinding, scope);
}
} else {
for (int i = 0, iend = arguments.size(); i < iend; i++) {
@@ -246,7 +288,6 @@ public class PatchExtensionMethod {
arg.implicitConversion = TypeIds.UNBOXING | (id + (id << 4)); // magic see TypeIds
}
}
- methodCall.arguments = arguments.toArray(new Expression[0]);
methodCall.receiver = createNameRef(extensionMethod.declaringClass, methodCall);
methodCall.actualReceiverType = extensionMethod.declaringClass;
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
index 97ca5a7e..c11a49cd 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
@@ -60,8 +60,6 @@ import org.eclipse.jdt.ui.text.java.CompletionProposalCollector;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
public class PatchExtensionMethodCompletionProposal {
-
-
public static IJavaCompletionProposal[] getJavaCompletionProposals(IJavaCompletionProposal[] javaCompletionProposals,
CompletionProposalCollector completionProposalCollector) {
@@ -178,7 +176,7 @@ public class PatchExtensionMethodCompletionProposal {
return !proposals.isEmpty() && Reflection.isComplete();
}
- private static int getReplacementOffset(IJavaCompletionProposal proposal) {
+ private static int getReplacementOffset(Object proposal) {
try {
return Reflection.replacementOffsetField.getInt(proposal);
} catch (Exception ignore) {
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java
index 6dca1901..19e1952e 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java
@@ -33,7 +33,6 @@ public class PatchExtensionMethodCompletionProposalPortal {
private static final String COMPLETION_PROPOSAL_COLLECTOR = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector";
private static final String I_JAVA_COMPLETION_PROPOSAL_ARRAY = "[Lorg.eclipse.jdt.ui.text.java.IJavaCompletionProposal;";
-
public static IJavaCompletionProposal[] getJavaCompletionProposals(Object[] javaCompletionProposals, Object completionProposalCollector) {
try {
return (IJavaCompletionProposal[]) ReflectionForUi.getJavaCompletionProposals.invoke(null, javaCompletionProposals, completionProposalCollector);
@@ -52,7 +51,7 @@ public class PatchExtensionMethodCompletionProposalPortal {
}
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
//do anything useful here.
- return (IJavaCompletionProposal[])javaCompletionProposals;
+ return (IJavaCompletionProposal[]) javaCompletionProposals;
}
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
deleted file mode 100644
index d1c668a0..00000000
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package lombok.eclipse.agent;
-
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Stack;
-
-import lombok.core.DiagnosticsReceiver;
-import lombok.core.PostCompiler;
-import lombok.core.Version;
-import lombok.eclipse.EclipseAugments;
-
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.jdt.core.IAnnotatable;
-import org.eclipse.jdt.core.IAnnotation;
-import org.eclipse.jdt.core.IMethod;
-import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.core.dom.MethodDeclaration;
-import org.eclipse.jdt.core.dom.SimpleName;
-import org.eclipse.jdt.internal.compiler.ast.Annotation;
-import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent;
-import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent;
-import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner;
-import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
-
-public class PatchFixes {
- public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
- if ("aboutText".equals(key)) {
- return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. http://projectlombok.org/";
- }
-
- return origReturnValue;
- }
-
- public static boolean isGenerated(org.eclipse.jdt.core.dom.ASTNode node) {
- boolean result = false;
- try {
- result = ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue();
- if (!result && node.getParent() != null && node.getParent() instanceof org.eclipse.jdt.core.dom.QualifiedName)
- result = isGenerated(node.getParent());
- } catch (Exception e) {
- // better to assume it isn't generated
- }
- return result;
- }
-
- public static boolean isListRewriteOnGeneratedNode(org.eclipse.jdt.core.dom.rewrite.ListRewrite rewrite) {
- return isGenerated(rewrite.getParent());
- }
-
- public static boolean returnFalse(java.lang.Object object) {
- return false;
- }
-
- public static boolean returnTrue(java.lang.Object object) {
- return true;
- }
-
- @java.lang.SuppressWarnings({"unchecked", "rawtypes"}) public static java.util.List removeGeneratedNodes(java.util.List list) {
- try {
- java.util.List realNodes = new java.util.ArrayList(list.size());
- for (java.lang.Object node : list) {
- if(!isGenerated(((org.eclipse.jdt.core.dom.ASTNode)node))) {
- realNodes.add(node);
- }
- }
- return realNodes;
- } catch (Exception e) {
- }
- return list;
- }
-
- public static java.lang.String getRealMethodDeclarationSource(java.lang.String original, Object processor, org.eclipse.jdt.core.dom.MethodDeclaration declaration) throws Exception {
- if (!isGenerated(declaration)) return original;
-
- List<org.eclipse.jdt.core.dom.Annotation> annotations = new ArrayList<org.eclipse.jdt.core.dom.Annotation>();
- for (Object modifier : declaration.modifiers()) {
- if (modifier instanceof org.eclipse.jdt.core.dom.Annotation) {
- org.eclipse.jdt.core.dom.Annotation annotation = (org.eclipse.jdt.core.dom.Annotation)modifier;
- String qualifiedAnnotationName = annotation.resolveTypeBinding().getQualifiedName();
- if (!"java.lang.Override".equals(qualifiedAnnotationName) && !"java.lang.SuppressWarnings".equals(qualifiedAnnotationName)) annotations.add(annotation);
- }
- }
-
- StringBuilder signature = new StringBuilder();
- addAnnotations(annotations, signature);
-
- if ((Boolean)processor.getClass().getDeclaredField("fPublic").get(processor)) signature.append("public ");
- if ((Boolean)processor.getClass().getDeclaredField("fAbstract").get(processor)) signature.append("abstract ");
-
- signature
- .append(declaration.getReturnType2().toString())
- .append(" ").append(declaration.getName().getFullyQualifiedName())
- .append("(");
-
- boolean first = true;
- for (Object parameter : declaration.parameters()) {
- if (!first) signature.append(", ");
- first = false;
- // We should also add the annotations of the parameters
- signature.append(parameter);
- }
-
- signature.append(");");
- return signature.toString();
- }
-
- // part of getRealMethodDeclarationSource(...)
- public static void addAnnotations(List<org.eclipse.jdt.core.dom.Annotation> annotations, StringBuilder signature) {
- /*
- * We SHOULD be able to handle the following cases:
- * @Override
- * @Override()
- * @SuppressWarnings("all")
- * @SuppressWarnings({"all", "unused"})
- * @SuppressWarnings(value = "all")
- * @SuppressWarnings(value = {"all", "unused"})
- * @EqualsAndHashCode(callSuper=true, of="id")
- *
- * Currently, we only seem to correctly support:
- * @Override
- * @Override() N.B. We lose the parentheses here, since there are no values. No big deal.
- * @SuppressWarnings("all")
- */
- for (org.eclipse.jdt.core.dom.Annotation annotation : annotations) {
- List<String> values = new ArrayList<String>();
- if (annotation.isSingleMemberAnnotation()) {
- org.eclipse.jdt.core.dom.SingleMemberAnnotation smAnn = (org.eclipse.jdt.core.dom.SingleMemberAnnotation) annotation;
- values.add(smAnn.getValue().toString());
- } else if (annotation.isNormalAnnotation()) {
- org.eclipse.jdt.core.dom.NormalAnnotation normalAnn = (org.eclipse.jdt.core.dom.NormalAnnotation) annotation;
- for (Object value : normalAnn.values()) values.add(value.toString());
- }
-
- signature.append("@").append(annotation.resolveTypeBinding().getQualifiedName());
- if (!values.isEmpty()) {
- signature.append("(");
- boolean first = true;
- for (String string : values) {
- if (!first) signature.append(", ");
- first = false;
- signature.append('"').append(string).append('"');
- }
- signature.append(")");
- }
- signature.append(" ");
- }
- }
-
- public static org.eclipse.jdt.core.dom.MethodDeclaration getRealMethodDeclarationNode(org.eclipse.jdt.core.IMethod sourceMethod, org.eclipse.jdt.core.dom.CompilationUnit cuUnit) throws JavaModelException {
- MethodDeclaration methodDeclarationNode = ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, cuUnit);
- if (isGenerated(methodDeclarationNode)) {
- IType declaringType = sourceMethod.getDeclaringType();
- Stack<IType> typeStack = new Stack<IType>();
- while (declaringType != null) {
- typeStack.push(declaringType);
- declaringType = declaringType.getDeclaringType();
- }
-
- IType rootType = typeStack.pop();
- org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = findTypeDeclaration(rootType, cuUnit.types());
- while (!typeStack.isEmpty() && typeDeclaration != null) {
- typeDeclaration = findTypeDeclaration(typeStack.pop(), typeDeclaration.bodyDeclarations());
- }
-
- if (typeStack.isEmpty() && typeDeclaration != null) {
- String methodName = sourceMethod.getElementName();
- for (Object declaration : typeDeclaration.bodyDeclarations()) {
- if (declaration instanceof org.eclipse.jdt.core.dom.MethodDeclaration) {
- org.eclipse.jdt.core.dom.MethodDeclaration methodDeclaration = (org.eclipse.jdt.core.dom.MethodDeclaration) declaration;
- if (methodDeclaration.getName().toString().equals(methodName)) {
- return methodDeclaration;
- }
- }
- }
- }
- }
- return methodDeclarationNode;
- }
-
- // part of getRealMethodDeclarationNode
- public static org.eclipse.jdt.core.dom.AbstractTypeDeclaration findTypeDeclaration(IType searchType, List<?> nodes) {
- for (Object object : nodes) {
- if (object instanceof org.eclipse.jdt.core.dom.AbstractTypeDeclaration) {
- org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = (org.eclipse.jdt.core.dom.AbstractTypeDeclaration) object;
- if (typeDeclaration.getName().toString().equals(searchType.getElementName()))
- return typeDeclaration;
- }
- }
- return null;
- }
-
- public static int getSourceEndFixed(int sourceEnd, org.eclipse.jdt.internal.compiler.ast.ASTNode node) throws Exception {
- if (sourceEnd == -1) {
- org.eclipse.jdt.internal.compiler.ast.ASTNode object = (org.eclipse.jdt.internal.compiler.ast.ASTNode)node.getClass().getField("$generatedBy").get(node);
- if (object != null) {
- return object.sourceEnd;
- }
- }
- return sourceEnd;
- }
-
- public static int fixRetrieveStartingCatchPosition(int original, int start) {
- return original == -1 ? start : original;
- }
-
- public static int fixRetrieveIdentifierEndPosition(int original, int end) {
- return original == -1 ? end : original;
- }
-
- public static int fixRetrieveEllipsisStartPosition(int original, int end) {
- return original == -1 ? end : original;
- }
-
- public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) {
-// return original;
- return original == -1 ? end : original; // Need to fix: see issue 325.
- }
-
- public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24
-
- public static boolean checkBit24(Object node) throws Exception {
- int bits = (Integer)(node.getClass().getField("bits").get(node));
- return (bits & ALREADY_PROCESSED_FLAG) != 0;
- }
-
- public static boolean skipRewritingGeneratedNodes(org.eclipse.jdt.core.dom.ASTNode node) throws Exception {
- return ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue();
- }
-
- public static void setIsGeneratedFlag(org.eclipse.jdt.core.dom.ASTNode domNode,
- org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode) throws Exception {
- if (internalNode == null || domNode == null) return;
- boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(internalNode) != null;
- if (isGenerated) domNode.getClass().getField("$isGenerated").set(domNode, true);
- }
-
- public static void setIsGeneratedFlagForName(org.eclipse.jdt.core.dom.Name name, Object internalNode) throws Exception {
- if (internalNode instanceof org.eclipse.jdt.internal.compiler.ast.ASTNode) {
- if (EclipseAugments.ASTNode_generatedBy.get((org.eclipse.jdt.internal.compiler.ast.ASTNode) internalNode) != null) {
- name.getClass().getField("$isGenerated").set(name, true);
- }
- }
- }
-
- public static RewriteEvent[] listRewriteHandleGeneratedMethods(RewriteEvent parent) {
- RewriteEvent[] children = parent.getChildren();
- List<RewriteEvent> newChildren = new ArrayList<RewriteEvent>();
- List<RewriteEvent> modifiedChildren = new ArrayList<RewriteEvent>();
- for (int i=0; i<children.length; i++) {
- RewriteEvent child = children[i];
- boolean isGenerated = isGenerated( (org.eclipse.jdt.core.dom.ASTNode)child.getOriginalValue() );
- if (isGenerated) {
- if ((child.getChangeKind() == RewriteEvent.REPLACED || child.getChangeKind() == RewriteEvent.REMOVED)
- && child.getOriginalValue() instanceof org.eclipse.jdt.core.dom.MethodDeclaration
- && child.getNewValue() != null
- ) {
- modifiedChildren.add(new NodeRewriteEvent(null, child.getNewValue()));
- }
- } else {
- newChildren.add(child);
- }
- }
- // Since Eclipse doesn't honor the "insert at specified location" for already existing members,
- // we'll just add them last
- newChildren.addAll(modifiedChildren);
- return newChildren.toArray(new RewriteEvent[newChildren.size()]);
- }
-
- public static int getTokenEndOffsetFixed(TokenScanner scanner, int token, int startOffset, Object domNode) throws CoreException {
- boolean isGenerated = false;
- try {
- isGenerated = (Boolean) domNode.getClass().getField("$isGenerated").get(domNode);
- } catch (Exception e) {
- // If this fails, better to break some refactor scripts than to crash eclipse.
- }
- if (isGenerated) return -1;
- return scanner.getTokenEndOffset(token, startOffset);
- }
-
- public static IMethod[] removeGeneratedMethods(IMethod[] methods) throws Exception {
- List<IMethod> result = new ArrayList<IMethod>();
- for (IMethod m : methods) {
- if (m.getNameRange().getLength() > 0 && !m.getNameRange().equals(m.getSourceRange())) result.add(m);
- }
- return result.size() == methods.length ? methods : result.toArray(new IMethod[result.size()]);
- }
-
- public static SimpleName[] removeGeneratedSimpleNames(SimpleName[] in) throws Exception {
- Field f = SimpleName.class.getField("$isGenerated");
-
- int count = 0;
- for (int i = 0; i < in.length; i++) {
- if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) count++;
- }
- if (count == in.length) return in;
- SimpleName[] newSimpleNames = new SimpleName[count];
- count = 0;
- for (int i = 0; i < in.length; i++) {
- if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) newSimpleNames[count++] = in[i];
- }
- return newSimpleNames;
- }
-
- public static byte[] runPostCompiler(byte[] bytes, String fileName) {
- byte[] transformed = PostCompiler.applyTransformations(bytes, fileName, DiagnosticsReceiver.CONSOLE);
- return transformed == null ? bytes : transformed;
- }
-
- public static OutputStream runPostCompiler(OutputStream out) throws IOException {
- return PostCompiler.wrapOutputStream(out, "TEST", DiagnosticsReceiver.CONSOLE);
- }
-
- public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
- String fileName = path + "/" + name;
- return new BufferedOutputStream(PostCompiler.wrapOutputStream(out, fileName, DiagnosticsReceiver.CONSOLE));
- }
-
- public static Annotation[] convertAnnotations(Annotation[] out, IAnnotatable annotatable) {
- IAnnotation[] in;
-
- try {
- in = annotatable.getAnnotations();
- } catch (Exception e) {
- return out;
- }
-
- if (out == null) return null;
- int toWrite = 0;
-
- for (int idx = 0; idx < out.length; idx++) {
- String oName = new String(out[idx].type.getLastToken());
- boolean found = false;
- for (IAnnotation i : in) {
- String name = i.getElementName();
- int li = name.lastIndexOf('.');
- if (li > -1) name = name.substring(li + 1);
- if (name.equals(oName)) {
- found = true;
- break;
- }
- }
- if (!found) out[idx] = null;
- else toWrite++;
- }
-
- Annotation[] replace = out;
- if (toWrite < out.length) {
- replace = new Annotation[toWrite];
- int idx = 0;
- for (int i = 0; i < out.length; i++) {
- if (out[i] == null) continue;
- replace[idx++] = out[i];
- }
- }
-
- return replace;
- }
-}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
new file mode 100644
index 00000000..52f63765
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.agent;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import lombok.core.DiagnosticsReceiver;
+import lombok.core.PostCompiler;
+import lombok.core.Version;
+
+public class PatchFixesShadowLoaded {
+ public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
+ if ("aboutText".equals(key)) {
+ return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. https://projectlombok.org/";
+ }
+ return origReturnValue;
+ }
+
+ public static byte[] runPostCompiler(byte[] bytes, String fileName) {
+ byte[] transformed = PostCompiler.applyTransformations(bytes, fileName, DiagnosticsReceiver.CONSOLE);
+ return transformed == null ? bytes : transformed;
+ }
+
+ public static OutputStream runPostCompiler(OutputStream out) throws IOException {
+ return PostCompiler.wrapOutputStream(out, "TEST", DiagnosticsReceiver.CONSOLE);
+ }
+
+ public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
+ String fileName = path + "/" + name;
+ return new BufferedOutputStream(PostCompiler.wrapOutputStream(out, fileName, DiagnosticsReceiver.CONSOLE));
+ }
+}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
index e734dceb..3da37869 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 The Project Lombok Authors.
+ * Copyright (C) 2010-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
@@ -21,11 +21,6 @@
*/
package lombok.eclipse.agent;
-import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
-import static lombok.eclipse.Eclipse.*;
-
-import java.lang.reflect.Field;
-
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
@@ -43,6 +38,11 @@ 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 java.lang.reflect.Field;
+
+import static lombok.eclipse.Eclipse.poss;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.makeType;
+
public class PatchVal {
// This is half of the work for 'val' support - the other half is in PatchValEclipse. This half is enough for ecj.
@@ -57,6 +57,9 @@ public class PatchVal {
return expr.resolveType(scope);
} catch (NullPointerException e) {
return null;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments.
+ return null;
}
}
@@ -66,6 +69,9 @@ public class PatchVal {
return expr.resolveType(scope);
} catch (NullPointerException e) {
return null;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // This will occur internally due to for example 'val x = mth("X");', where mth takes 2 arguments.
+ return null;
}
}
@@ -78,31 +84,44 @@ public class PatchVal {
return true;
}
- public static boolean couldBeVal(TypeReference ref) {
+ public static boolean couldBe(String key, TypeReference ref) {
+ String[] keyParts = key.split("\\.");
if (ref instanceof SingleTypeReference) {
char[] token = ((SingleTypeReference)ref).token;
- return matches("val", token);
+ return matches(keyParts[keyParts.length - 1], token);
}
if (ref instanceof QualifiedTypeReference) {
char[][] tokens = ((QualifiedTypeReference)ref).tokens;
- if (tokens == null || tokens.length != 2) return false;
- return matches("lombok", tokens[0]) && matches("val", tokens[1]);
+ if (keyParts.length != tokens.length) return false;
+ for(int i = 0; i < tokens.length; ++i) {
+ String part = keyParts[i];
+ char[] token = tokens[i];
+ if (!matches(part, token)) return false;
+ }
+ return true;
}
return false;
}
-
- private static boolean isVal(TypeReference ref, BlockScope scope) {
- if (!couldBeVal(ref)) return false;
-
+
+ private static boolean is(TypeReference ref, BlockScope scope, String key) {
+ if (!couldBe(key, ref)) return false;
+
TypeBinding resolvedType = ref.resolvedType;
if (resolvedType == null) resolvedType = ref.resolveType(scope, false);
if (resolvedType == null) return false;
char[] pkg = resolvedType.qualifiedPackageName();
char[] nm = resolvedType.qualifiedSourceName();
- return matches("lombok", pkg) && matches("val", nm);
+ int pkgFullLength = pkg.length > 0 ? pkg.length + 1: 0;
+ char[] fullName = new char[pkgFullLength + nm.length];
+ if(pkg.length > 0) {
+ System.arraycopy(pkg, 0, fullName, 0, pkg.length);
+ fullName[pkg.length] = '.';
+ }
+ System.arraycopy(nm, 0, fullName, pkgFullLength, nm.length);
+ return matches(key, fullName);
}
public static final class Reflection {
@@ -126,9 +145,20 @@ public class PatchVal {
if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false;
boolean decomponent = false;
- if (!isVal(local.type, scope)) return false;
+ boolean val = isVal(local, scope);
+ boolean var = isVar(local, scope);
+ if (!(val || var)) return false;
- if (new Throwable().getStackTrace()[2].getClassName().contains("ForStatement")) return false;
+ StackTraceElement[] st = new Throwable().getStackTrace();
+ for (int i = 0; i < st.length - 2 && i < 10; i++) {
+ if (st[i].getClassName().equals("lombok.launch.PatchFixesHider$Val")) {
+ boolean valInForStatement = val &&
+ st[i + 1].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration") &&
+ st[i + 2].getClassName().equals("org.eclipse.jdt.internal.compiler.ast.ForStatement");
+ if (valInForStatement) return false;
+ break;
+ }
+ }
Expression init = local.initialization;
if (init == null && Reflection.initCopyField != null) {
@@ -157,7 +187,7 @@ public class PatchVal {
TypeBinding resolved = null;
try {
- resolved = decomponent ? getForEachComponentType(init, scope) : init.resolveType(scope);
+ resolved = decomponent ? getForEachComponentType(init, scope) : resolveForExpression(init, scope);
} catch (NullPointerException e) {
// This definitely occurs if as part of resolving the initializer expression, a
// lambda expression in it must also be resolved (such as when lambdas are part of
@@ -174,23 +204,33 @@ public class PatchVal {
}
}
- local.modifiers |= ClassFileConstants.AccFinal;
+ if(val) local.modifiers |= ClassFileConstants.AccFinal;
local.annotations = addValAnnotation(local.annotations, local.type, scope);
local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(local.type, 3));
return false;
}
+ private static boolean isVar(LocalDeclaration local, BlockScope scope) {
+ return is(local.type, scope, "lombok.experimental.var") || is(local.type, scope, "lombok.var");
+ }
+
+ private static boolean isVal(LocalDeclaration local, BlockScope scope) {
+ return is(local.type, scope, "lombok.val");
+ }
+
public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) {
if (forEach.elementVariable == null) return false;
- if (!isVal(forEach.elementVariable.type, scope)) return false;
+ boolean val = isVal(forEach.elementVariable, scope);
+ boolean var = isVar(forEach.elementVariable, scope);
+ if (!(val || var)) return false;
TypeBinding component = getForEachComponentType(forEach.collection, scope);
if (component == null) return false;
TypeReference replacement = makeType(component, forEach.elementVariable.type, false);
- forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal;
+ if (val) forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal;
forEach.elementVariable.annotations = addValAnnotation(forEach.elementVariable.annotations, forEach.elementVariable.type, scope);
forEach.elementVariable.type = replacement != null ? replacement :
new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, poss(forEach.elementVariable.type, 3));
@@ -215,7 +255,7 @@ public class PatchVal {
private static TypeBinding getForEachComponentType(Expression collection, BlockScope scope) {
if (collection != null) {
TypeBinding resolved = collection.resolvedType;
- if (resolved == null) resolved = collection.resolveType(scope);
+ if (resolved == null) resolved = resolveForExpression(collection, scope);
if (resolved == null) return null;
if (resolved.isArrayType()) {
resolved = ((ArrayBinding) resolved).elementsType();
@@ -243,4 +283,13 @@ public class PatchVal {
return null;
}
+
+ private static TypeBinding resolveForExpression(Expression collection, BlockScope scope) {
+ try {
+ return collection.resolveType(scope);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Known cause of issues; for example: val e = mth("X"), where mth takes 2 arguments.
+ return null;
+ }
+ }
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
index 7d5f36f4..46237dcf 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 The Project Lombok Authors.
+ * Copyright (C) 2010-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
@@ -47,6 +47,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.parser.Parser;
@@ -65,7 +66,9 @@ public class PatchValEclipse {
ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr];
ASTNode init = foreachDecl.collection;
if (init == null) return;
- if (foreachDecl.elementVariable == null || !PatchVal.couldBeVal(foreachDecl.elementVariable.type)) return;
+ boolean val = couldBeVal(foreachDecl.elementVariable.type);
+ boolean var = couldBeVar(foreachDecl.elementVariable.type);
+ if (foreachDecl.elementVariable == null || !(val || var)) return;
try {
if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init);
@@ -88,7 +91,9 @@ public class PatchValEclipse {
if (!(variableDecl instanceof LocalDeclaration)) return;
ASTNode init = variableDecl.initialization;
if (init == null) return;
- if (!PatchVal.couldBeVal(variableDecl.type)) return;
+ boolean val = couldBeVal(variableDecl.type);
+ boolean var = couldBeVar(variableDecl.type);
+ if (!(val || var)) return;
try {
if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init);
@@ -97,6 +102,10 @@ public class PatchValEclipse {
}
}
+ private static boolean couldBeVar(TypeReference type) {
+ return PatchVal.couldBe("lombok.experimental.var", type) || PatchVal.couldBe("lombok.var", type);
+ }
+
public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, SingleVariableDeclaration out, LocalDeclaration in) {
@SuppressWarnings("unchecked") List<IExtendedModifier> modifiers = out.modifiers();
addFinalAndValAnnotationToModifierList(converter, modifiers, out.getAST(), in);
@@ -115,7 +124,7 @@ public class PatchValEclipse {
Annotation valAnnotation = null;
for (Annotation ann : in.annotations) {
- if (PatchVal.couldBeVal(ann.type)) {
+ if (couldBeVal(ann.type)) {
found = true;
valAnnotation = ann;
break;
@@ -167,6 +176,10 @@ public class PatchValEclipse {
}
}
+ private static boolean couldBeVal(TypeReference type) {
+ return PatchVal.couldBe("lombok.val", type);
+ }
+
public static Modifier createModifier(AST ast, ModifierKeyword keyword, int start, int end) {
Modifier modifier = null;
try {
diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
new file mode 100644
index 00000000..b1d73352
--- /dev/null
+++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2010-2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.launch;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import lombok.eclipse.EclipseAugments;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IAnnotatable;
+import org.eclipse.jdt.core.IAnnotation;
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.search.SearchMatch;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
+import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
+import org.eclipse.jdt.internal.core.SourceField;
+import org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent;
+import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEvent;
+import org.eclipse.jdt.internal.core.dom.rewrite.TokenScanner;
+import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
+import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
+
+/** These contain a mix of the following:
+ * <ul>
+ * <li> 'dependency free' method wrappers that cross the shadowloader barrier.
+ * <li> methods that directly patch, <em>but</em>, these should ALWAYS be transplanted.
+ * </ul>
+ *
+ * <strong>This class lives on the outside of the shadowloader barrier, and as a consequence, cannot access any other lombok code except other
+ * code in the {@code lombok.launch} package!</strong>.
+ * <p>
+ * This class is package private with lots of public inner static classes. This hides all of them from IDE autocomplete dialogs and such but at the JVM
+ * level the inner static class are just plain public, which is important, because calls to the contents of these inner static classes are injected into
+ * various eclipse classes verbatim, and if they weren't public, the verifier wouldn't accept it.
+ */
+final class PatchFixesHider {
+
+ /** These utility methods are only used 'internally', but because of transplant methods, the class (and its methods) still have to be public! */
+ public static final class Util {
+ private static ClassLoader shadowLoader;
+
+ public static Class<?> shadowLoadClass(String name) {
+ try {
+ if (shadowLoader == null) {
+ try {
+ Class.forName("lombok.core.LombokNode");
+ // If we get here, then lombok is already available.
+ shadowLoader = Util.class.getClassLoader();
+ } catch (ClassNotFoundException e) {
+ // If we get here, it isn't, and we should use the shadowloader.
+ shadowLoader = Main.createShadowClassLoader();
+ }
+ }
+
+ return Class.forName(name, true, shadowLoader);
+ } catch (ClassNotFoundException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ public static Method findMethod(Class<?> type, String name, Class<?>... parameterTypes) {
+ try {
+ return type.getDeclaredMethod(name, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ public static Object invokeMethod(Method method, Object... args) {
+ try {
+ return method.invoke(null, args);
+ } catch (IllegalAccessException e) {
+ throw sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw sneakyThrow(e.getCause());
+ }
+ }
+
+ private static RuntimeException sneakyThrow(Throwable t) {
+ if (t == null) throw new NullPointerException("t");
+ Util.<RuntimeException>sneakyThrow0(t);
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
+ throw (T)t;
+ }
+ }
+
+ /** Contains patch fixes that are dependent on lombok internals. */
+ public static final class LombokDeps {
+ public static final Method ADD_LOMBOK_NOTES;
+ public static final Method POST_COMPILER_BYTES_STRING;
+ public static final Method POST_COMPILER_OUTPUTSTREAM;
+ public static final Method POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchFixesShadowLoaded");
+ ADD_LOMBOK_NOTES = Util.findMethod(shadowed, "addLombokNotesToEclipseAboutDialog", String.class, String.class);
+ POST_COMPILER_BYTES_STRING = Util.findMethod(shadowed, "runPostCompiler", byte[].class, String.class);
+ POST_COMPILER_OUTPUTSTREAM = Util.findMethod(shadowed, "runPostCompiler", OutputStream.class);
+ POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING = Util.findMethod(shadowed, "runPostCompiler", BufferedOutputStream.class, String.class, String.class);
+ }
+
+ public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
+ return (String) Util.invokeMethod(LombokDeps.ADD_LOMBOK_NOTES, origReturnValue, key);
+ }
+
+ public static byte[] runPostCompiler(byte[] bytes, String fileName) {
+ return (byte[]) Util.invokeMethod(LombokDeps.POST_COMPILER_BYTES_STRING, bytes, fileName);
+ }
+
+ public static OutputStream runPostCompiler(OutputStream out) throws IOException {
+ return (OutputStream) Util.invokeMethod(LombokDeps.POST_COMPILER_OUTPUTSTREAM, out);
+ }
+
+ public static BufferedOutputStream runPostCompiler(BufferedOutputStream out, String path, String name) throws IOException {
+ return (BufferedOutputStream) Util.invokeMethod(LombokDeps.POST_COMPILER_BUFFEREDOUTPUTSTREAM_STRING_STRING, out, path, name);
+ }
+ }
+
+ public static final class Transform {
+ private static final Method TRANSFORM;
+ private static final Method TRANSFORM_SWAPPED;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.TransformEclipseAST");
+ TRANSFORM = Util.findMethod(shadowed, "transform", Parser.class, CompilationUnitDeclaration.class);
+ TRANSFORM_SWAPPED = Util.findMethod(shadowed, "transform_swapped", CompilationUnitDeclaration.class, Parser.class);
+ }
+
+ public static void transform(Parser parser, CompilationUnitDeclaration ast) throws IOException {
+ Util.invokeMethod(TRANSFORM, parser, ast);
+ }
+
+ public static void transform_swapped(CompilationUnitDeclaration ast, Parser parser) throws IOException {
+ Util.invokeMethod(TRANSFORM_SWAPPED, ast, parser);
+ }
+ }
+
+ /** Contains patch code to support {@code @Delegate} */
+ public static final class Delegate {
+ private static final Method HANDLE_DELEGATE_FOR_TYPE;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchDelegatePortal");
+ HANDLE_DELEGATE_FOR_TYPE = Util.findMethod(shadowed, "handleDelegateForType", Object.class);
+ }
+
+ public static boolean handleDelegateForType(Object classScope) {
+ return (Boolean) Util.invokeMethod(HANDLE_DELEGATE_FOR_TYPE, classScope);
+ }
+ }
+
+ /** Contains patch code to support {@code val} (eclipse specific) */
+ public static final class ValPortal {
+ private static final Method COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE;
+ private static final Method COPY_INITIALIZATION_OF_LOCAL_DECLARATION;
+ private static final Method ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT;
+ private static final Method ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchValEclipsePortal");
+ COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE = Util.findMethod(shadowed, "copyInitializationOfForEachIterable", Object.class);
+ COPY_INITIALIZATION_OF_LOCAL_DECLARATION = Util.findMethod(shadowed, "copyInitializationOfLocalDeclaration", Object.class);
+ ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT = Util.findMethod(shadowed, "addFinalAndValAnnotationToVariableDeclarationStatement", Object.class, Object.class, Object.class);
+ ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION = Util.findMethod(shadowed, "addFinalAndValAnnotationToSingleVariableDeclaration", Object.class, Object.class, Object.class);
+ }
+
+ public static void copyInitializationOfForEachIterable(Object parser) {
+ Util.invokeMethod(COPY_INITIALIZATION_OF_FOR_EACH_ITERABLE, parser);
+ }
+
+ public static void copyInitializationOfLocalDeclaration(Object parser) {
+ Util.invokeMethod(COPY_INITIALIZATION_OF_LOCAL_DECLARATION, parser);
+ }
+
+ public static void addFinalAndValAnnotationToVariableDeclarationStatement(Object converter, Object out, Object in) {
+ Util.invokeMethod(ADD_FINAL_AND_VAL_ANNOTATION_TO_VARIABLE_DECLARATION_STATEMENT, converter, out, in);
+ }
+
+ public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, Object out, Object in) {
+ Util.invokeMethod(ADD_FINAL_AND_VAL_ANNOTATION_TO_SINGLE_VARIABLE_DECLARATION, converter, out, in);
+ }
+ }
+
+ /** Contains patch code to support {@code val} (eclipse and ecj) */
+ public static final class Val {
+ private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED;
+ private static final Method SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2;
+ private static final Method HANDLE_VAL_FOR_LOCAL_DECLARATION;
+ private static final Method HANDLE_VAL_FOR_FOR_EACH;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchVal");
+ SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled", Expression.class, BlockScope.class);
+ SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2 = Util.findMethod(shadowed, "skipResolveInitializerIfAlreadyCalled2", Expression.class, BlockScope.class, LocalDeclaration.class);
+ HANDLE_VAL_FOR_LOCAL_DECLARATION = Util.findMethod(shadowed, "handleValForLocalDeclaration", LocalDeclaration.class, BlockScope.class);
+ HANDLE_VAL_FOR_FOR_EACH = Util.findMethod(shadowed, "handleValForForEach", ForeachStatement.class, BlockScope.class);
+ }
+
+ public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) {
+ return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED, expr, scope);
+ }
+
+ public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) {
+ return (TypeBinding) Util.invokeMethod(SKIP_RESOLVE_INITIALIZER_IF_ALREADY_CALLED2, expr, scope, decl);
+ }
+
+ public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) {
+ return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_LOCAL_DECLARATION, local, scope);
+ }
+
+ public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) {
+ return (Boolean) Util.invokeMethod(HANDLE_VAL_FOR_FOR_EACH, forEach, scope);
+ }
+ }
+
+ /** Contains patch code to support {@code @ExtensionMethod} */
+ public static final class ExtensionMethod {
+ private static final Method RESOLVE_TYPE;
+ private static final Method ERROR_NO_METHOD_FOR;
+ private static final Method INVALID_METHOD, INVALID_METHOD2;
+
+ static {
+ Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchExtensionMethod");
+ RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", TypeBinding.class, MessageSend.class, BlockScope.class);
+ ERROR_NO_METHOD_FOR = Util.findMethod(shadowed, "errorNoMethodFor", ProblemReporter.class, MessageSend.class, TypeBinding.class, TypeBinding[].class);
+ INVALID_METHOD = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class);
+ INVALID_METHOD2 = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class, Scope.class);
+ }
+
+ public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) {
+ return (TypeBinding) Util.invokeMethod(RESOLVE_TYPE, resolvedType, methodCall, scope);
+ }
+
+ public static void errorNoMethodFor(ProblemReporter problemReporter, MessageSend messageSend, TypeBinding recType, TypeBinding[] params) {
+ Util.invokeMethod(ERROR_NO_METHOD_FOR, problemReporter, messageSend, recType, params);
+ }
+
+ public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
+ Util.invokeMethod(INVALID_METHOD, problemReporter, messageSend, method);
+ }
+
+ public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) {
+ Util.invokeMethod(INVALID_METHOD2, problemReporter, messageSend, method, scope);
+ }
+ }
+
+ /**
+ * Contains a mix of methods: ecj only, ecj+eclipse, and eclipse only. As a consequence, _EVERY_ method from here used for ecj MUST be
+ * transplanted, as ecj itself cannot load this class (signatures refer to things that don't exist in ecj-only mode).
+ * <p>
+ * Because of usage of transplant(), a bunch of these contain direct code and don't try to cross the shadowloader barrier.
+ */
+ public static final class PatchFixes {
+ public static boolean isGenerated(org.eclipse.jdt.core.dom.ASTNode node) {
+ boolean result = false;
+ try {
+ result = ((Boolean)node.getClass().getField("$isGenerated").get(node)).booleanValue();
+ if (!result && node.getParent() != null && node.getParent() instanceof org.eclipse.jdt.core.dom.QualifiedName) {
+ result = isGenerated(node.getParent());
+ }
+ } catch (Exception e) {
+ // better to assume it isn't generated
+ }
+ return result;
+ }
+
+ public static boolean isListRewriteOnGeneratedNode(org.eclipse.jdt.core.dom.rewrite.ListRewrite rewrite) {
+ return isGenerated(rewrite.getParent());
+ }
+
+ public static boolean returnFalse(java.lang.Object object) {
+ return false;
+ }
+
+ public static boolean returnTrue(java.lang.Object object) {
+ return true;
+ }
+
+ @java.lang.SuppressWarnings({"unchecked", "rawtypes"}) public static java.util.List removeGeneratedNodes(java.util.List list) {
+ try {
+ java.util.List realNodes = new java.util.ArrayList(list.size());
+ for (java.lang.Object node : list) {
+ if(!isGenerated(((org.eclipse.jdt.core.dom.ASTNode)node))) {
+ realNodes.add(node);
+ }
+ }
+ return realNodes;
+ } catch (Exception e) {
+ }
+ return list;
+ }
+
+ public static java.lang.String getRealMethodDeclarationSource(java.lang.String original, Object processor, org.eclipse.jdt.core.dom.MethodDeclaration declaration) throws Exception {
+ if (!isGenerated(declaration)) return original;
+
+ List<org.eclipse.jdt.core.dom.Annotation> annotations = new ArrayList<org.eclipse.jdt.core.dom.Annotation>();
+ for (Object modifier : declaration.modifiers()) {
+ if (modifier instanceof org.eclipse.jdt.core.dom.Annotation) {
+ org.eclipse.jdt.core.dom.Annotation annotation = (org.eclipse.jdt.core.dom.Annotation)modifier;
+ String qualifiedAnnotationName = annotation.resolveTypeBinding().getQualifiedName();
+ if (!"java.lang.Override".equals(qualifiedAnnotationName) && !"java.lang.SuppressWarnings".equals(qualifiedAnnotationName)) annotations.add(annotation);
+ }
+ }
+
+ StringBuilder signature = new StringBuilder();
+ addAnnotations(annotations, signature);
+
+ if ((Boolean)processor.getClass().getDeclaredField("fPublic").get(processor)) signature.append("public ");
+ if ((Boolean)processor.getClass().getDeclaredField("fAbstract").get(processor)) signature.append("abstract ");
+
+ signature
+ .append(declaration.getReturnType2().toString())
+ .append(" ").append(declaration.getName().getFullyQualifiedName())
+ .append("(");
+
+ boolean first = true;
+ for (Object parameter : declaration.parameters()) {
+ if (!first) signature.append(", ");
+ first = false;
+ // We should also add the annotations of the parameters
+ signature.append(parameter);
+ }
+
+ signature.append(");");
+ return signature.toString();
+ }
+
+ // part of getRealMethodDeclarationSource(...)
+ public static void addAnnotations(List<org.eclipse.jdt.core.dom.Annotation> annotations, StringBuilder signature) {
+ /*
+ * We SHOULD be able to handle the following cases:
+ * @Override
+ * @Override()
+ * @SuppressWarnings("all")
+ * @SuppressWarnings({"all", "unused"})
+ * @SuppressWarnings(value = "all")
+ * @SuppressWarnings(value = {"all", "unused"})
+ * @EqualsAndHashCode(callSuper=true, of="id")
+ *
+ * Currently, we only seem to correctly support:
+ * @Override
+ * @Override() N.B. We lose the parentheses here, since there are no values. No big deal.
+ * @SuppressWarnings("all")
+ */
+ for (org.eclipse.jdt.core.dom.Annotation annotation : annotations) {
+ List<String> values = new ArrayList<String>();
+ if (annotation.isSingleMemberAnnotation()) {
+ org.eclipse.jdt.core.dom.SingleMemberAnnotation smAnn = (org.eclipse.jdt.core.dom.SingleMemberAnnotation) annotation;
+ values.add(smAnn.getValue().toString());
+ } else if (annotation.isNormalAnnotation()) {
+ org.eclipse.jdt.core.dom.NormalAnnotation normalAnn = (org.eclipse.jdt.core.dom.NormalAnnotation) annotation;
+ for (Object value : normalAnn.values()) values.add(value.toString());
+ }
+
+ signature.append("@").append(annotation.resolveTypeBinding().getQualifiedName());
+ if (!values.isEmpty()) {
+ signature.append("(");
+ boolean first = true;
+ for (String string : values) {
+ if (!first) signature.append(", ");
+ first = false;
+ signature.append('"').append(string).append('"');
+ }
+ signature.append(")");
+ }
+ signature.append(" ");
+ }
+ }
+
+ public static org.eclipse.jdt.core.dom.MethodDeclaration getRealMethodDeclarationNode(org.eclipse.jdt.core.IMethod sourceMethod, org.eclipse.jdt.core.dom.CompilationUnit cuUnit) throws JavaModelException {
+ MethodDeclaration methodDeclarationNode = ASTNodeSearchUtil.getMethodDeclarationNode(sourceMethod, cuUnit);
+ if (isGenerated(methodDeclarationNode)) {
+ IType declaringType = sourceMethod.getDeclaringType();
+ Stack<IType> typeStack = new Stack<IType>();
+ while (declaringType != null) {
+ typeStack.push(declaringType);
+ declaringType = declaringType.getDeclaringType();
+ }
+
+ IType rootType = typeStack.pop();
+ org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = findTypeDeclaration(rootType, cuUnit.types());
+ while (!typeStack.isEmpty() && typeDeclaration != null) {
+ typeDeclaration = findTypeDeclaration(typeStack.pop(), typeDeclaration.bodyDeclarations());
+ }
+
+ if (typeStack.isEmpty() && typeDeclaration != null) {
+ String methodName = sourceMethod.getElementName();
+ for (Object declaration : typeDeclaration.bodyDeclarations()) {
+ if (declaration instanceof org.eclipse.jdt.core.dom.MethodDeclaration) {
+ org.eclipse.jdt.core.dom.MethodDeclaration methodDeclaration = (org.eclipse.jdt.core.dom.MethodDeclaration) declaration;
+ if (methodDeclaration.getName().toString().equals(methodName)) {
+ return methodDeclaration;
+ }
+ }
+ }
+ }
+ }
+ return methodDeclarationNode;
+ }
+
+ // part of getRealMethodDeclarationNode
+ public static org.eclipse.jdt.core.dom.AbstractTypeDeclaration findTypeDeclaration(IType searchType, List<?> nodes) {
+ for (Object object : nodes) {
+ if (object instanceof org.eclipse.jdt.core.dom.AbstractTypeDeclaration) {
+ org.eclipse.jdt.core.dom.AbstractTypeDeclaration typeDeclaration = (org.eclipse.jdt.core.dom.AbstractTypeDeclaration) object;
+ if (typeDeclaration.getName().toString().equals(searchType.getElementName()))
+ return typeDeclaration;
+ }
+ }
+ return null;
+ }
+
+ public static int getSourceEndFixed(int sourceEnd, org.eclipse.jdt.internal.compiler.ast.ASTNode node) throws Exception {
+ if (sourceEnd == -1) {
+ org.eclipse.jdt.internal.compiler.ast.ASTNode object = (org.eclipse.jdt.internal.compiler.ast.ASTNode)node.getClass().getField("$generatedBy").get(node);
+ if (object != null) {
+ return object.sourceEnd;
+ }
+ }
+ return sourceEnd;
+ }
+
+ public static int fixRetrieveStartingCatchPosition(int original, int start) {
+ return original == -1 ? start : original;
+ }
+
+ public static int fixRetrieveIdentifierEndPosition(int original, int end) {
+ return original == -1 ? end : original;
+ }
+
+ public static int fixRetrieveEllipsisStartPosition(int original, int end) {
+ return original == -1 ? end : original;
+ }
+
+ public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) {
+// if (original == -1) {
+// Thread.dumpStack();
+// }
+ return original == -1 ? end : original;
+ }
+
+ public static int fixRetrieveRightBraceOrSemiColonPosition(int retVal, AbstractMethodDeclaration amd) {
+ if (retVal != -1 || amd == null) return retVal;
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(amd) != null;
+ if (isGenerated) return amd.declarationSourceEnd;
+ return -1;
+ }
+
+ public static int fixRetrieveRightBraceOrSemiColonPosition(int retVal, FieldDeclaration fd) {
+ if (retVal != -1 || fd == null) return retVal;
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(fd) != null;
+ if (isGenerated) return fd.declarationSourceEnd;
+ return -1;
+ }
+
+ public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24
+
+ public static boolean checkBit24(Object node) throws Exception {
+ int bits = (Integer)(node.getClass().getField("bits").get(node));
+ return (bits & ALREADY_PROCESSED_FLAG) != 0;
+ }
+
+ public static boolean skipRewritingGeneratedNodes(org.eclipse.jdt.core.dom.ASTNode node) throws Exception {
+ return ((Boolean) node.getClass().getField("$isGenerated").get(node)).booleanValue();
+ }
+
+ public static void setIsGeneratedFlag(org.eclipse.jdt.core.dom.ASTNode domNode,
+ org.eclipse.jdt.internal.compiler.ast.ASTNode internalNode) throws Exception {
+
+ if (internalNode == null || domNode == null) return;
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(internalNode) != null;
+ if (isGenerated) domNode.getClass().getField("$isGenerated").set(domNode, true);
+ }
+
+ public static void setIsGeneratedFlagForName(org.eclipse.jdt.core.dom.Name name, Object internalNode) throws Exception {
+ if (internalNode instanceof org.eclipse.jdt.internal.compiler.ast.ASTNode) {
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get((org.eclipse.jdt.internal.compiler.ast.ASTNode) internalNode) != null;
+ if (isGenerated) name.getClass().getField("$isGenerated").set(name, true);
+ }
+ }
+
+ public static RewriteEvent[] listRewriteHandleGeneratedMethods(RewriteEvent parent) {
+ RewriteEvent[] children = parent.getChildren();
+ List<RewriteEvent> newChildren = new ArrayList<RewriteEvent>();
+ List<RewriteEvent> modifiedChildren = new ArrayList<RewriteEvent>();
+ for (int i = 0; i < children.length; i++) {
+ RewriteEvent child = children[i];
+ boolean isGenerated = isGenerated((org.eclipse.jdt.core.dom.ASTNode) child.getOriginalValue());
+ if (isGenerated) {
+ boolean isReplacedOrRemoved = child.getChangeKind() == RewriteEvent.REPLACED || child.getChangeKind() == RewriteEvent.REMOVED;
+ boolean convertingFromMethod = child.getOriginalValue() instanceof org.eclipse.jdt.core.dom.MethodDeclaration;
+ if (isReplacedOrRemoved && convertingFromMethod && child.getNewValue() != null) {
+ modifiedChildren.add(new NodeRewriteEvent(null, child.getNewValue()));
+ }
+ } else {
+ newChildren.add(child);
+ }
+ }
+ // Since Eclipse doesn't honor the "insert at specified location" for already existing members,
+ // we'll just add them last
+ newChildren.addAll(modifiedChildren);
+ return newChildren.toArray(new RewriteEvent[newChildren.size()]);
+ }
+
+ public static int getTokenEndOffsetFixed(TokenScanner scanner, int token, int startOffset, Object domNode) throws CoreException {
+ boolean isGenerated = false;
+ try {
+ isGenerated = (Boolean) domNode.getClass().getField("$isGenerated").get(domNode);
+ } catch (Exception e) {
+ // If this fails, better to break some refactor scripts than to crash eclipse.
+ }
+ if (isGenerated) return -1;
+ return scanner.getTokenEndOffset(token, startOffset);
+ }
+
+ public static IMethod[] removeGeneratedMethods(IMethod[] methods) throws Exception {
+ List<IMethod> result = new ArrayList<IMethod>();
+ for (IMethod m : methods) {
+ if (m.getNameRange().getLength() > 0 && !m.getNameRange().equals(m.getSourceRange())) result.add(m);
+ }
+ return result.size() == methods.length ? methods : result.toArray(new IMethod[result.size()]);
+ }
+
+ public static SearchMatch[] removeGenerated(SearchMatch[] returnValue) {
+ List<SearchMatch> result = new ArrayList<SearchMatch>();
+ for (int j = 0; j < returnValue.length; j++) {
+ SearchMatch searchResult = returnValue[j];
+ if (searchResult.getElement() instanceof IField) {
+ IField field = (IField) searchResult.getElement();
+
+ // can not check for value=lombok because annotation is
+ // not fully resolved
+ IAnnotation annotation = field.getAnnotation("Generated");
+ if (annotation != null) {
+ // Method generated at field location, skip
+ continue;
+ }
+
+ }
+ result.add(searchResult);
+ }
+ return result.toArray(new SearchMatch[result.size()]);
+ }
+
+ public static SearchResultGroup[] createFakeSearchResult(SearchResultGroup[] returnValue,
+ Object/*
+ * org.eclipse.jdt.internal.corext.refactoring.rename.
+ * RenameFieldProcessor
+ */ processor) throws Exception {
+ if (returnValue == null || returnValue.length == 0) {
+ // if no matches were found, check if Data annotation is present on the class
+ Field declaredField = processor.getClass().getDeclaredField("fField");
+ if (declaredField != null) {
+ declaredField.setAccessible(true);
+ SourceField fField = (SourceField) declaredField.get(processor);
+ IAnnotation dataAnnotation = fField.getDeclaringType().getAnnotation("Data");
+ if (dataAnnotation != null) {
+ // add fake item, to make refactoring checks pass
+ return new SearchResultGroup[] {new SearchResultGroup(null, new SearchMatch[1])};
+ }
+ }
+ }
+ return returnValue;
+ }
+
+ public static SimpleName[] removeGeneratedSimpleNames(SimpleName[] in) throws Exception {
+ Field f = SimpleName.class.getField("$isGenerated");
+
+ int count = 0;
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) count++;
+ }
+ if (count == in.length) return in;
+ SimpleName[] newSimpleNames = new SimpleName[count];
+ count = 0;
+ for (int i = 0; i < in.length; i++) {
+ if (in[i] == null || !((Boolean)f.get(in[i])).booleanValue()) newSimpleNames[count++] = in[i];
+ }
+ return newSimpleNames;
+ }
+
+ public static Annotation[] convertAnnotations(Annotation[] out, IAnnotatable annotatable) {
+ IAnnotation[] in;
+
+ try {
+ in = annotatable.getAnnotations();
+ } catch (Exception e) {
+ return out;
+ }
+
+ if (out == null) return null;
+ int toWrite = 0;
+
+ for (int idx = 0; idx < out.length; idx++) {
+ String oName = new String(out[idx].type.getLastToken());
+ boolean found = false;
+ for (IAnnotation i : in) {
+ String name = i.getElementName();
+ int li = name.lastIndexOf('.');
+ if (li > -1) name = name.substring(li + 1);
+ if (name.equals(oName)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) out[idx] = null;
+ else toWrite++;
+ }
+
+ Annotation[] replace = out;
+ if (toWrite < out.length) {
+ replace = new Annotation[toWrite];
+ int idx = 0;
+ for (int i = 0; i < out.length; i++) {
+ if (out[i] == null) continue;
+ replace[idx++] = out[i];
+ }
+ }
+
+ return replace;
+ }
+ }
+}
diff --git a/src/installer/lombok/installer/IdeLocation.java b/src/installer/lombok/installer/IdeLocation.java
index 4e3a7e41..c3853867 100644
--- a/src/installer/lombok/installer/IdeLocation.java
+++ b/src/installer/lombok/installer/IdeLocation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2014 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 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
@@ -25,7 +25,6 @@ import java.io.File;
import java.io.IOException;
import java.net.URL;
-import lombok.installer.eclipse.EclipseFinder;
import lombok.patcher.ClassRootFinder;
/**
@@ -46,7 +45,7 @@ public abstract class IdeLocation {
* a jar that wasn't accessed via the file-system, or if its started via e.g. unpacking the jar.
*/
public static File findOurJar() {
- return new File(ClassRootFinder.findClassRootOfClass(IdeFinder.class));
+ return new File(ClassRootFinder.findClassRootOfClass(OsUtils.class));
}
@Override public String toString() {
@@ -70,7 +69,7 @@ public abstract class IdeLocation {
private static final String LEGAL_PATH_CHARS_WINDOWS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/:\\ ";
public static String escapePath(String path) {
StringBuilder out = new StringBuilder();
- String legalChars = IdeFinder.getOS() == EclipseFinder.OS.UNIX ? LEGAL_PATH_CHARS : LEGAL_PATH_CHARS_WINDOWS;
+ String legalChars = OsUtils.getOS() == OsUtils.OS.UNIX ? LEGAL_PATH_CHARS : LEGAL_PATH_CHARS_WINDOWS;
for (char c : path.toCharArray()) {
if (legalChars.indexOf(c) == -1) out.append('\\');
out.append(c);
diff --git a/src/installer/lombok/installer/IdeLocationProvider.java b/src/installer/lombok/installer/IdeLocationProvider.java
index 933a5989..c4b64141 100644
--- a/src/installer/lombok/installer/IdeLocationProvider.java
+++ b/src/installer/lombok/installer/IdeLocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,20 +21,30 @@
*/
package lombok.installer;
+import java.util.List;
import java.util.regex.Pattern;
-import lombok.installer.IdeFinder.OS;
-
public interface IdeLocationProvider {
/**
* @throws CorruptedIdeLocationException
* Only throw this exception if the location seems like a proper installation except there's something wrong with it.
* Do not throw it (just return {@code null}) if there's nothing there or it looks absolutely nothing like your IDE.
*/
- public abstract IdeLocation create(String path) throws CorruptedIdeLocationException;
+ IdeLocation create(String path) throws CorruptedIdeLocationException;
+
+ /**
+ * Return the usual name of the IDE executable or other obvious marker of an IDE installation on the current platform.
+ */
+ Pattern getLocationSelectors();
/**
- * Return the usual name of the IDE executable or other obvious marker of an IDE installation on the provided platform.
+ * Look for installations of your IDE in the usual places.
+ *
+ * @param locations Add to this list any valid locations that you found.
+ * @param problems
+ * Add to this list any locations that look like installations,
+ * but have problems that prevent you from installing/uninstalling from them. DONT add to this list
+ * any common locations that have no installation at all - only add near misses.
*/
- public abstract Pattern getLocationSelectors(OS os);
+ void findIdes(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems);
}
diff --git a/src/installer/lombok/installer/Installer.java b/src/installer/lombok/installer/Installer.java
index b9faeebd..94cc1a45 100644
--- a/src/installer/lombok/installer/Installer.java
+++ b/src/installer/lombok/installer/Installer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2010 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 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
@@ -38,7 +38,7 @@ import lombok.Lombok;
import lombok.core.LombokApp;
import lombok.core.SpiLoadUtil;
import lombok.core.Version;
-import lombok.installer.IdeFinder.OS;
+import lombok.installer.OsUtils.OS;
import lombok.patcher.ClassRootFinder;
import org.mangosdk.spi.ProviderFor;
@@ -56,7 +56,7 @@ import com.zwitserloot.cmdreader.Shorthand;
* and looks in some common places on Mac OS X, Linux and Windows.
*/
public class Installer {
- static final URI ABOUT_LOMBOK_URL = URI.create("http://projectlombok.org");
+ static final URI ABOUT_LOMBOK_URL = URI.create("https://projectlombok.org");
static final List<IdeLocationProvider> locationProviders;
static {
@@ -72,10 +72,9 @@ public class Installer {
}
static List<Pattern> getIdeExecutableNames() {
- OS os = IdeFinder.getOS();
List<Pattern> list = new ArrayList<Pattern>();
for (IdeLocationProvider provider : locationProviders) {
- Pattern p = provider.getLocationSelectors(os);
+ Pattern p = provider.getLocationSelectors();
if (p != null) list.add(p);
}
return list;
@@ -91,12 +90,8 @@ public class Installer {
}
static void autoDiscover(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems) {
- try {
- for (IdeFinder finder : SpiLoadUtil.findServices(IdeFinder.class)) {
- finder.findIdes(locations, problems);
- }
- } catch (IOException e) {
- throw Lombok.sneakyThrow(e);
+ for (IdeLocationProvider provider : locationProviders) {
+ provider.findIdes(locations, problems);
}
}
@@ -160,7 +155,7 @@ public class Installer {
}
private static int guiInstaller() {
- if (IdeFinder.getOS() == OS.MAC_OS_X) {
+ if (OsUtils.getOS() == OS.MAC_OS_X) {
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Lombok Installer");
System.setProperty("com.apple.macos.use-file-dialog-packages", "true");
}
diff --git a/src/installer/lombok/installer/InstallerGUI.java b/src/installer/lombok/installer/InstallerGUI.java
index 5717948a..231e2d3c 100644
--- a/src/installer/lombok/installer/InstallerGUI.java
+++ b/src/installer/lombok/installer/InstallerGUI.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2010 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 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
@@ -39,6 +39,8 @@ import java.awt.event.ActionListener;
import java.awt.font.TextAttribute;
import java.io.File;
import java.io.FilenameFilter;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
@@ -62,12 +64,15 @@ import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
+import javax.swing.JTextPane;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
import javax.swing.filechooser.FileFilter;
+import javax.swing.text.html.HTMLDocument;
import lombok.core.Version;
-import lombok.installer.IdeFinder.OS;
+import lombok.installer.OsUtils.OS;
/**
* The lombok GUI installer.
@@ -75,6 +80,7 @@ import lombok.installer.IdeFinder.OS;
* Also offers info on what this installer does in case people want to instrument their IDE manually.
*/
public class InstallerGUI {
+ private static final int INSTALLER_WINDOW_WIDTH = 662;
static final AtomicReference<Integer> exitMarker = new AtomicReference<Integer>();
private JFrame appWindow;
@@ -85,6 +91,7 @@ public class InstallerGUI {
private Component ideArea;
private Component uninstallArea;
private Component howIWorkArea;
+ private Component successArea;
private Box uninstallBox;
private JHyperLink uninstallButton;
@@ -101,6 +108,7 @@ public class InstallerGUI {
public InstallerGUI() {
appWindow = new JFrame(String.format("Project Lombok v%s - Installer", Version.getVersion()));
+ appWindow.setLocationByPlatform(true);
appWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
appWindow.setResizable(false);
appWindow.setIconImage(Toolkit.getDefaultToolkit().getImage(Installer.class.getResource("lombokIcon.png")));
@@ -112,6 +120,8 @@ public class InstallerGUI {
uninstallArea.setVisible(false);
howIWorkArea = buildHowIWorkArea();
howIWorkArea.setVisible(false);
+ successArea = buildSuccessArea();
+ successArea.setVisible(false);
buildChrome(appWindow.getContentPane());
appWindow.pack();
} catch (Throwable t) {
@@ -152,6 +162,7 @@ public class InstallerGUI {
howIWorkArea.setVisible(false);
javacArea.setVisible(true);
ideArea.setVisible(true);
+ successArea.setVisible(false);
appWindow.pack();
}
});
@@ -159,9 +170,86 @@ public class InstallerGUI {
constraints.gridy = 2;
container.add(buttonBar, constraints);
+ container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415));
+ container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415));
return container;
}
+ private void showSuccess(String installSpecific) {
+ successExplanation.setText(SUCCESS_EXPLANATION.replace("%%%", installSpecific));
+ howIWorkArea.setVisible(false);
+ javacArea.setVisible(false);
+ ideArea.setVisible(false);
+ successArea.setVisible(true);
+ appWindow.pack();
+ }
+
+ private JLabel successExplanation;
+
+ private Component buildSuccessArea() {
+ JPanel container = new JPanel();
+
+ container.setLayout(new GridBagLayout());
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.anchor = GridBagConstraints.WEST;
+
+ JLabel title;
+ container.add(title = new JLabel(SUCCESS_TITLE), constraints);
+ title.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH - 82, 20));
+ title.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH - 82, 20));
+
+ constraints.gridy = 1;
+ constraints.insets = new Insets(8, 0, 0, 16);
+ container.add(successExplanation = new JLabel(SUCCESS_EXPLANATION), constraints);
+ successExplanation.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH - 82, 175));
+ successExplanation.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH - 82, 175));
+
+ constraints.gridy++;
+ constraints.fill = GridBagConstraints.BOTH;
+
+ JTextPane notes = new JTextPane();
+ notes.setContentType("text/html");
+ notes.setText(readChangeLog());
+ notes.setEditable(false);
+ notes.setOpaque(false);
+ notes.setBorder(null);
+ notes.setSelectionStart(0);
+ notes.setSelectionEnd(0);
+
+ Font font = UIManager.getFont("Label.font");
+ String bodyRule = "body { font-family: " + font.getFamily() + "; font-size: " + font.getSize() + "pt; }";
+ ((HTMLDocument) notes.getDocument()).getStyleSheet().addRule(bodyRule);
+ JScrollPane scroller = new JScrollPane(notes);
+ container.add(scroller, constraints);
+ scroller.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH - 82, 200));
+ scroller.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH - 82, 200));
+ container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415));
+ container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415));
+ return container;
+ }
+
+ private String readChangeLog() {
+ InputStream in = Installer.class.getResourceAsStream("/latestchanges.html");
+ try {
+ char[] buff = new char[8192];
+ StringBuilder contents = new StringBuilder();
+ InputStreamReader reader = new InputStreamReader(in, "UTF-8");
+ while (true) {
+ int read = reader.read(buff);
+ if (read == -1) break;
+ contents.append(buff, 0, read);
+ }
+ return "<html>" + contents + "</html>";
+ } catch (Exception e) {
+ return "No Changelog available";
+ }
+ finally {
+ try {
+ in.close();
+ } catch (Exception ignore){ /**/}
+ }
+ }
+
private Component buildUninstallArea() {
JPanel container = new JPanel();
@@ -209,6 +297,8 @@ public class InstallerGUI {
constraints.gridy = 4;
container.add(buttonBar, constraints);
+ container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415));
+ container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415));
return container;
}
@@ -231,6 +321,8 @@ public class InstallerGUI {
constraints.gridy = 2;
container.add(example, constraints);
+ container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 105));
+ container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 105));
return container;
}
@@ -288,11 +380,11 @@ public class InstallerGUI {
if (locations.size() + problems.size() == 0) {
JOptionPane.showMessageDialog(appWindow,
- "I can't find any IDEs on your computer.\n" +
- "If you have IDEs installed on this computer, please use the " +
- "'Specify Location...' button to manually point out the \n" +
- "location of your IDE installation to me. Thanks!",
- "Can't find IDE", JOptionPane.INFORMATION_MESSAGE);
+ "I can't find any IDEs on your computer.\n" +
+ "If you have IDEs installed on this computer, please use the " +
+ "'Specify Location...' button to manually point out the \n" +
+ "location of your IDE installation to me. Thanks!",
+ "Can't find IDE", JOptionPane.INFORMATION_MESSAGE);
}
}
});
@@ -312,7 +404,7 @@ public class InstallerGUI {
final List<Pattern> exeNames = Installer.getIdeExecutableNames();
String file = null;
- if (IdeFinder.getOS() == OS.MAC_OS_X) {
+ if (OsUtils.getOS() == OS.MAC_OS_X) {
FileDialog chooser = new FileDialog(appWindow);
chooser.setMode(FileDialog.LOAD);
@@ -418,7 +510,8 @@ public class InstallerGUI {
uninstallPlaceholder.setVisible(false);
container.add(uninstallPlaceholder, constraints);
-
+ container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 296));
+ container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 296));
return container;
}
@@ -426,6 +519,7 @@ public class InstallerGUI {
javacArea.setVisible(false);
ideArea.setVisible(false);
howIWorkArea.setVisible(true);
+ successArea.setVisible(false);
appWindow.pack();
}
@@ -452,6 +546,7 @@ public class InstallerGUI {
spinner.setOpaque(true);
spinner.setLayout(new FlowLayout());
spinner.add(new JLabel(new ImageIcon(Installer.class.getResource("loading.gif"))));
+ final Container appWindowContent = appWindow.getContentPane();
appWindow.setContentPane(spinner);
final AtomicInteger successes = new AtomicInteger();
@@ -499,20 +594,13 @@ public class InstallerGUI {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override public void run() {
+ appWindow.setContentPane(appWindowContent);
+ appWindow.pack();
StringBuilder installSpecific = new StringBuilder();
for (String installSpecificMessage : installSpecificMessages) {
installSpecific.append("<br>").append(installSpecificMessage);
}
- JOptionPane.showMessageDialog(appWindow,
- "<html>Lombok has been installed on the selected IDE installations.<br>" +
- "Don't forget to add <code>lombok.jar</code> to your projects, and restart your IDE!" + installSpecific.toString() + "</html>",
- "Install successful",
- JOptionPane.INFORMATION_MESSAGE);
- appWindow.setVisible(false);
- synchronized (exitMarker) {
- exitMarker.set(0);
- exitMarker.notifyAll();
- }
+ showSuccess(installSpecific.toString());
}
});
} catch (Exception e) {
@@ -710,6 +798,8 @@ public class InstallerGUI {
appWindowContainer.add(howIWorkArea, constraints);
+ appWindowContainer.add(successArea, constraints);
+
constraints.gridy++;
constraints.gridwidth = 2;
constraints.gridx = 0;
@@ -718,9 +808,33 @@ public class InstallerGUI {
constraints.ipadx = 0;
constraints.ipady = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
- constraints.anchor = GridBagConstraints.SOUTHEAST;
+ constraints.anchor = GridBagConstraints.SOUTHWEST;
constraints.insets = new Insets(0, 16, 8, 8);
+
+ appWindow.add(buildButtonBar(), constraints);
+ }
+
+ private Box buildButtonBar() {
Box buttonBar = Box.createHorizontalBox();
+
+ JHyperLink aboutLink = new JHyperLink(Installer.ABOUT_LOMBOK_URL.toString());
+ aboutLink.addActionListener(openBrowser(aboutLink, Installer.ABOUT_LOMBOK_URL));
+ buttonBar.add(aboutLink);
+
+ buttonBar.add(Box.createRigidArea(new Dimension(16, 1)));
+
+ JLabel versionLabel = new JLabel();
+ versionLabel.setText("v" + Version.getVersion());
+
+ buttonBar.add(versionLabel);
+ buttonBar.add(Box.createRigidArea(new Dimension(16, 1)));
+
+ JHyperLink changelogLink = new JHyperLink("View full changelog");
+ changelogLink.addActionListener(openBrowser(changelogLink, Installer.ABOUT_LOMBOK_URL.resolve("/changelog.html")));
+ buttonBar.add(changelogLink);
+
+ buttonBar.add(Box.createHorizontalGlue());
+
JButton quitButton = new JButton("Quit Installer");
quitButton.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
@@ -728,51 +842,48 @@ public class InstallerGUI {
System.exit(0);
}
});
- final JHyperLink hyperlink = new JHyperLink(Installer.ABOUT_LOMBOK_URL.toString());
- hyperlink.addActionListener(new ActionListener() {
+ buttonBar.add(quitButton);
+ return buttonBar;
+ }
+
+ private ActionListener openBrowser(final JHyperLink hyperlink, final URI location) {
+ return new ActionListener() {
@Override public void actionPerformed(ActionEvent event) {
hyperlink.setForeground(new Color(85, 145, 90));
try {
//java.awt.Desktop doesn't exist in 1.5.
Object desktop = Class.forName("java.awt.Desktop").getMethod("getDesktop").invoke(null);
- Class.forName("java.awt.Desktop").getMethod("browse", URI.class).invoke(desktop, Installer.ABOUT_LOMBOK_URL);
+ Class.forName("java.awt.Desktop").getMethod("browse", URI.class).invoke(desktop, location);
} catch (Exception e) {
Runtime rt = Runtime.getRuntime();
try {
- switch (IdeFinder.getOS()) {
+ switch (OsUtils.getOS()) {
case WINDOWS:
String[] cmd = new String[4];
cmd[0] = "cmd.exe";
cmd[1] = "/C";
cmd[2] = "start";
- cmd[3] = Installer.ABOUT_LOMBOK_URL.toString();
+ cmd[3] = location.toString();
rt.exec(cmd);
break;
case MAC_OS_X:
- rt.exec("open " + Installer.ABOUT_LOMBOK_URL.toString());
+ rt.exec("open " + location.toString());
break;
default:
case UNIX:
- rt.exec("firefox " + Installer.ABOUT_LOMBOK_URL.toString());
+ rt.exec("firefox " + location.toString());
break;
}
} catch (Exception e2) {
JOptionPane.showMessageDialog(appWindow,
"Well, this is embarrassing. I don't know how to open a webbrowser.\n" +
- "I guess you'll have to open it. Browse to:\n" + Installer.ABOUT_LOMBOK_URL +
+ "I guess you'll have to open it. Browse to:\n" + location +
" for more information about Lombok.",
"I'm embarrassed", JOptionPane.INFORMATION_MESSAGE);
}
}
}
- });
- buttonBar.add(hyperlink);
- buttonBar.add(Box.createRigidArea(new Dimension(16, 1)));
- buttonBar.add(new JLabel("<html><font size=\"-1\">v" + Version.getVersion() + "</font></html>"));
-
- buttonBar.add(Box.createHorizontalGlue());
- buttonBar.add(quitButton);
- appWindow.add(buttonBar, constraints);
+ };
}
/**
@@ -780,7 +891,7 @@ public class InstallerGUI {
*/
public void show() {
appWindow.setVisible(true);
- if (IdeFinder.getOS() == OS.MAC_OS_X) {
+ if (OsUtils.getOS() == OS.MAC_OS_X) {
try {
AppleNativeLook.go();
} catch (Throwable ignore) {
@@ -790,7 +901,7 @@ public class InstallerGUI {
}
private static final String IDE_TITLE =
- "<html><font size=\"+1\"><b><i>IDEs</i></b></font></html>";
+ "<html><font size=\"+1\"><b><i>IDEs </i></b></font></html>";
private static final String IDE_EXPLANATION =
"<html>Lombok can update your Eclipse or eclipse-based IDE to fully support all Lombok features.<br>" +
@@ -800,7 +911,7 @@ public class InstallerGUI {
"Scanning your drives for IDE installations...";
private static final String JAVAC_TITLE =
- "<html><font size=\"+1\"><b><i>Javac</i></b></font> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (and tools that invoke javac such as <i>ant</i> and <i>maven</i>)</html>";
+ "<html><font size=\"+1\"><b><i>Javac </i></b></font> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (and tools that invoke javac such as <i>ant</i> and <i>maven</i>)</html>";
private static final String JAVAC_EXPLANATION =
"<html>Lombok works 'out of the box' with javac.<br>Just make sure the lombok.jar is in your classpath when you compile.";
@@ -809,23 +920,27 @@ public class InstallerGUI {
"<html>Example: <code>javac -cp lombok.jar MyCode.java</code></html>";
private static final String UNINSTALL_TITLE =
- "<html><font size=\"+1\"><b><i>Uninstall</i></b></font></html>";
+ "<html><font size=\"+1\"><b><i>Uninstall </i></b></font></html>";
private static final String UNINSTALL_EXPLANATION =
"<html>Uninstall Lombok from the following IDE Installations?</html>";
private static final String HOW_I_WORK_TITLE =
- "<html><font size=\"+1\"><b><i>What this installer does</i></b></font></html>";
+ "<html><font size=\"+1\"><b><i>What this installer does </i></b></font></html>";
private static final String HOW_I_WORK_EXPLANATION =
"<html><h2>Eclipse</h2><ol>" +
"<li>First, I copy myself (lombok.jar) to your Eclipse install directory.</li>" +
- "<li>Then, I edit the <i>eclipse.ini</i> file to add the following two entries:<br>" +
- "<pre>-Xbootclasspath/a:lombok.jar<br>" +
- "-javaagent:lombok.jar</pre></li></ol>" +
+ "<li>Then, I edit the <i>eclipse.ini</i> file to add the following entry:<br>" +
+ "<pre>-javaagent:lombok.jar</pre></li></ol>" +
"On Mac OS X, eclipse.ini is hidden in<br>" +
"<code>Eclipse.app/Contents/MacOS</code> so that's where I place the jar files.</html>";
+ private static final String SUCCESS_TITLE = "<html><font size=\"+1\"><b><i>Install successful </i></b></font></html>";
+ private static final String SUCCESS_EXPLANATION = "<html>Lombok has been installed on the selected IDE installations.<br>" +
+ "Don't forget to:<ul><li> add <code>lombok.jar</code> to your projects,<li><b>exit and start</b> your IDE,<li><b>rebuild</b> all projects!</ul>%%%</html>";
+
+
private static class JHyperLink extends JButton {
private static final long serialVersionUID = 1L;
diff --git a/src/installer/lombok/installer/IdeFinder.java b/src/installer/lombok/installer/OsUtils.java
index f68a0e4c..2da7de09 100644
--- a/src/installer/lombok/installer/IdeFinder.java
+++ b/src/installer/lombok/installer/OsUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 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
@@ -34,9 +34,13 @@ import lombok.core.Version;
/**
* Implement and provide this class to add auto-finding a certain brand of IDEs to the lombok installer.
*/
-public abstract class IdeFinder {
+public final class OsUtils {
private static final AtomicBoolean windowsDriveInfoLibLoaded = new AtomicBoolean(false);
+ private OsUtils() {
+ // Prevent instantiation
+ }
+
private static void loadWindowsDriveInfoLib() throws IOException {
if (!windowsDriveInfoLibLoaded.compareAndSet(false, true)) return;
@@ -63,7 +67,7 @@ public abstract class IdeFinder {
}
private static boolean unpackDLL(String dllName, File target) throws IOException {
- InputStream in = IdeFinder.class.getResourceAsStream(dllName);
+ InputStream in = OsUtils.class.getResourceAsStream(dllName);
try {
try {
FileOutputStream out = new FileOutputStream(target);
@@ -130,15 +134,4 @@ public abstract class IdeFinder {
return OS.UNIX;
}
-
- /**
- * Look for installations of your IDE in the usual places.
- *
- * @param locations Add to this list any valid locations that you found.
- * @param problems
- * Add to this list any locations that look like installations,
- * but have problems that prevent you from installing/uninstalling from them. DONT add to this list
- * any common locations that have no installation at all - only add near misses.
- */
- public abstract void findIdes(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems);
}
diff --git a/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java b/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java
index 914de588..fa2ce958 100644
--- a/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java
+++ b/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,140 +21,24 @@
*/
package lombok.installer.eclipse;
-import static lombok.installer.IdeLocation.canonical;
+import java.util.Collections;
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import lombok.installer.IdeLocation;
import lombok.installer.IdeLocationProvider;
-import lombok.installer.CorruptedIdeLocationException;
-import lombok.installer.IdeFinder.OS;
import org.mangosdk.spi.ProviderFor;
@ProviderFor(IdeLocationProvider.class)
-public class EclipseLocationProvider implements IdeLocationProvider {
- @Override public IdeLocation create(String path) throws CorruptedIdeLocationException {
- return create0(path);
- }
-
- protected List<String> getEclipseExecutableNames() {
- return Arrays.asList("eclipse.app", "eclipse.exe", "eclipse");
- }
-
- protected String getIniName() {
- return "eclipse.ini";
- }
-
- protected IdeLocation makeLocation(String name, File ini) throws CorruptedIdeLocationException {
- return new EclipseLocation(name, ini);
- }
-
- protected String getMacAppName() {
- return "Eclipse.app";
- }
-
- protected String getUnixAppName() {
- return "eclipse";
- }
-
- /**
- * Create a new EclipseLocation by pointing at either the directory contains the Eclipse executable, or the executable itself,
- * or an eclipse.ini file.
- *
- * @throws NotAnIdeLocationException
- * If this isn't an Eclipse executable or a directory with an
- * Eclipse executable.
- */
- protected IdeLocation create0(String path) throws CorruptedIdeLocationException {
- if (path == null) throw new NullPointerException("path");
- File p = new File(path);
-
- if (!p.exists()) return null;
- if (p.isDirectory()) {
- for (String possibleExeName : getEclipseExecutableNames()) {
- File f = new File(p, possibleExeName);
- if (f.exists()) return findEclipseIniFromExe(f, 0);
- }
-
- File f = new File(p, getIniName());
- if (f.exists()) return new EclipseLocation(canonical(p), f);
- }
-
- if (p.isFile()) {
- if (p.getName().equalsIgnoreCase(getIniName())) {
- return new EclipseLocation(canonical(p.getParentFile()), p);
- }
- }
-
- if (getEclipseExecutableNames().contains(p.getName().toLowerCase())) {
- return findEclipseIniFromExe(p, 0);
- }
-
- return null;
- }
-
- private IdeLocation findEclipseIniFromExe(File exePath, int loopCounter) throws CorruptedIdeLocationException {
- /* Try looking for eclipse.ini as sibling to the executable */ {
- File ini = new File(exePath.getParentFile(), getIniName());
- if (ini.isFile()) return makeLocation(canonical(exePath), ini);
- }
-
- /* Try looking for Eclipse/app/Contents/MacOS/eclipse.ini as sibling to executable; this works on Mac OS X. */ {
- File ini = new File(exePath.getParentFile(), getMacAppName() + "/Contents/MacOS/" + getIniName());
- if (ini.isFile()) return makeLocation(canonical(exePath), ini);
- }
-
- /* If executable is a soft link, follow it and retry. */ {
- if (loopCounter < 50) {
- try {
- String oPath = exePath.getAbsolutePath();
- String nPath = exePath.getCanonicalPath();
- if (!oPath.equals(nPath)) try {
- IdeLocation loc = findEclipseIniFromExe(new File(nPath), loopCounter + 1);
- if (loc != null) return loc;
- } catch (CorruptedIdeLocationException ignore) {
- // Unlinking didn't help find an eclipse, so continue.
- }
- } catch (IOException ignore) { /* okay, that didn't work, assume it isn't a soft link then. */ }
- }
- }
-
- /* If executable is a linux LSB-style path, then look in the usual places that package managers like apt-get use.*/ {
- String path = exePath.getAbsolutePath();
- try {
- path = exePath.getCanonicalPath();
- } catch (IOException ignore) { /* We'll stick with getAbsolutePath()'s result then. */ }
-
- if (path.equals("/usr/bin/" + getUnixAppName()) || path.equals("/bin/" + getUnixAppName()) || path.equals("/usr/local/bin/" + getUnixAppName())) {
- File ini = new File("/usr/lib/" + getUnixAppName() + "/" + getIniName());
- if (ini.isFile()) return makeLocation(path, ini);
- ini = new File("/usr/local/lib/" + getUnixAppName() + "/" + getIniName());
- if (ini.isFile()) return makeLocation(path, ini);
- ini = new File("/usr/local/etc/" + getUnixAppName() + "/" + getIniName());
- if (ini.isFile()) return makeLocation(path, ini);
- ini = new File("/etc/" + getIniName());
- if (ini.isFile()) return makeLocation(path, ini);
- }
- }
-
- /* If we get this far, we lose. */
- return null;
- }
-
- @Override public Pattern getLocationSelectors(OS os) {
- switch (os) {
- case MAC_OS_X:
- return Pattern.compile("^(eclipse|eclipse\\.ini|eclipse\\.app)$", Pattern.CASE_INSENSITIVE);
- case WINDOWS:
- return Pattern.compile("^(eclipse\\.exe|eclipse\\.ini)$", Pattern.CASE_INSENSITIVE);
- default:
- case UNIX:
- return Pattern.compile("^(eclipse|eclipse\\.ini)$", Pattern.CASE_INSENSITIVE);
- }
+public class EclipseLocationProvider extends EclipseProductLocationProvider {
+
+ private static final EclipseProductDescriptor ECLIPSE = new StandardProductDescriptor(
+ "Eclipse",
+ "eclipse",
+ "eclipse",
+ EclipseLocationProvider.class.getResource("eclipse.png"),
+ Collections.<String>emptySet()
+ );
+
+ public EclipseLocationProvider() {
+ super(ECLIPSE);
}
}
diff --git a/src/installer/lombok/installer/eclipse/STSLocation.java b/src/installer/lombok/installer/eclipse/EclipseProductDescriptor.java
index 40ade40a..8f736a57 100644
--- a/src/installer/lombok/installer/eclipse/STSLocation.java
+++ b/src/installer/lombok/installer/eclipse/EclipseProductDescriptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,25 +21,21 @@
*/
package lombok.installer.eclipse;
-import java.io.File;
import java.net.URL;
+import java.util.List;
+import java.util.regex.Pattern;
-import lombok.installer.CorruptedIdeLocationException;
-
-public class STSLocation extends EclipseLocation {
- public STSLocation(String nameOfLocation, File pathToEclipseIni) throws CorruptedIdeLocationException {
- super(nameOfLocation, pathToEclipseIni);
- }
-
- @Override public URL getIdeIcon() {
- return STSLocation.class.getResource("STS.png");
- }
-
- @Override protected String getIniFileName() {
- return "STS.ini";
- }
-
- @Override protected String getTypeName() {
- return "STS";
- }
-}
+public interface EclipseProductDescriptor {
+ String getProductName();
+ String getWindowsExecutableName();
+ String getUnixAppName();
+ String getMacAppName();
+ String getDirectoryName();
+ List<String> getExecutableNames();
+ List<String> getSourceDirsOnWindows();
+ List<String> getSourceDirsOnMac();
+ List<String> getSourceDirsOnUnix();
+ String getIniFileName();
+ Pattern getLocationSelectors();
+ URL getIdeIcon();
+} \ No newline at end of file
diff --git a/src/installer/lombok/installer/eclipse/EclipseLocation.java b/src/installer/lombok/installer/eclipse/EclipseProductLocation.java
index e347cd98..aa97a3e5 100644
--- a/src/installer/lombok/installer/eclipse/EclipseLocation.java
+++ b/src/installer/lombok/installer/eclipse/EclipseProductLocation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2012 The Project Lombok Authors.
+ * Copyright (C) 2009-2017 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,7 +35,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.installer.CorruptedIdeLocationException;
-import lombok.installer.IdeFinder;
+import lombok.installer.OsUtils;
import lombok.installer.IdeLocation;
import lombok.installer.InstallException;
import lombok.installer.Installer;
@@ -46,30 +46,35 @@ import lombok.installer.UninstallException;
* An instance can figure out if an Eclipse installation has been lombok-ified, and can
* install and uninstall lombok from the Eclipse installation.
*/
-public class EclipseLocation extends IdeLocation {
- private final String name;
- private final File eclipseIniPath;
- private volatile boolean hasLombok;
-
- private static final String OS_NEWLINE = IdeFinder.getOS().getLineEnding();
+public final class EclipseProductLocation extends IdeLocation {
- protected String getTypeName() {
- return "eclipse";
- }
+ private static final String OS_NEWLINE = OsUtils.getOS().getLineEnding();
- protected String getIniFileName() {
- return "eclipse.ini";
- }
+ private final EclipseProductDescriptor descriptor;
+ private final String name;
+ private final File eclipseIniPath;
+ private final String pathToLombokJarPrefix;
+ private final boolean hasLombok;
- EclipseLocation(String nameOfLocation, File pathToEclipseIni) throws CorruptedIdeLocationException {
+ EclipseProductLocation(EclipseProductDescriptor descriptor, String nameOfLocation, File pathToEclipseIni) throws CorruptedIdeLocationException {
+ this.descriptor = descriptor;
this.name = nameOfLocation;
this.eclipseIniPath = pathToEclipseIni;
+ File p1 = pathToEclipseIni.getParentFile();
+ File p2 = p1 == null ? null : p1.getParentFile();
+ File p3 = p2 == null ? null : p2.getParentFile();
+ if (p1 != null && p1.getName().equals("Eclipse") && p2 != null && p2.getName().equals("Contents") && p3 != null && p3.getName().endsWith(".app")) {
+ this.pathToLombokJarPrefix = "../Eclipse/";
+ } else {
+ this.pathToLombokJarPrefix = "";
+ }
+
try {
this.hasLombok = checkForLombok(eclipseIniPath);
} catch (IOException e) {
throw new CorruptedIdeLocationException(
- "I can't read the configuration file of the " + getTypeName() + " installed at " + name + "\n" +
- "You may need to run this installer with root privileges if you want to modify that " + getTypeName() + ".", getTypeName(), e);
+ "I can't read the configuration file of the " + descriptor.getProductName() + " installed at " + name + "\n" +
+ "You may need to run this installer with root privileges if you want to modify that " + descriptor.getProductName() + ".", descriptor.getProductName(), e);
}
}
@@ -78,8 +83,8 @@ public class EclipseLocation extends IdeLocation {
}
@Override public boolean equals(Object o) {
- if (!(o instanceof EclipseLocation)) return false;
- return ((EclipseLocation)o).eclipseIniPath.equals(eclipseIniPath);
+ if (!(o instanceof EclipseProductLocation)) return false;
+ return ((EclipseProductLocation)o).eclipseIniPath.equals(eclipseIniPath);
}
/**
@@ -98,13 +103,13 @@ public class EclipseLocation extends IdeLocation {
return hasLombok;
}
- private final Pattern JAVA_AGENT_LINE_MATCHER = Pattern.compile(
+ private static final Pattern JAVA_AGENT_LINE_MATCHER = Pattern.compile(
"^\\-javaagent\\:.*lombok.*\\.jar$", Pattern.CASE_INSENSITIVE);
- private final Pattern BOOTCLASSPATH_LINE_MATCHER = Pattern.compile(
+ private static final Pattern BOOTCLASSPATH_LINE_MATCHER = Pattern.compile(
"^\\-Xbootclasspath\\/a\\:(.*lombok.*\\.jar.*)$", Pattern.CASE_INSENSITIVE);
- private boolean checkForLombok(File iniFile) throws IOException {
+ private static boolean checkForLombok(File iniFile) throws IOException {
if (!iniFile.exists()) return false;
FileInputStream fis = new FileInputStream(iniFile);
try {
@@ -196,7 +201,7 @@ public class EclipseLocation extends IdeLocation {
File lombokJar = new File(dir, "lombok.jar");
if (lombokJar.exists()) {
if (!lombokJar.delete()) {
- if (IdeFinder.getOS() == IdeFinder.OS.WINDOWS && Installer.isSelf(lombokJar.getAbsolutePath())) {
+ if (OsUtils.getOS() == OsUtils.OS.WINDOWS && Installer.isSelf(lombokJar.getAbsolutePath())) {
lombokJarsForWhichCantDeleteSelf.add(lombokJar);
} else {
throw new UninstallException(
@@ -218,14 +223,14 @@ public class EclipseLocation extends IdeLocation {
throw new UninstallException(true, String.format(
"lombok.jar cannot delete itself on windows.\nHowever, lombok has been uncoupled from your %s.\n" +
"You can safely delete this jar file. You can find it at:\n%s",
- getTypeName(), lombokJarsForWhichCantDeleteSelf.get(0).getAbsolutePath()), null);
+ descriptor.getProductName(), lombokJarsForWhichCantDeleteSelf.get(0).getAbsolutePath()), null);
}
}
private static String generateWriteErrorMessage() {
String osSpecificError;
- switch (IdeFinder.getOS()) {
+ switch (OsUtils.getOS()) {
default:
case MAC_OS_X:
case UNIX:
@@ -251,11 +256,11 @@ public class EclipseLocation extends IdeLocation {
*/
@Override
public String install() throws InstallException {
- // For whatever reason, relative paths in your eclipse.ini file don't work on linux, but only for -javaagent.
- // If someone knows how to fix this, please do so, as this current hack solution (putting the absolute path
- // to the jar files in your eclipse.ini) means you can't move your eclipse around on linux without lombok
- // breaking it. NB: rerunning lombok.jar installer and hitting 'update' will fix it if you do that.
- boolean fullPathRequired = IdeFinder.getOS() == EclipseFinder.OS.UNIX || System.getProperty("lombok.installer.fullpath") != null;
+ // On Linux, for whatever reason, relative paths in your eclipse.ini file don't work, but only for -javaagent.
+ // On Windows, since the Oomph, the generated shortcut starts in the wrong directory.
+ // So the default is to use absolute paths, breaking lombok when you move the eclipse directory.
+ // Or not break when you copy your directory, but break later when you remove the original one.
+ boolean fullPathRequired = !"false".equals(System.getProperty("lombok.installer.fullpath", "true"));
boolean installSucceeded = false;
StringBuilder newContents = new StringBuilder();
@@ -293,7 +298,7 @@ public class EclipseLocation extends IdeLocation {
"I can't read my own jar file. I think you've found a bug in this installer!\nI suggest you restart it " +
"and use the 'what do I do' link, to manually install lombok. Also, tell us about this at:\n" +
"http://groups.google.com/group/project-lombok - Thanks!", e);
- throw new InstallException("I can't write to your " + getTypeName() + " directory at " + name + generateWriteErrorMessage(), e);
+ throw new InstallException("I can't write to your " + descriptor.getProductName() + " directory at " + name + generateWriteErrorMessage(), e);
}
}
@@ -333,12 +338,15 @@ public class EclipseLocation extends IdeLocation {
fis.close();
}
- String fullPathToLombok = fullPathRequired ? (lombokJar.getParentFile().getCanonicalPath() + File.separator) : "";
+ String pathPrefix;
+ if (fullPathRequired) {
+ pathPrefix = lombokJar.getParentFile().getCanonicalPath() + File.separator;
+ } else {
+ pathPrefix = pathToLombokJarPrefix;
+ }
newContents.append(String.format(
- "-javaagent:%s", escapePath(fullPathToLombok + "lombok.jar"))).append(OS_NEWLINE);
- newContents.append(String.format(
- "-Xbootclasspath/a:%s", escapePath(fullPathToLombok + "lombok.jar"))).append(OS_NEWLINE);
+ "-javaagent:%s", escapePath(pathPrefix + "lombok.jar"))).append(OS_NEWLINE);
FileOutputStream fos = new FileOutputStream(eclipseIniPath);
try {
@@ -356,15 +364,14 @@ public class EclipseLocation extends IdeLocation {
}
if (!installSucceeded) {
- throw new InstallException("I can't find the " + getIniFileName() + " file. Is this a real " + getTypeName() + " installation?", null);
+ throw new InstallException("I can't find the " + descriptor.getIniFileName() + " file. Is this a real " + descriptor.getProductName() + " installation?", null);
}
- return "If you start " + getTypeName() + " with a custom -vm parameter, you'll need to add:<br>" +
- "<code>-vmargs -Xbootclasspath/a:lombok.jar -javaagent:lombok.jar</code><br>" +
- "as parameter as well.";
+ return "If you start " + descriptor.getProductName() + " with a custom -vm parameter, you'll need to add:<br>" +
+ "<code>-vmargs -javaagent:lombok.jar</code><br>as parameter as well.";
}
@Override public URL getIdeIcon() {
- return EclipseLocation.class.getResource("eclipse.png");
+ return descriptor.getIdeIcon();
}
}
diff --git a/src/installer/lombok/installer/eclipse/EclipseFinder.java b/src/installer/lombok/installer/eclipse/EclipseProductLocationProvider.java
index 8a1a689a..b807f02b 100644
--- a/src/installer/lombok/installer/eclipse/EclipseFinder.java
+++ b/src/installer/lombok/installer/eclipse/EclipseProductLocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 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
@@ -22,109 +22,130 @@
package lombok.installer.eclipse;
import static java.util.Arrays.asList;
+import static lombok.installer.IdeLocation.canonical;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.regex.Pattern;
-import lombok.installer.IdeFinder;
-import lombok.installer.IdeLocation;
import lombok.installer.CorruptedIdeLocationException;
+import lombok.installer.OsUtils;
+import lombok.installer.IdeLocation;
+import lombok.installer.IdeLocationProvider;
-import org.mangosdk.spi.ProviderFor;
+public class EclipseProductLocationProvider implements IdeLocationProvider {
+ private final EclipseProductDescriptor descriptor;
-@ProviderFor(IdeFinder.class)
-public class EclipseFinder extends IdeFinder {
- /** should be lowercase! */
- protected String getDirName() {
- return "eclipse";
- }
-
- protected String getWindowsExecutableName() {
- return "eclipse.exe";
- }
-
- protected String getUnixExecutableName() {
- return "eclipse";
- }
-
- protected String getMacExecutableName() {
- return "Eclipse.app";
+ EclipseProductLocationProvider(EclipseProductDescriptor descriptor) {
+ this.descriptor = descriptor;
}
- protected IdeLocation createLocation(String guess) throws CorruptedIdeLocationException {
- return new EclipseLocationProvider().create0(guess);
- }
-
- protected List<String> getSourceDirsOnWindows() {
- return Arrays.asList("\\", "\\Program Files", "\\Program Files (x86)", System.getProperty("user.home", "."));
+ @Override public final IdeLocation create(String path) throws CorruptedIdeLocationException {
+ return create0(path);
}
/**
- * Returns a list of paths of Eclipse installations.
+ * Create a new EclipseLocation by pointing at either the directory contains the Eclipse executable, or the executable itself,
+ * or an eclipse.ini file.
*
- * The search process works by scanning for each 'source dir' for either an eclipse installation or a folder containing the text returned
- * by getDirName(). If such a folder is found, this process is applied recursively. On windows, this process is run on each drive letter
- * which represents a physical hard disk. If the native windows API call to determine these drive letters fails, only 'C:' is checked.
+ * @throws NotAnIdeLocationException
+ * If this isn't an Eclipse executable or a directory with an
+ * Eclipse executable.
*/
- private List<String> getSourceDirsOnWindowsWithDriveLetters() {
- List<String> driveLetters = asList("C");
- try {
- driveLetters = getDrivesOnWindows();
- } catch (Throwable ignore) {
- ignore.printStackTrace();
+ private IdeLocation create0(String path) throws CorruptedIdeLocationException {
+ if (path == null) throw new NullPointerException("path");
+ String iniName = descriptor.getIniFileName();
+ File p = new File(path);
+
+ if (!p.exists()) return null;
+ if (p.isDirectory()) {
+ for (String possibleExeName : descriptor.getExecutableNames()) {
+ File f = new File(p, possibleExeName);
+ if (f.exists()) return findEclipseIniFromExe(f, 0);
+ }
+
+ File f = new File(p, iniName);
+ if (f.exists()) return makeLocation(canonical(p), f);
}
- List<String> sourceDirs = new ArrayList<String>();
- for (String letter : driveLetters) {
- for (String possibleSource : getSourceDirsOnWindows()) {
- if (!isDriveSpecificOnWindows(possibleSource)) {
- sourceDirs.add(letter + ":" + possibleSource);
- }
+
+ if (p.isFile()) {
+ if (p.getName().equalsIgnoreCase(iniName)) {
+ return makeLocation(canonical(p.getParentFile()), p);
}
}
- for (String possibleSource : getSourceDirsOnWindows()) {
- if (isDriveSpecificOnWindows(possibleSource)) sourceDirs.add(possibleSource);
+
+ if (descriptor.getExecutableNames().contains(p.getName().toLowerCase())) {
+ return findEclipseIniFromExe(p, 0);
}
- return sourceDirs;
+ return null;
}
- public boolean isDriveSpecificOnWindows(String path) {
- return path.length() > 1 && path.charAt(1) == ':';
- }
-
- protected List<String> getSourceDirsOnMac() {
- return Arrays.asList("/Applications", System.getProperty("user.home", "."));
- }
-
- protected List<String> getSourceDirsOnUnix() {
- return Arrays.asList(System.getProperty("user.home", "."));
- }
-
- private List<File> transformToFiles(List<String> fileNames) {
- List<File> files = new ArrayList<File>();
- for (String fileName : fileNames) {
- files.add(new File(fileName));
+ private IdeLocation findEclipseIniFromExe(File exePath, int loopCounter) throws CorruptedIdeLocationException {
+ String iniName = descriptor.getIniFileName();
+ /* Try looking for eclipse.ini as sibling to the executable */ {
+ File ini = new File(exePath.getParentFile(), iniName);
+ if (ini.isFile()) return makeLocation(canonical(exePath), ini);
}
- return files;
+
+ String macAppName = descriptor.getMacAppName();
+ /* Try looking for Eclipse.app/Contents/MacOS/eclipse.ini as sibling to executable; this works on Mac OS X. */ {
+ File ini = new File(exePath.getParentFile(), macAppName + "/Contents/MacOS/" + iniName);
+ if (ini.isFile()) return makeLocation(canonical(exePath), ini);
+ }
+
+ /* Starting with Eclipse Mars (with the oomph installer), the structure has changed, and it's now at Eclipse.app/Contents/Eclipse/eclipse.ini*/ {
+ File ini = new File(exePath.getParentFile(), macAppName + "/Contents/Eclipse/" + iniName);
+ if (ini.isFile()) return makeLocation(canonical(exePath), ini);
+ }
+
+ /* If executable is a soft link, follow it and retry. */ {
+ if (loopCounter < 50) {
+ try {
+ String oPath = exePath.getAbsolutePath();
+ String nPath = exePath.getCanonicalPath();
+ if (!oPath.equals(nPath)) try {
+ IdeLocation loc = findEclipseIniFromExe(new File(nPath), loopCounter + 1);
+ if (loc != null) return loc;
+ } catch (CorruptedIdeLocationException ignore) {
+ // Unlinking didn't help find an eclipse, so continue.
+ }
+ } catch (IOException ignore) { /* okay, that didn't work, assume it isn't a soft link then. */ }
+ }
+ }
+
+ /* If executable is a linux LSB-style path, then look in the usual places that package managers like apt-get use.*/ {
+ String path = exePath.getAbsolutePath();
+ try {
+ path = exePath.getCanonicalPath();
+ } catch (IOException ignore) { /* We'll stick with getAbsolutePath()'s result then. */ }
+
+ String unixAppName = descriptor.getUnixAppName();
+ if (path.equals("/usr/bin/" + unixAppName) || path.equals("/bin/" + unixAppName) || path.equals("/usr/local/bin/" + unixAppName)) {
+ File ini = new File("/usr/lib/" + unixAppName + "/" + iniName);
+ if (ini.isFile()) return makeLocation(path, ini);
+ ini = new File("/usr/local/lib/" + unixAppName + "/" + iniName);
+ if (ini.isFile()) return makeLocation(path, ini);
+ ini = new File("/usr/local/etc/" + unixAppName + "/" + iniName);
+ if (ini.isFile()) return makeLocation(path, ini);
+ ini = new File("/etc/" + iniName);
+ if (ini.isFile()) return makeLocation(path, ini);
+ }
+ }
+
+ /* If we get this far, we lose. */
+ return null;
}
- private List<File> getFlatSourceLocationsOnUnix() {
- List<File> dirs = new ArrayList<File>();
- dirs.add(new File("/usr/bin/"));
- dirs.add(new File("/usr/local/bin/"));
- dirs.add(new File(System.getProperty("user.home", "."), "bin/"));
- return dirs;
+ private IdeLocation makeLocation(String name, File ini) throws CorruptedIdeLocationException {
+ return new EclipseProductLocation(descriptor, name, ini);
}
- private List<File> getNestedSourceLocationOnUnix() {
- List<File> dirs = new ArrayList<File>();
- dirs.add(new File("/usr/local/share"));
- dirs.add(new File("/usr/local"));
- dirs.add(new File("/usr/share"));
- return dirs;
+ @Override public Pattern getLocationSelectors() {
+ return descriptor.getLocationSelectors();
}
/**
@@ -141,7 +162,7 @@ public class EclipseFinder extends IdeFinder {
*/
@Override
public void findIdes(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems) {
- switch (getOS()) {
+ switch (OsUtils.getOS()) {
case WINDOWS:
new WindowsFinder().findEclipse(locations, problems);
break;
@@ -155,17 +176,74 @@ public class EclipseFinder extends IdeFinder {
}
}
+ private List<File> transformToFiles(List<String> fileNames) {
+ List<File> files = new ArrayList<File>();
+ for (String fileName : fileNames) {
+ files.add(new File(fileName));
+ }
+ return files;
+ }
+
+ private List<File> getFlatSourceLocationsOnUnix() {
+ List<File> dirs = new ArrayList<File>();
+ dirs.add(new File("/usr/bin/"));
+ dirs.add(new File("/usr/local/bin/"));
+ dirs.add(new File(System.getProperty("user.home", "."), "bin/"));
+ return dirs;
+ }
+
+ private List<File> getNestedSourceLocationOnUnix() {
+ List<File> dirs = new ArrayList<File>();
+ dirs.add(new File("/usr/local/share"));
+ dirs.add(new File("/usr/local"));
+ dirs.add(new File("/usr/share"));
+ return dirs;
+ }
+
private class UnixFinder extends DirectoryFinder {
UnixFinder() {
super(getNestedSourceLocationOnUnix(), getFlatSourceLocationsOnUnix());
}
@Override protected String findEclipseOnPlatform(File dir) {
- File possible = new File(dir, getUnixExecutableName());
+ File possible = new File(dir, descriptor.getUnixAppName());
return (possible.exists()) ? possible.getAbsolutePath() : null;
}
}
+ /**
+ * Returns a list of paths of Eclipse installations.
+ *
+ * The search process works by scanning for each 'source dir' for either an eclipse installation or a folder containing the text returned
+ * by getDirName(). If such a folder is found, this process is applied recursively. On windows, this process is run on each drive letter
+ * which represents a physical hard disk. If the native windows API call to determine these drive letters fails, only 'C:' is checked.
+ */
+ private List<String> getSourceDirsOnWindowsWithDriveLetters() {
+ List<String> driveLetters = asList("C");
+ try {
+ driveLetters = OsUtils.getDrivesOnWindows();
+ } catch (Throwable ignore) {
+ ignore.printStackTrace();
+ }
+ List<String> sourceDirs = new ArrayList<String>();
+ for (String letter : driveLetters) {
+ for (String possibleSource : descriptor.getSourceDirsOnWindows()) {
+ if (!isDriveSpecificOnWindows(possibleSource)) {
+ sourceDirs.add(letter + ":" + possibleSource);
+ }
+ }
+ }
+ for (String possibleSource : descriptor.getSourceDirsOnWindows()) {
+ if (isDriveSpecificOnWindows(possibleSource)) sourceDirs.add(possibleSource);
+ }
+
+ return sourceDirs;
+ }
+
+ private boolean isDriveSpecificOnWindows(String path) {
+ return path.length() > 1 && path.charAt(1) == ':';
+ }
+
private class WindowsFinder extends DirectoryFinder {
WindowsFinder() {
super(transformToFiles(getSourceDirsOnWindowsWithDriveLetters()), Collections.<File>emptyList());
@@ -174,20 +252,20 @@ public class EclipseFinder extends IdeFinder {
/** Checks if the provided directory contains 'eclipse.exe', and if so, returns the directory, otherwise null. */
@Override
protected String findEclipseOnPlatform(File dir) {
- File possible = new File(dir, getWindowsExecutableName());
+ File possible = new File(dir, descriptor.getWindowsExecutableName());
return (possible.isFile()) ? dir.getAbsolutePath() : null;
}
}
private class MacFinder extends DirectoryFinder {
MacFinder() {
- super(transformToFiles(getSourceDirsOnMac()), Collections.<File>emptyList());
+ super(transformToFiles(descriptor.getSourceDirsOnMac()), Collections.<File>emptyList());
}
protected String findEclipseOnPlatform(File dir) {
- if (dir.getName().toLowerCase().equals(getMacExecutableName().toLowerCase())) return dir.getParent();
- if (dir.getName().toLowerCase().contains(getDirName())) {
- if (new File(dir, getMacExecutableName()).exists()) return dir.toString();
+ if (dir.getName().toLowerCase().equals(descriptor.getMacAppName().toLowerCase())) return dir.getParent();
+ if (dir.getName().toLowerCase().contains(descriptor.getDirectoryName())) {
+ if (new File(dir, descriptor.getMacAppName()).exists()) return dir.toString();
}
return null;
}
@@ -202,18 +280,18 @@ public class EclipseFinder extends IdeFinder {
this.flatSourceDirs = flatSourceDirs;
}
- public void findEclipse(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems) {
+ void findEclipse(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems) {
for (File dir : nestedSourceDirs) recurseDirectory(locations, problems, dir);
for (File dir : flatSourceDirs) findEclipse(locations, problems, dir);
}
- protected abstract String findEclipseOnPlatform(File dir);
+ abstract String findEclipseOnPlatform(File dir);
- protected void recurseDirectory(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems, File dir) {
- recurseDirectory0(locations, problems, dir, 0);
+ void recurseDirectory(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems, File dir) {
+ recurseDirectory0(locations, problems, dir, 0, false);
}
- private void recurseDirectory0(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems, File f, int loopCounter) {
+ private void recurseDirectory0(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems, File f, int loopCounter, boolean nameFound) {
//Various try/catch/ignore statements are in this for loop. Weird conditions on the disk can cause exceptions,
//such as an unformatted drive causing a NullPointerException on listFiles. Best action is almost invariably to just
//continue onwards.
@@ -223,19 +301,19 @@ public class EclipseFinder extends IdeFinder {
for (File dir : listFiles) {
if (!dir.isDirectory()) continue;
try {
- if (dir.getName().toLowerCase().contains(getDirName())) {
+ if (nameFound || dir.getName().toLowerCase().contains(descriptor.getDirectoryName())) {
findEclipse(locations, problems, dir);
- if (loopCounter < 50) recurseDirectory0(locations, problems, dir, loopCounter + 1);
+ if (loopCounter < 50) recurseDirectory0(locations, problems, dir, loopCounter + 1, true);
}
} catch (Exception ignore) {}
}
}
-
+
private void findEclipse(List<IdeLocation> locations, List<CorruptedIdeLocationException> problems, File dir) {
String eclipseLocation = findEclipseOnPlatform(dir);
if (eclipseLocation != null) {
try {
- IdeLocation newLocation = createLocation(eclipseLocation);
+ IdeLocation newLocation = create(eclipseLocation);
if (newLocation != null) locations.add(newLocation);
} catch (CorruptedIdeLocationException e) {
problems.add(e);
diff --git a/src/installer/lombok/installer/eclipse/JbdsFinder.java b/src/installer/lombok/installer/eclipse/JbdsFinder.java
deleted file mode 100644
index 2dfaacba..00000000
--- a/src/installer/lombok/installer/eclipse/JbdsFinder.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 The Project Lombok Authors.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package lombok.installer.eclipse;
-
-import java.util.Arrays;
-import java.util.List;
-
-import lombok.installer.CorruptedIdeLocationException;
-import lombok.installer.IdeFinder;
-import lombok.installer.IdeLocation;
-
-import org.mangosdk.spi.ProviderFor;
-
-/**
- * JBDS (JBoss Developer Studio) is an eclipse variant.
- * Other than different executable names, it's the same as eclipse, as far as lombok support goes.
- */
-@ProviderFor(IdeFinder.class)
-public class JbdsFinder extends EclipseFinder {
- @Override protected IdeLocation createLocation(String guess) throws CorruptedIdeLocationException {
- return new JbdsLocationProvider().create0(guess);
- }
-
- @Override protected String getDirName() {
- return "studio";
- }
-
- @Override protected String getMacExecutableName() {
- return "jbdevstudio.app";
- }
-
- @Override protected String getUnixExecutableName() {
- return "jbdevstudio";
- }
-
- @Override protected String getWindowsExecutableName() {
- return "jbdevstudio.exe";
- }
-
- @Override protected List<String> getSourceDirsOnWindows() {
- return Arrays.asList("\\", "\\Program Files", "\\Program Files (x86)", System.getProperty("user.home", "."));
- }
-
- @Override protected List<String> getSourceDirsOnMac() {
- return Arrays.asList("/Applications", System.getProperty("user.home", "."));
- }
-
- @Override protected List<String> getSourceDirsOnUnix() {
- return Arrays.asList(System.getProperty("user.home", "."));
- }
-}
diff --git a/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java b/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java
index e6df0e43..635f304a 100644
--- a/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java
+++ b/src/installer/lombok/installer/eclipse/JbdsLocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Project Lombok Authors.
+ * Copyright (C) 2013-2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,49 +21,24 @@
*/
package lombok.installer.eclipse;
-import java.io.File;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Pattern;
+import java.util.Collections;
-import lombok.installer.CorruptedIdeLocationException;
-import lombok.installer.IdeLocation;
import lombok.installer.IdeLocationProvider;
-import lombok.installer.IdeFinder.OS;
import org.mangosdk.spi.ProviderFor;
@ProviderFor(IdeLocationProvider.class)
-public class JbdsLocationProvider extends EclipseLocationProvider {
- @Override protected List<String> getEclipseExecutableNames() {
- return Arrays.asList("jbdevstudio.app", "jbdevstudio.exe", "jbdevstudioc.exe", "jbdevstudio");
- }
-
- @Override protected String getIniName() {
- return "jbdevstudio.ini";
- }
-
- @Override protected IdeLocation makeLocation(String name, File ini) throws CorruptedIdeLocationException {
- return new JbdsLocation(name, ini);
- }
-
- @Override protected String getMacAppName() {
- return "jbdevstudio.app";
- }
+public class JbdsLocationProvider extends EclipseProductLocationProvider {
- @Override protected String getUnixAppName() {
- return "jbdevstudio";
- }
+ private static final EclipseProductDescriptor JBDS = new StandardProductDescriptor(
+ "JBoss Developer Studio",
+ "jbdevstudio",
+ "studio",
+ JbdsLocationProvider.class.getResource("jbds.png"),
+ Collections.<String>emptySet()
+ );
- @Override public Pattern getLocationSelectors(OS os) {
- switch (os) {
- case MAC_OS_X:
- return Pattern.compile("^(jbdevstudio|jbdevstudio\\.ini|jbdevstudio\\.app)$", Pattern.CASE_INSENSITIVE);
- case WINDOWS:
- return Pattern.compile("^(jbdevstudioc?\\.exe|jbdevstudio\\.ini)$", Pattern.CASE_INSENSITIVE);
- default:
- case UNIX:
- return Pattern.compile("^(jbdevstudio|jbdevstudio\\.ini)$", Pattern.CASE_INSENSITIVE);
- }
+ public JbdsLocationProvider() {
+ super(JBDS);
}
}
diff --git a/src/installer/lombok/installer/eclipse/JbdsLocation.java b/src/installer/lombok/installer/eclipse/MyEclipseLocationProvider.java
index 81fb5261..298cabd6 100644
--- a/src/installer/lombok/installer/eclipse/JbdsLocation.java
+++ b/src/installer/lombok/installer/eclipse/MyEclipseLocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Project Lombok Authors.
+ * Copyright (C) 2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,25 +21,24 @@
*/
package lombok.installer.eclipse;
-import java.io.File;
-import java.net.URL;
+import java.util.Collections;
-import lombok.installer.CorruptedIdeLocationException;
+import lombok.installer.IdeLocationProvider;
-public class JbdsLocation extends EclipseLocation {
- public JbdsLocation(String nameOfLocation, File pathToEclipseIni) throws CorruptedIdeLocationException {
- super(nameOfLocation, pathToEclipseIni);
- }
-
- @Override public URL getIdeIcon() {
- return JbdsLocation.class.getResource("jbds.png");
- }
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(IdeLocationProvider.class)
+public class MyEclipseLocationProvider extends EclipseProductLocationProvider {
- @Override protected String getIniFileName() {
- return "jbdevstudio.ini";
- }
+ private static final EclipseProductDescriptor MY_ECLIPSE = new StandardProductDescriptor(
+ "MyEclipse",
+ "myeclipse",
+ "myeclipse",
+ MyEclipseLocationProvider.class.getResource("myeclipse.png"),
+ Collections.<String>emptySet()
+ );
- @Override protected String getTypeName() {
- return "JBoss Developer Studio";
+ public MyEclipseLocationProvider() {
+ super(MY_ECLIPSE);
}
}
diff --git a/src/installer/lombok/installer/eclipse/RhdsLocationProvider.java b/src/installer/lombok/installer/eclipse/RhdsLocationProvider.java
new file mode 100644
index 00000000..5e1d303d
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/RhdsLocationProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013-2016 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.installer.eclipse;
+
+import java.util.Collections;
+
+import lombok.installer.IdeLocationProvider;
+
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(IdeLocationProvider.class)
+public class RhdsLocationProvider extends EclipseProductLocationProvider {
+
+ private static final EclipseProductDescriptor RHDS = new StandardProductDescriptor(
+ "Red Hat JBoss Developer Studio",
+ "devstudio",
+ "studio",
+ RhdsLocationProvider.class.getResource("rhds.png"),
+ Collections.<String>emptySet()
+ );
+
+ public RhdsLocationProvider() {
+ super(RHDS);
+ }
+}
diff --git a/src/installer/lombok/installer/eclipse/STS4LocationProvider.java b/src/installer/lombok/installer/eclipse/STS4LocationProvider.java
new file mode 100644
index 00000000..47a07bdd
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/STS4LocationProvider.java
@@ -0,0 +1,44 @@
+/*
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.installer.eclipse;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import lombok.installer.IdeLocationProvider;
+
+import org.mangosdk.spi.ProviderFor;
+
+@ProviderFor(IdeLocationProvider.class)
+public class STS4LocationProvider extends EclipseProductLocationProvider {
+
+ private static final EclipseProductDescriptor STS4 = new StandardProductDescriptor("Spring Tools Suite 4",
+ "SpringToolSuite4",
+ "sts",
+ STS4LocationProvider.class.getResource("STS.png"),
+ Collections.unmodifiableList(Arrays.asList("springsource", "spring-tool-suite"))
+ );
+
+ public STS4LocationProvider() {
+ super(STS4);
+ }
+}
diff --git a/src/installer/lombok/installer/eclipse/STSFinder.java b/src/installer/lombok/installer/eclipse/STSFinder.java
deleted file mode 100644
index 82bc9b80..00000000
--- a/src/installer/lombok/installer/eclipse/STSFinder.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2009 The Project Lombok Authors.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package lombok.installer.eclipse;
-
-import java.util.Arrays;
-import java.util.List;
-
-import lombok.installer.CorruptedIdeLocationException;
-import lombok.installer.IdeFinder;
-import lombok.installer.IdeLocation;
-
-import org.mangosdk.spi.ProviderFor;
-
-/**
- * STS (Springsource Tool Suite) is an eclipse variant.
- * Other than different executable names, it's the same as eclipse, as far as lombok support goes.
- */
-@ProviderFor(IdeFinder.class)
-public class STSFinder extends EclipseFinder {
- @Override protected IdeLocation createLocation(String guess) throws CorruptedIdeLocationException {
- return new STSLocationProvider().create0(guess);
- }
-
- @Override protected String getDirName() {
- return "sts";
- }
-
- @Override protected String getMacExecutableName() {
- return "STS.app";
- }
-
- @Override protected String getUnixExecutableName() {
- return "STS";
- }
-
- @Override protected String getWindowsExecutableName() {
- return "STS.exe";
- }
-
- @Override protected List<String> getSourceDirsOnWindows() {
- return Arrays.asList("\\", "\\springsource", "\\Program Files", "\\Program Files (x86)", "\\Program Files\\springsource", "\\Program Files (x86)\\springsource", System.getProperty("user.home", "."), System.getProperty("user.home", ".") + "\\springsource");
- }
-
- @Override protected List<String> getSourceDirsOnMac() {
- return Arrays.asList("/Applications", "/Applications/springsource", System.getProperty("user.home", "."), System.getProperty("user.home", ".") + "/springsource");
- }
-
- @Override protected List<String> getSourceDirsOnUnix() {
- return Arrays.asList(System.getProperty("user.home", "."), System.getProperty("user.home", ".") + "/springsource");
- }
-}
diff --git a/src/installer/lombok/installer/eclipse/STSLocationProvider.java b/src/installer/lombok/installer/eclipse/STSLocationProvider.java
index 7d129838..d2efb956 100644
--- a/src/installer/lombok/installer/eclipse/STSLocationProvider.java
+++ b/src/installer/lombok/installer/eclipse/STSLocationProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 The Project Lombok Authors.
+ * Copyright (C) 2009-2016 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -21,49 +21,23 @@
*/
package lombok.installer.eclipse;
-import java.io.File;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Pattern;
+import java.util.Collections;
-import lombok.installer.CorruptedIdeLocationException;
-import lombok.installer.IdeLocation;
import lombok.installer.IdeLocationProvider;
-import lombok.installer.IdeFinder.OS;
import org.mangosdk.spi.ProviderFor;
@ProviderFor(IdeLocationProvider.class)
-public class STSLocationProvider extends EclipseLocationProvider {
- @Override protected List<String> getEclipseExecutableNames() {
- return Arrays.asList("sts.app", "sts.exe", "stsc.exe", "sts");
- }
-
- @Override protected String getIniName() {
- return "STS.ini";
- }
-
- @Override protected IdeLocation makeLocation(String name, File ini) throws CorruptedIdeLocationException {
- return new STSLocation(name, ini);
- }
-
- @Override protected String getMacAppName() {
- return "STS.app";
- }
+public class STSLocationProvider extends EclipseProductLocationProvider {
- @Override protected String getUnixAppName() {
- return "STS";
- }
+ private static final EclipseProductDescriptor STS = new StandardProductDescriptor("STS",
+ "STS",
+ "sts",
+ STSLocationProvider.class.getResource("STS.png"),
+ Collections.singleton("springsource")
+ );
- @Override public Pattern getLocationSelectors(OS os) {
- switch (os) {
- case MAC_OS_X:
- return Pattern.compile("^(sts|sts\\.ini|sts\\.app)$", Pattern.CASE_INSENSITIVE);
- case WINDOWS:
- return Pattern.compile("^(stsc?\\.exe|sts\\.ini)$", Pattern.CASE_INSENSITIVE);
- default:
- case UNIX:
- return Pattern.compile("^(sts|sts\\.ini)$", Pattern.CASE_INSENSITIVE);
- }
+ public STSLocationProvider() {
+ super(STS);
}
}
diff --git a/src/installer/lombok/installer/eclipse/StandardProductDescriptor.java b/src/installer/lombok/installer/eclipse/StandardProductDescriptor.java
new file mode 100644
index 00000000..47e103aa
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/StandardProductDescriptor.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.installer.eclipse;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import lombok.installer.OsUtils;
+
+public class StandardProductDescriptor implements EclipseProductDescriptor {
+
+ private static final String USER_HOME = System.getProperty("user.home", ".");
+ private static final String[] WINDOWS_ROOTS = {"\\", "\\Program Files", "\\Program Files (x86)", USER_HOME};
+ private static final String[] MAC_ROOTS = {"/Applications", USER_HOME};
+ private static final String[] UNIX_ROOTS = {USER_HOME};
+
+ private final String productName;
+ private final String windowsName;
+ private final String unixName;
+ private final String macAppName;
+ private final List<String> executableNames;
+ private final List<String> sourceDirsOnWindows;
+ private final List<String> sourceDirsOnMac;
+ private final List<String> sourceDirsOnUnix;
+ private final String iniFileName;
+ private final Pattern locationSelectors;
+ private final String directoryName;
+ private final URL ideIcon;
+
+ public StandardProductDescriptor(String productName, String baseName, String directoryName, URL ideIcon, Collection<String> alternativeDirectoryNames) {
+ this.productName = productName;
+ this.windowsName = baseName + ".exe";
+ this.unixName = baseName;
+ this.macAppName = baseName + ".app";
+ this.executableNames = executableNames(baseName);
+ this.sourceDirsOnWindows = generateAlternatives(WINDOWS_ROOTS, "\\", alternativeDirectoryNames);
+ this.sourceDirsOnMac = generateAlternatives(MAC_ROOTS, "/", alternativeDirectoryNames);
+ this.sourceDirsOnUnix = generateAlternatives(UNIX_ROOTS, "/", alternativeDirectoryNames);
+ this.iniFileName = baseName + ".ini";
+ this.locationSelectors = getLocationSelectors(baseName);
+ this.directoryName = directoryName.toLowerCase();
+ this.ideIcon = ideIcon;
+ }
+
+ @Override public String getProductName() {
+ return productName;
+ }
+
+ @Override public String getWindowsExecutableName() {
+ return windowsName;
+ }
+
+ @Override public String getUnixAppName() {
+ return unixName;
+ }
+
+ @Override public String getMacAppName() {
+ return macAppName;
+ }
+
+ @Override public String getDirectoryName() {
+ return directoryName;
+ }
+
+ @Override public List<String> getExecutableNames() {
+ return executableNames;
+ }
+
+ @Override public List<String> getSourceDirsOnWindows() {
+ return sourceDirsOnWindows;
+ }
+
+ @Override public List<String> getSourceDirsOnMac() {
+ return sourceDirsOnMac;
+ }
+
+ @Override public List<String> getSourceDirsOnUnix() {
+ return sourceDirsOnUnix;
+ }
+
+ @Override public String getIniFileName() {
+ return iniFileName;
+ }
+
+ @Override public Pattern getLocationSelectors() {
+ return locationSelectors;
+ }
+
+ @Override public URL getIdeIcon() {
+ return ideIcon;
+ }
+
+ private static Pattern getLocationSelectors(String baseName) {
+ return Pattern.compile(String.format(platformPattern(), baseName.toLowerCase()), Pattern.CASE_INSENSITIVE);
+ }
+
+ private static String platformPattern() {
+ switch (OsUtils.getOS()) {
+ case MAC_OS_X:
+ return "^(%s|%<s\\.ini|%<s\\.app)$";
+ case WINDOWS:
+ return "^(%sc?\\.exe|%<s\\.ini)$";
+ default:
+ case UNIX:
+ return "^(%s|%<s\\.ini)$";
+ }
+ }
+
+ private static List<String> executableNames(String baseName) {
+ String base = baseName.toLowerCase();
+ return Collections.unmodifiableList(Arrays.asList(base, base + ".app", base + ".exe", base + "c.exe"));
+ }
+
+ private static List<String> generateAlternatives(String[] roots, String pathSeparator, Collection<String> alternatives) {
+ List<String> result = new ArrayList<String>();
+ for (String root : roots) {
+ result.add(concat(root, pathSeparator, ""));
+ for (String alternative : alternatives) {
+ result.add(concat(root, pathSeparator, alternative));
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ private static String concat(String base, String pathSeparator, String alternative) {
+ if (alternative.isEmpty()) {
+ return base;
+ }
+ if (base.endsWith(pathSeparator)) {
+ return base + alternative.replaceAll("[\\/]", "\\" + pathSeparator);
+ }
+ return base + pathSeparator + alternative.replaceAll("[\\/]", "\\" + pathSeparator);
+ }
+}
diff --git a/src/installer/lombok/installer/eclipse/myeclipse.png b/src/installer/lombok/installer/eclipse/myeclipse.png
new file mode 100644
index 00000000..49c4fab3
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/myeclipse.png
Binary files differ
diff --git a/src/installer/lombok/installer/eclipse/rhds.png b/src/installer/lombok/installer/eclipse/rhds.png
new file mode 100644
index 00000000..ca7738e6
--- /dev/null
+++ b/src/installer/lombok/installer/eclipse/rhds.png
Binary files differ
diff --git a/src/j9stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java b/src/j9stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java
new file mode 100644
index 00000000..ffb99030
--- /dev/null
+++ b/src/j9stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
+ * and/or other contributors as indicated by the @authors tag. See the
+ * copyright.txt file in the distribution for a full listing of all
+ * contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mapstruct.ap.spi;
+
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A contract to be implemented by other annotation processors which - against the design philosophy of JSR 269 - alter
+ * the types under compilation.
+ * <p>
+ * This contract will be queried by MapStruct when examining types referenced by mappers to be generated, most notably
+ * the source and target types of mapping methods. If at least one AST-modifying processor announces further changes to
+ * such type, the generation of the affected mapper(s) will be deferred to a future round in the annnotation processing
+ * cycle.
+ * <p>
+ * Implementations are discovered via the service loader, i.e. a JAR providing an AST-modifying processor needs to
+ * declare its implementation in a file {@code META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor}.
+ *
+ * @author Gunnar Morling
+ */
+//@org.mapstruct.util.Experimental
+public interface AstModifyingAnnotationProcessor {
+
+ /**
+ * Whether the specified type has been fully processed by this processor or not (i.e. this processor will amend the
+ * given type's structure after this invocation).
+ *
+ * @param type The type of interest
+ * @return {@code true} if this processor has fully processed the given type, {@code false} otherwise.
+ */
+ boolean isTypeComplete(TypeMirror type);
+} \ No newline at end of file
diff --git a/src/launch/lombok/launch/Agent.java b/src/launch/lombok/launch/Agent.java
new file mode 100644
index 00000000..7989e51f
--- /dev/null
+++ b/src/launch/lombok/launch/Agent.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.launch;
+
+import java.lang.instrument.Instrumentation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+final class Agent {
+ public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Throwable {
+ runLauncher(agentArgs, instrumentation, true);
+ }
+
+ public static void premain(String agentArgs, Instrumentation instrumentation) throws Throwable {
+ runLauncher(agentArgs, instrumentation, false);
+ }
+
+ private static void runLauncher(String agentArgs, Instrumentation instrumentation, boolean injected) throws Throwable {
+ ClassLoader cl = Main.createShadowClassLoader();
+ try {
+ Class<?> c = cl.loadClass("lombok.core.AgentLauncher");
+ Method m = c.getDeclaredMethod("runAgents", String.class, Instrumentation.class, boolean.class, Class.class);
+ m.invoke(null, agentArgs, instrumentation, injected, Agent.class);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+}
diff --git a/src/launch/lombok/launch/AnnotationProcessor.java b/src/launch/lombok/launch/AnnotationProcessor.java
new file mode 100644
index 00000000..c4f922b9
--- /dev/null
+++ b/src/launch/lombok/launch/AnnotationProcessor.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014-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
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.launch;
+
+import java.lang.reflect.Field;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Completion;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
+
+import sun.misc.Unsafe;
+
+class AnnotationProcessorHider {
+ public static class AstModificationNotifier implements AstModifyingAnnotationProcessor {
+ @Override public boolean isTypeComplete(TypeMirror type) {
+ if (System.getProperty("lombok.disable") != null) return true;
+ return AstModificationNotifierData.lombokInvoked;
+ }
+ }
+
+ static class AstModificationNotifierData {
+ volatile static boolean lombokInvoked = false;
+ }
+
+ public static class AnnotationProcessor extends AbstractProcessor {
+ private final AbstractProcessor instance = createWrappedInstance();
+
+ @Override public Set<String> getSupportedOptions() {
+ return instance.getSupportedOptions();
+ }
+
+ @Override public Set<String> getSupportedAnnotationTypes() {
+ return instance.getSupportedAnnotationTypes();
+ }
+
+ @Override public SourceVersion getSupportedSourceVersion() {
+ return instance.getSupportedSourceVersion();
+ }
+
+ @Override public void init(ProcessingEnvironment processingEnv) {
+ disableJava9SillyWarning();
+ AstModificationNotifierData.lombokInvoked = true;
+ instance.init(processingEnv);
+ super.init(processingEnv);
+ }
+
+ // 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"})
+ private void disableJava9SillyWarning() {
+ // JVM9 complains about using reflection to access packages from a module that aren't exported. This makes no sense; the whole point of reflection
+ // is to get past such issues. The only comment from the jigsaw team lead on this was some unspecified mumbling about security which makes no sense,
+ // as the SecurityManager is invoked to check such things. Therefore this warning is a bug, so we shall patch java to fix it.
+
+ try {
+ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ Unsafe u = (Unsafe) theUnsafe.get(null);
+
+ Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
+ Field logger = cls.getDeclaredField("logger");
+ u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
+ } catch (Throwable t) {
+ // We shall ignore it; the effect of this code failing is that the user gets to see a warning they remove with various --add-opens magic.
+ }
+ }
+
+ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return instance.process(annotations, roundEnv);
+ }
+
+ @Override public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
+ return instance.getCompletions(element, annotation, member, userText);
+ }
+
+ private static AbstractProcessor createWrappedInstance() {
+ ClassLoader cl = Main.createShadowClassLoader();
+ try {
+ Class<?> mc = cl.loadClass("lombok.core.AnnotationProcessor");
+ return (AbstractProcessor) mc.getDeclaredConstructor().newInstance();
+ } catch (Throwable t) {
+ if (t instanceof Error) throw (Error) t;
+ if (t instanceof RuntimeException) throw (RuntimeException) t;
+ throw new RuntimeException(t);
+ }
+ }
+ }
+
+ @SupportedAnnotationTypes("lombok.*")
+ public static class ClaimingProcessor extends AbstractProcessor {
+ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ return true;
+ }
+
+ @Override public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+ }
+}
diff --git a/src/launch/lombok/launch/Main.java b/src/launch/lombok/launch/Main.java
new file mode 100644
index 00000000..b81b6268
--- /dev/null
+++ b/src/launch/lombok/launch/Main.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014-2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.launch;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+
+class Main {
+ static ClassLoader createShadowClassLoader() {
+ return new ShadowClassLoader(Main.class.getClassLoader(), "lombok", null, Arrays.<String>asList(), Arrays.asList("lombok.patcher.Symbols"));
+ }
+
+ public static void main(String[] args) throws Throwable {
+ ClassLoader cl = createShadowClassLoader();
+ Class<?> mc = cl.loadClass("lombok.core.Main");
+ try {
+ mc.getMethod("main", String[].class).invoke(null, new Object[] {args});
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+}
diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java
new file mode 100644
index 00000000..72f006e8
--- /dev/null
+++ b/src/launch/lombok/launch/ShadowClassLoader.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2014-2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.launch;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+import java.util.WeakHashMap;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * The shadow classloader serves to completely hide almost all classes in a given jar file by using a different file ending.
+ *
+ * The shadow classloader also serves to link in a project as it is being developed (a 'bin' dir from an IDE for example).
+ * <p>
+ * Classes loaded by the shadowloader use ".SCL.<em>sclSuffix</em>" in addition to ".class". In other words, most of the class files in a given jar end in this suffix, which
+ * serves to hide them from any tool that isn't aware of the suffix (such as IDEs generating auto-complete dialogs, and javac's classpath in general). Only shadowloader can actually
+ * load these classes.
+ * <p>
+ * The shadowloader will pick up an alternate (priority) classpath, using normal class files, from the system property "<code>shadow.override.<em>sclSuffix</em></code>".
+ * This shadow classpath looks just like a normal java classpath; the path separator is applied (semi-colon on windows, colon elsewhere), and entries can consist of directories,
+ * jar files, or directories ending in "/*" to pick up all jars inside it.
+ * <p>
+ * Load order is as follows if at least one override is present:
+ * <li>First, if the resource is found in one of the paths stated in the shadow classpath, find that.
+ * <li>Next, ask the <code>parent</code> loader, which is passed during construction of the ShadowClassLoader.
+ * <li>Notably, this jar's contents are always skipped! (The idea of the shadow classpath is that this jar only functions as a launcher, not as a source of your actual application).
+ * </ul>
+ *
+ * If no overrides are present, the load order is as follows:
+ * <li>First, if the resource is found in our own jar (trying ".SCL.<em>sclSuffix</em>" first for any resource request ending in ".class"), return that.
+ * <li>Next, ask the <code>parent</code> loader.
+ * </ul>
+ *
+ * Use ShadowClassLoader to accomplish the following things:<ul>
+ * <li>Avoid contaminating the namespace of any project using an SCL-based jar. Autocompleters in IDEs will NOT suggest anything other than actual public API.
+ * <li>Like jarjar, allows folding in dependencies such as ASM without foisting these dependencies on projects that use this jar. shadowloader obviates the need for jarjar.
+ * <li>Allows an agent (which MUST be in jar form) to still load everything except this loader infrastructure from class files generated by the IDE, which should
+ * considerably help debugging, as you can now rely on the IDE's built-in auto-recompile features instead of having to run a full build everytime, and it should help
+ * with hot code replace and the like (this is what the {@code shadow.override} feature is for).
+ * </ul>
+ *
+ * Implementation note: {@code lombok.eclipse.agent.EclipseLoaderPatcher} <em>relies</em> on this class having no dependencies on any other class except the JVM boot class, notably
+ * including any other classes in this package, <strong>including</strong> inner classes. So, don't write closures, anonymous inner class literals,
+ * enums, or anything else that could cause the compilation of this file to produce more than 1 class file. In general, actually passing load control to this loader is a bit tricky
+ * so ensure that this class has zero dependencies on anything except java core classes.
+ */
+class ShadowClassLoader extends ClassLoader {
+ private static final String SELF_NAME = "lombok/launch/ShadowClassLoader.class";
+ private static final ConcurrentMap<String, Class<?>> highlanderMap = new ConcurrentHashMap<String, Class<?>>();
+
+ private final String SELF_BASE;
+ private final File SELF_BASE_FILE;
+ private final int SELF_BASE_LENGTH;
+
+ private final List<File> override = new ArrayList<File>();
+ private final String sclSuffix;
+ private final List<String> parentExclusion = new ArrayList<String>();
+ private final List<String> highlanders = new ArrayList<String>();
+
+ /**
+ * @param source The 'parent' classloader.
+ * @param sclSuffix The suffix of the shadowed class files in our own jar. For example, if this is {@code lombok}, then the class files in your jar should be {@code foo/Bar.SCL.lombok} and not {@code foo/Bar.class}.
+ * @param selfBase The (preferably absolute) path to our own jar. This jar will be searched for class/SCL.sclSuffix files.
+ * @param parentExclusion For example {@code "lombok."}; upon invocation of loadClass of this loader, the parent loader ({@code source}) will NOT be invoked if the class to be loaded begins with anything in the parent exclusion list. No exclusion is applied for getResource(s).
+ * @param highlanders SCL will put in extra effort to ensure that these classes (in simple class spec, so {@code foo.bar.baz.ClassName}) are only loaded once as a class, even if many different classloaders try to load classes, such as equinox/OSGi.
+ */
+ ShadowClassLoader(ClassLoader source, String sclSuffix, String selfBase, List<String> parentExclusion, List<String> highlanders) {
+ super(source);
+ this.sclSuffix = sclSuffix;
+ if (parentExclusion != null) for (String pe : parentExclusion) {
+ pe = pe.replace(".", "/");
+ if (!pe.endsWith("/")) pe = pe + "/";
+ this.parentExclusion.add(pe);
+ }
+ if (highlanders != null) for (String hl : highlanders) {
+ this.highlanders.add(hl);
+ }
+
+ if (selfBase != null) {
+ SELF_BASE = selfBase;
+ SELF_BASE_LENGTH = selfBase.length();
+ } else {
+ URL sclClassUrl = ShadowClassLoader.class.getResource("ShadowClassLoader.class");
+ String sclClassStr = sclClassUrl == null ? null : sclClassUrl.toString();
+ if (sclClassStr == null || !sclClassStr.endsWith(SELF_NAME)) {
+ ClassLoader cl = ShadowClassLoader.class.getClassLoader();
+ throw new RuntimeException("ShadowLoader can't find itself. SCL loader type: " + (cl == null ? "*NULL*" : cl.getClass().toString()));
+ }
+ SELF_BASE_LENGTH = sclClassStr.length() - SELF_NAME.length();
+ String decoded;
+ try {
+ decoded = URLDecoder.decode(sclClassStr.substring(0, SELF_BASE_LENGTH), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalError("UTF-8 not available");
+ }
+ SELF_BASE = decoded;
+ }
+
+ if (SELF_BASE.startsWith("jar:file:") && SELF_BASE.endsWith("!/")) SELF_BASE_FILE = new File(SELF_BASE.substring(9, SELF_BASE.length() - 2));
+ else if (SELF_BASE.startsWith("file:")) SELF_BASE_FILE = new File(SELF_BASE.substring(5));
+ else SELF_BASE_FILE = new File(SELF_BASE);
+ String scl = System.getProperty("shadow.override." + sclSuffix);
+ if (scl != null && !scl.isEmpty()) {
+ for (String part : scl.split("\\s*" + (File.pathSeparatorChar == ';' ? ";" : ":") + "\\s*")) {
+ if (part.endsWith("/*") || part.endsWith(File.separator + "*")) {
+ addOverrideJarDir(part.substring(0, part.length() - 2));
+ } else {
+ addOverrideClasspathEntry(part);
+ }
+ }
+ }
+ }
+
+ private final Map<String, Object> mapJarPathToTracker = new HashMap<String, Object>();
+ private static final Map<Object, String> mapTrackerToJarPath = new WeakHashMap<Object, String>();
+ private static final Map<Object, Set<String>> mapTrackerToJarContents = new WeakHashMap<Object, Set<String>>();
+
+ /**
+ * This cache ensures that any given jar file is only opened once in order to determine the full contents of it.
+ * We use 'trackers' to make sure that the bulk of the memory taken up by this cache (the list of strings representing the content of a jar file)
+ * gets garbage collected if all ShadowClassLoaders that ever tried to request a listing of this jar file, are garbage collected.
+ */
+ private Set<String> getOrMakeJarListing(final String absolutePathToJar) {
+ synchronized (mapTrackerToJarPath) {
+ /*
+ * 1) Check our private instance JarPath-to-Tracker Mappings:
+ */
+ Object ourTracker = mapJarPathToTracker.get(absolutePathToJar);
+ if (ourTracker != null) {
+ /*
+ * Yes, we are already tracking this Jar. Just return its contents...
+ */
+ return mapTrackerToJarContents.get(ourTracker);
+ }
+
+ /*
+ * 2) Not tracked by us as yet. Check statically whether others have tracked this JarPath:
+ */
+ for (Entry<Object, String> entry : mapTrackerToJarPath.entrySet()) {
+ if (entry.getValue().equals(absolutePathToJar)) {
+ /*
+ * Yes, 3rd party is tracking this jar. We must track too, then return its contents.
+ */
+ Object otherTracker = entry.getKey();
+ mapJarPathToTracker.put(absolutePathToJar, otherTracker);
+ return mapTrackerToJarContents.get(otherTracker);
+ }
+ }
+
+ /*
+ * 3) Not tracked by anyone so far. Build, publish, track & return Jar contents...
+ */
+ Object newTracker = new Object();
+ Set<String> jarMembers = getJarMemberSet(absolutePathToJar);
+
+ mapTrackerToJarContents.put(newTracker, jarMembers);
+ mapTrackerToJarPath.put(newTracker, absolutePathToJar);
+ mapJarPathToTracker.put(absolutePathToJar, newTracker);
+
+ return jarMembers;
+ }
+ }
+
+ /**
+ * Return a {@link Set} of members in the Jar identified by {@code absolutePathToJar}.
+ *
+ * @param absolutePathToJar Cache key
+ * @return a Set with the Jar member-names
+ */
+ private Set<String> getJarMemberSet(String absolutePathToJar) {
+ /*
+ * Note:
+ * Our implementation returns a HashSet. initialCapacity and loadFactor are carefully tweaked for speed and RAM optimization purposes.
+ *
+ * Benchmark:
+ * The HashSet implementation is about 10% slower to build (only happens once) than the ArrayList.
+ * The HashSet with shiftBits = 1 was about 33 times(!) faster than the ArrayList for retrievals.
+ */
+ try {
+ int shiftBits = 1; // (fast, but big) 0 <= shiftBits <= 5, say (slower & compact)
+ JarFile jar = new JarFile(absolutePathToJar);
+
+ /*
+ * Find the first power of 2 >= JarSize (as calculated in HashSet constructor)
+ */
+ int jarSizePower2 = Integer.highestOneBit(jar.size());
+ if (jarSizePower2 != jar.size()) jarSizePower2 <<= 1;
+ if (jarSizePower2 == 0) jarSizePower2 = 1;
+
+ Set<String> jarMembers = new HashSet<String>(jarSizePower2 >> shiftBits, 1 << shiftBits);
+ try {
+ Enumeration<JarEntry> entries = jar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry = entries.nextElement();
+ if (jarEntry.isDirectory()) continue;
+ jarMembers.add(jarEntry.getName());
+ }
+ } catch (Exception ignore) {
+ // ignored; if the jar can't be read, treating it as if the jar contains no classes is just what we want.
+ } finally {
+ jar.close();
+ }
+ return jarMembers;
+ }
+ catch (Exception newJarFileException) {
+ return Collections.emptySet();
+ }
+ }
+
+ /**
+ * Looks up {@code altName} in {@code location}, and if that isn't found, looks up {@code name}; {@code altName} can be null in which case it is skipped.
+ */
+ private URL getResourceFromLocation(String name, String altName, File location) {
+ if (location.isDirectory()) {
+ try {
+ if (altName != null) {
+ File f = new File(location, altName);
+ if (f.isFile() && f.canRead()) return f.toURI().toURL();
+ }
+
+ File f = new File(location, name);
+ if (f.isFile() && f.canRead()) return f.toURI().toURL();
+ return null;
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+ if (!location.isFile() || !location.canRead()) return null;
+
+ File absoluteFile; {
+ try {
+ absoluteFile = location.getCanonicalFile();
+ } catch (Exception e) {
+ absoluteFile = location.getAbsoluteFile();
+ }
+ }
+ Set<String> jarContents = getOrMakeJarListing(absoluteFile.getAbsolutePath());
+
+ String absoluteUri = absoluteFile.toURI().toString();
+
+ try {
+ if (jarContents.contains(altName)) {
+ return new URI("jar:" + absoluteUri + "!/" + altName).toURL();
+ }
+ } catch (Exception ignore) {
+ // intentional fallthrough
+ }
+
+ try {
+ if (jarContents.contains(name)) {
+ return new URI("jar:" + absoluteUri + "!/" + name).toURL();
+ }
+ } catch(Exception ignore) {
+ // intentional fallthrough
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks if the stated item is located inside the same classpath root as the jar that hosts ShadowClassLoader.class. {@code item} and {@code name} refer to the same thing.
+ */
+ private boolean inOwnBase(URL item, String name) {
+ if (item == null) return false;
+ String itemString = item.toString();
+ return (itemString.length() == SELF_BASE_LENGTH + name.length()) && SELF_BASE.regionMatches(0, itemString, 0, SELF_BASE_LENGTH);
+ }
+
+ @Override public Enumeration<URL> getResources(String name) throws IOException {
+ String altName = null;
+ if (name.endsWith(".class")) altName = name.substring(0, name.length() - 6) + ".SCL." + sclSuffix;
+
+ // Vector? Yes, we need one:
+ // * We can NOT make inner classes here (this class is loaded with special voodoo magic in eclipse, as a one off, it's not a full loader.
+ // * We need to return an enumeration.
+ // * We can't make one on the fly.
+ // * ArrayList can't make these.
+ Vector<URL> vector = new Vector<URL>();
+
+ for (File ce : override) {
+ URL url = getResourceFromLocation(name, altName, ce);
+ if (url != null) vector.add(url);
+ }
+
+ if (override.isEmpty()) {
+ URL fromSelf = getResourceFromLocation(name, altName, SELF_BASE_FILE);
+ if (fromSelf != null) vector.add(fromSelf);
+ }
+
+ Enumeration<URL> sec = super.getResources(name);
+ while (sec.hasMoreElements()) {
+ URL item = sec.nextElement();
+ if (!inOwnBase(item, name)) vector.add(item);
+ }
+
+ if (altName != null) {
+ Enumeration<URL> tern = super.getResources(altName);
+ while (tern.hasMoreElements()) {
+ URL item = tern.nextElement();
+ if (!inOwnBase(item, altName)) vector.add(item);
+ }
+ }
+
+ return vector.elements();
+ }
+
+ @Override public URL getResource(String name) {
+ return getResource_(name, false);
+ }
+
+ private URL getResource_(String name, boolean noSuper) {
+ String altName = null;
+ if (name.endsWith(".class")) altName = name.substring(0, name.length() - 6) + ".SCL." + sclSuffix;
+ for (File ce : override) {
+ URL url = getResourceFromLocation(name, altName, ce);
+ if (url != null) return url;
+ }
+
+ if (!override.isEmpty()) {
+ if (noSuper) return null;
+ if (altName != null) {
+ try {
+ URL res = getResourceSkippingSelf(altName);
+ if (res != null) return res;
+ } catch (IOException ignore) {}
+ }
+
+ try {
+ return getResourceSkippingSelf(name);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ URL url = getResourceFromLocation(name, altName, SELF_BASE_FILE);
+ if (url != null) return url;
+
+ if (altName != null) {
+ URL res = super.getResource(altName);
+ if (res != null && (!noSuper || inOwnBase(res, altName))) return res;
+ }
+
+ URL res = super.getResource(name);
+ if (res != null && (!noSuper || inOwnBase(res, name))) return res;
+ return null;
+ }
+
+ private boolean exclusionListMatch(String name) {
+ for (String pe : parentExclusion) {
+ if (name.startsWith(pe)) return true;
+ }
+ return false;
+ }
+
+ private URL getResourceSkippingSelf(String name) throws IOException {
+ URL candidate = super.getResource(name);
+ if (candidate == null) return null;
+ if (!inOwnBase(candidate, name)) return candidate;
+
+ Enumeration<URL> en = super.getResources(name);
+ while (en.hasMoreElements()) {
+ candidate = en.nextElement();
+ if (!inOwnBase(candidate, name)) return candidate;
+ }
+
+ return null;
+ }
+
+ @Override public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ {
+ Class<?> alreadyLoaded = findLoadedClass(name);
+ if (alreadyLoaded != null) return alreadyLoaded;
+ }
+
+ if (highlanders.contains(name)) {
+ Class<?> c = highlanderMap.get(name);
+ if (c != null) return c;
+ }
+
+ String fileNameOfClass = name.replace(".", "/") + ".class";
+ URL res = getResource_(fileNameOfClass, true);
+ if (res == null) {
+ if (!exclusionListMatch(fileNameOfClass)) return super.loadClass(name, resolve);
+ throw new ClassNotFoundException(name);
+ }
+
+ byte[] b;
+ int p = 0;
+ try {
+ InputStream in = res.openStream();
+
+ try {
+ b = new byte[65536];
+ while (true) {
+ int r = in.read(b, p, b.length - p);
+ if (r == -1) break;
+ p += r;
+ if (p == b.length) {
+ byte[] nb = new byte[b.length * 2];
+ System.arraycopy(b, 0, nb, 0, p);
+ b = nb;
+ }
+ }
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ throw new ClassNotFoundException("I/O exception reading class " + name, e);
+ }
+
+ Class<?> c;
+ try {
+ c = defineClass(name, b, 0, p);
+ } catch (LinkageError e) {
+ if (highlanders.contains(name)) {
+ Class<?> alreadyDefined = highlanderMap.get(name);
+ if (alreadyDefined != null) return alreadyDefined;
+ }
+ try {
+ c = this.findLoadedClass(name);
+ } catch (LinkageError e2) {
+ throw e;
+ }
+ if (c == null) throw e;
+ }
+
+ if (highlanders.contains(name)) {
+ Class<?> alreadyDefined = highlanderMap.putIfAbsent(name, c);
+ if (alreadyDefined != null) c = alreadyDefined;
+ }
+
+ if (resolve) resolveClass(c);
+ return c;
+ }
+
+ public void addOverrideJarDir(String dir) {
+ File f = new File(dir);
+ for (File j : f.listFiles()) {
+ if (j.getName().toLowerCase().endsWith(".jar") && j.canRead() && j.isFile()) override.add(j);
+ }
+ }
+
+ public void addOverrideClasspathEntry(String entry) {
+ override.add(new File(entry));
+ }
+}
diff --git a/src/stubs/com/sun/tools/javac/code/Symbol.java b/src/stubs/com/sun/tools/javac/code/Symbol.java
new file mode 100644
index 00000000..4aef63ad
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/code/Symbol.java
@@ -0,0 +1,84 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
+package com.sun.tools.javac.code;
+
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+
+import com.sun.tools.javac.util.Name;
+
+public abstract class Symbol implements Element {
+ public Type type;
+ public Name name;
+
+ public long flags() { return 0; }
+ public boolean isStatic() { return false; }
+ public boolean isConstructor() { return false; }
+ public boolean isLocal() { return false; }
+ public Name flatName() { return null; }
+ public Name getQualifiedName() { return null; }
+ public <A extends Annotation> A[] getAnnotationsByType(Class<A> annoType) { return null; }
+ @Override public java.util.List<Attribute.Compound> getAnnotationMirrors() { return null; }
+ @Override public TypeMirror asType() { return null; }
+ public <A extends java.lang.annotation.Annotation> A getAnnotation(Class<A> annoType) { return null; }
+ @Override public Name getSimpleName() { return null; }
+ @Override public java.util.List<Symbol> getEnclosedElements() { return null; }
+ @Override public Element getEnclosingElement() { return null; }
+
+ public static abstract class TypeSymbol extends Symbol {}
+
+ public static class MethodSymbol extends Symbol implements ExecutableElement {
+ public MethodSymbol(long flags, Name name, Type type, Symbol owner) {}
+ @Override public ElementKind getKind() { return null; }
+ @Override public Set<Modifier> getModifiers() { return null; }
+ @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; }
+ @Override public java.util.List<? extends TypeParameterElement> getTypeParameters() { return null; }
+ @Override public TypeMirror getReturnType() { return null; }
+ @Override public java.util.List<? extends VariableElement> getParameters() { return null; }
+ @Override public boolean isVarArgs() { return false; }
+ @Override public java.util.List<? extends TypeMirror> getThrownTypes() { return null; }
+ @Override public AnnotationValue getDefaultValue() { return null; }
+ public TypeMirror getReceiverType() { return null; }
+ public boolean isDefault() { return false; }
+ public com.sun.tools.javac.util.List<VarSymbol> params() { return null; }
+ }
+
+ public static class VarSymbol extends Symbol implements VariableElement {
+ public Type type;
+ @Override public ElementKind getKind() { return null; }
+ @Override public Set<Modifier> getModifiers() { return null; }
+ @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; }
+ @Override public Object getConstantValue() { return null; }
+ }
+
+ public static class ClassSymbol extends TypeSymbol implements TypeElement {
+ @Override public Name getQualifiedName() { return null; }
+ @Override public java.util.List<? extends TypeMirror> getInterfaces() { return null; }
+ @Override public TypeMirror getSuperclass() { return null; }
+ @Override public ElementKind getKind() { return null; }
+ @Override public Set<Modifier> getModifiers() { return null; }
+ @Override public NestingKind getNestingKind() { return null; }
+ @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; }
+ @Override public java.util.List<? extends TypeParameterElement> getTypeParameters() { return null; }
+ }
+
+ // JDK9
+ public static class ModuleSymbol extends TypeSymbol {
+ @Override public ElementKind getKind() { return null; }
+ @Override public Set<Modifier> getModifiers() { return null; }
+ @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; }
+ }
+}
diff --git a/src/stubs/com/sun/tools/javac/code/Symtab.java b/src/stubs/com/sun/tools/javac/code/Symtab.java
new file mode 100644
index 00000000..2b524e4c
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/code/Symtab.java
@@ -0,0 +1,20 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
+package com.sun.tools.javac.code;
+
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.ModuleSymbol;
+import com.sun.tools.javac.util.Context;
+
+public class Symtab {
+ // Shared by JDK6-9
+ public ClassSymbol methodClass;
+ public Type iterableType;
+ public Type objectType;
+ public static Symtab instance(Context context) {return null;}
+ public Type unknownType;
+
+ // JDK 9
+ public ModuleSymbol unnamedModule;
+}
diff --git a/src/stubs/com/sun/tools/javac/file/BaseFileManager.java b/src/stubs/com/sun/tools/javac/file/BaseFileManager.java
new file mode 100644
index 00000000..a56a2430
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/file/BaseFileManager.java
@@ -0,0 +1,18 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
+package com.sun.tools.javac.file;
+
+import javax.tools.JavaFileManager;
+
+import com.sun.tools.javac.main.Option;
+import com.sun.tools.javac.util.Context;
+
+import java.nio.charset.Charset;
+import java.util.Map;
+
+public abstract class BaseFileManager implements JavaFileManager {
+ protected BaseFileManager(Charset charset) {}
+ public void setContext(Context context) {}
+ public boolean handleOptions(Map<Option, String> deferredFileManagerOptions) { return false; }
+}
diff --git a/src/stubs/com/sun/tools/javac/file/PathFileObject.java b/src/stubs/com/sun/tools/javac/file/PathFileObject.java
new file mode 100644
index 00000000..c1ee6074
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/file/PathFileObject.java
@@ -0,0 +1,12 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
+package com.sun.tools.javac.file;
+
+import java.nio.file.Path;
+
+import javax.tools.JavaFileObject;
+
+public abstract class PathFileObject implements JavaFileObject {
+ protected PathFileObject(BaseFileManager fileManager, Path path) {}
+}
diff --git a/src/stubs/com/sun/tools/javac/main/Arguments.java b/src/stubs/com/sun/tools/javac/main/Arguments.java
new file mode 100644
index 00000000..ea866b6e
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/main/Arguments.java
@@ -0,0 +1,13 @@
+package com.sun.tools.javac.main;
+
+import java.util.Map;
+
+import com.sun.tools.javac.util.Context;
+
+public class Arguments {
+ public static final Context.Key<Arguments> argsKey = new Context.Key<Arguments>();
+ public static Arguments instance(Context context) { return null; }
+ public void init(String ownName, String... argv) {}
+ public Map<Option, String> getDeferredFileManagerOptions() { return null; }
+ public boolean validate() { return false; }
+}
diff --git a/src/stubs/com/sun/tools/javac/main/JavaCompiler.java b/src/stubs/com/sun/tools/javac/main/JavaCompiler.java
new file mode 100644
index 00000000..d0e7b38f
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/main/JavaCompiler.java
@@ -0,0 +1,37 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
+package com.sun.tools.javac.main;
+
+import java.io.IOException;
+import java.util.Collection;
+import javax.annotation.processing.Processor;
+import javax.tools.JavaFileObject;
+
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.comp.Todo;
+
+public class JavaCompiler {
+ // Shared by JDK6-9
+ public boolean keepComments;
+ public boolean genEndPos;
+ public Todo todo;
+
+ public JavaCompiler(Context context) {}
+ public int errorCount() { return 0; }
+ public static String version() { return "<stub>"; }
+ public JCCompilationUnit parse(String fileName) throws IOException { return null; }
+ public List<JCCompilationUnit> enterTrees(List<JCCompilationUnit> roots) {return null;}
+
+ //JDK up to 8
+ public void initProcessAnnotations(Iterable<? extends Processor> processors) throws IOException {}
+ public JavaCompiler processAnnotations(List<JCCompilationUnit> roots, List<String> classnames) {return this;}
+
+ // JDK 9
+ public void initProcessAnnotations(Iterable<? extends Processor> processors, Collection<? extends JavaFileObject> initialFiles, Collection<String> initialClassNames) {}
+ public void processAnnotations(List<JCCompilationUnit> roots, Collection<String> classnames) {}
+ public void close() {}
+ public List<JCCompilationUnit> initModules(List<JCCompilationUnit> roots) { return null; }
+}
diff --git a/src/stubs/com/sun/tools/javac/main/Option.java b/src/stubs/com/sun/tools/javac/main/Option.java
index f3229c78..ae955772 100644
--- a/src/stubs/com/sun/tools/javac/main/Option.java
+++ b/src/stubs/com/sun/tools/javac/main/Option.java
@@ -7,4 +7,5 @@ package com.sun.tools.javac.main;
public enum Option {
;
public String text;
+ public String primaryName;
}
diff --git a/src/stubs/com/sun/tools/javac/parser/JavacParser.java b/src/stubs/com/sun/tools/javac/parser/JavacParser.java
index da42f37a..4f1f3380 100644
--- a/src/stubs/com/sun/tools/javac/parser/JavacParser.java
+++ b/src/stubs/com/sun/tools/javac/parser/JavacParser.java
@@ -1,3 +1,6 @@
+/*
+ * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these.
+ */
package com.sun.tools.javac.parser;
import com.sun.tools.javac.tree.JCTree;
@@ -6,6 +9,9 @@ public class JavacParser {
protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions) {
}
+ protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions, boolean parseModuleInfo) {
+ }
+
public JCTree.JCCompilationUnit parseCompilationUnit() {
return null;
}
diff --git a/src/stubs/com/sun/tools/javac/util/Options.java b/src/stubs/com/sun/tools/javac/util/Options.java
new file mode 100644
index 00000000..e7ba8960
--- /dev/null
+++ b/src/stubs/com/sun/tools/javac/util/Options.java
@@ -0,0 +1,20 @@
+package com.sun.tools.javac.util;
+
+import java.util.Set;
+
+import com.sun.tools.javac.main.Option;
+import com.sun.tools.javac.main.OptionName;
+import com.sun.tools.javac.main.JavacOption;
+
+public class Options {
+ public Options(Context context) {}
+ public static final Context.Key<Options> optionsKey = new Context.Key<Options>();
+ public static Options instance(Context context) { return null; }
+ public String get(String key) { return null; }
+ public String get(Option opt) { return null; }
+ public String get(OptionName name) { return null; }
+ public String get(JavacOption.Option opt) { return null; }
+ public void putAll(Options o) {}
+ public void put(String key, String value) {}
+ public Set<String> keySet() { return null; }
+}
diff --git a/src/stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java b/src/stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java
new file mode 100644
index 00000000..ffb99030
--- /dev/null
+++ b/src/stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
+ * and/or other contributors as indicated by the @authors tag. See the
+ * copyright.txt file in the distribution for a full listing of all
+ * contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mapstruct.ap.spi;
+
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A contract to be implemented by other annotation processors which - against the design philosophy of JSR 269 - alter
+ * the types under compilation.
+ * <p>
+ * This contract will be queried by MapStruct when examining types referenced by mappers to be generated, most notably
+ * the source and target types of mapping methods. If at least one AST-modifying processor announces further changes to
+ * such type, the generation of the affected mapper(s) will be deferred to a future round in the annnotation processing
+ * cycle.
+ * <p>
+ * Implementations are discovered via the service loader, i.e. a JAR providing an AST-modifying processor needs to
+ * declare its implementation in a file {@code META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor}.
+ *
+ * @author Gunnar Morling
+ */
+//@org.mapstruct.util.Experimental
+public interface AstModifyingAnnotationProcessor {
+
+ /**
+ * Whether the specified type has been fully processed by this processor or not (i.e. this processor will amend the
+ * given type's structure after this invocation).
+ *
+ * @param type The type of interest
+ * @return {@code true} if this processor has fully processed the given type, {@code false} otherwise.
+ */
+ boolean isTypeComplete(TypeMirror type);
+} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/code/Attribute.java b/src/stubsstubs/com/sun/tools/javac/code/Attribute.java
new file mode 100644
index 00000000..29bd54a9
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/code/Attribute.java
@@ -0,0 +1,15 @@
+package com.sun.tools.javac.code;
+
+import java.util.Map;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+
+public abstract class Attribute {
+ public static class Compound extends Attribute implements AnnotationMirror {
+ public DeclaredType getAnnotationType() { return null; }
+ public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues() { return null; }
+ }
+} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/code/Type.java b/src/stubsstubs/com/sun/tools/javac/code/Type.java
new file mode 100644
index 00000000..c130ae9c
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/code/Type.java
@@ -0,0 +1,3 @@
+package com.sun.tools.javac.code;
+
+public class Type {} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/comp/Todo.java b/src/stubsstubs/com/sun/tools/javac/comp/Todo.java
new file mode 100644
index 00000000..006a7fd3
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/comp/Todo.java
@@ -0,0 +1,3 @@
+package com.sun.tools.javac.comp;
+
+public class Todo {} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/main/JavacOption.java b/src/stubsstubs/com/sun/tools/javac/main/JavacOption.java
new file mode 100644
index 00000000..8e74d3d3
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/main/JavacOption.java
@@ -0,0 +1,5 @@
+package com.sun.tools.javac.main;
+
+public class JavacOption {
+ public static class Option {}
+} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/main/Option.java b/src/stubsstubs/com/sun/tools/javac/main/Option.java
new file mode 100644
index 00000000..45988e4c
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/main/Option.java
@@ -0,0 +1,3 @@
+package com.sun.tools.javac.main;
+
+public class Option {} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/main/OptionName.java b/src/stubsstubs/com/sun/tools/javac/main/OptionName.java
new file mode 100644
index 00000000..b1866633
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/main/OptionName.java
@@ -0,0 +1,3 @@
+package com.sun.tools.javac.main;
+
+public class OptionName {} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/util/Context.java b/src/stubsstubs/com/sun/tools/javac/util/Context.java
new file mode 100644
index 00000000..a090714e
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/util/Context.java
@@ -0,0 +1,5 @@
+package com.sun.tools.javac.util;
+
+public class Context {
+ public static class Key<T> {}
+} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/util/List.java b/src/stubsstubs/com/sun/tools/javac/util/List.java
new file mode 100644
index 00000000..16418a2b
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/util/List.java
@@ -0,0 +1,3 @@
+package com.sun.tools.javac.util;
+
+public class List<T> {} \ No newline at end of file
diff --git a/src/stubsstubs/com/sun/tools/javac/util/Name.java b/src/stubsstubs/com/sun/tools/javac/util/Name.java
new file mode 100644
index 00000000..c0e81926
--- /dev/null
+++ b/src/stubsstubs/com/sun/tools/javac/util/Name.java
@@ -0,0 +1,8 @@
+package com.sun.tools.javac.util;
+
+public class Name implements javax.lang.model.element.Name {
+ public boolean contentEquals(CharSequence cs) { return false; }
+ public int length() { return 0; }
+ public char charAt(int idx) { return '\0'; }
+ public CharSequence subSequence(int a, int b) { return null; }
+} \ No newline at end of file
diff --git a/src/testAP/org/projectlombok/testAp/ExampleAnnotation.java b/src/testAP/org/projectlombok/testAp/ExampleAnnotation.java
new file mode 100644
index 00000000..b419326b
--- /dev/null
+++ b/src/testAP/org/projectlombok/testAp/ExampleAnnotation.java
@@ -0,0 +1,10 @@
+package org.projectlombok.testAp;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface ExampleAnnotation {}
diff --git a/src/testAP/org/projectlombok/testAp/TestAp.java b/src/testAP/org/projectlombok/testAp/TestAp.java
new file mode 100644
index 00000000..b5f20d21
--- /dev/null
+++ b/src/testAP/org/projectlombok/testAp/TestAp.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.projectlombok.testAp;
+
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+@SupportedAnnotationTypes("org.projectlombok.testAp.ExampleAnnotation")
+public final class TestAp extends AbstractProcessor {
+ private int roundCounter = 0;
+ private static final long START = System.currentTimeMillis();
+
+ private void log(String txt) {
+ System.out.printf("***[%3d]: %s\n", System.currentTimeMillis() - START, txt);
+ }
+
+ @Override public void init(ProcessingEnvironment processingEnv) {
+ log("TestAP in init");
+ super.init(processingEnv);
+ }
+
+ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ roundCounter++;
+ log("TestAP in round " + roundCounter);
+ boolean foundGetTest = false;
+ int annotatedElemCount = 0;
+ for (Element annotated : roundEnv.getElementsAnnotatedWith(ExampleAnnotation.class)) {
+ annotatedElemCount++;
+ for (Element child : annotated.getEnclosedElements()) {
+ if (child.getSimpleName().toString().equals("getTest") && child.getKind() == ElementKind.METHOD) foundGetTest = true;
+ if (child instanceof ExecutableElement) {
+ TypeMirror returnType = ((ExecutableElement) child).getReturnType();
+ System.out.println("RETURN TYPE for " + child.getSimpleName() + ": " + returnType.getClass() + " -- " + returnType.toString());
+ }
+ }
+ }
+
+ if (foundGetTest) log("RESULT: POSITIVE -- found the getTest method");
+ else if (annotatedElemCount > 0) log("RESULT: NEGATIVE -- found the example class but there's no getTest method in it according to the type mirror.");
+ else log("RESULT: AMBIVALENT -- The example class is not provided by 'getElementsAnnotatedWith' in this round. Not an issue, unless previously you got a NEGATIVE result.");
+
+ return false;
+ }
+
+ @Override public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+}
diff --git a/src/useTestAP/UseTestAp.java b/src/useTestAP/UseTestAp.java
new file mode 100644
index 00000000..2d7eab7b
--- /dev/null
+++ b/src/useTestAP/UseTestAp.java
@@ -0,0 +1,13 @@
+@org.projectlombok.testAp.ExampleAnnotation
+//@lombok.experimental.Accessors(chain=true)
+public class UseTestAp {
+ @lombok.Setter @lombok.Getter String test;
+
+ public void confirmGetTestExists() {
+ System.out.println(getTest());
+ }
+
+ public UseTestAp returningSelf() {
+ return this;
+ }
+}
diff --git a/src/utils/lombok/core/LombokImmutableList.java b/src/utils/lombok/core/LombokImmutableList.java
index e0e1136c..4603f2ad 100644
--- a/src/utils/lombok/core/LombokImmutableList.java
+++ b/src/utils/lombok/core/LombokImmutableList.java
@@ -80,6 +80,12 @@ public final class LombokImmutableList<T> implements Iterable<T> {
return copyOf(list);
}
+ public static <T> LombokImmutableList<T> copyOf(T[] array) {
+ Object[] content = new Object[array.length];
+ System.arraycopy(array, 0, content, 0, array.length);
+ return new LombokImmutableList<T>(content);
+ }
+
private LombokImmutableList(Object[] content) {
this.content = content;
}
diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java
index c2a863d5..18b22256 100644
--- a/src/utils/lombok/eclipse/Eclipse.java
+++ b/src/utils/lombok/eclipse/Eclipse.java
@@ -98,15 +98,20 @@ public class Eclipse {
* string containing the same fully qualified name with dots in the string.
*/
public static boolean nameEquals(char[][] typeName, String string) {
- StringBuilder sb = new StringBuilder();
- boolean first = true;
- for (char[] elem : typeName) {
- if (first) first = false;
- else sb.append('.');
- sb.append(elem);
+ int pos = 0, len = string.length();
+ for (int i = 0; i < typeName.length; i++) {
+ char[] t = typeName[i];
+ if (i > 0) {
+ if (pos == len) return false;
+ if (string.charAt(pos++) != '.') return false;
+ }
+ for (int j = 0; j < t.length; j++) {
+ if (pos == len) return false;
+ if (string.charAt(pos++) != t[j]) return false;
+ }
}
- return string.contentEquals(sb);
+ return true;
}
public static boolean hasClinit(TypeDeclaration parent) {
diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java
index c32da68b..afbd7b52 100644
--- a/src/utils/lombok/javac/CommentCatcher.java
+++ b/src/utils/lombok/javac/CommentCatcher.java
@@ -95,8 +95,10 @@ public class CommentCatcher {
parserFactory = Class.forName("lombok.javac.java6.CommentCollectingParserFactory");
} else if (javaCompilerVersion == 7) {
parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory");
- } else {
+ } else if (javaCompilerVersion == 8) {
parserFactory = Class.forName("lombok.javac.java8.CommentCollectingParserFactory");
+ } else {
+ parserFactory = Class.forName("lombok.javac.java9.CommentCollectingParserFactory");
}
parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class).invoke(null, compiler, context);
} catch (InvocationTargetException e) {
diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java
index e207c44a..9ff4d22f 100644
--- a/src/utils/lombok/javac/Javac.java
+++ b/src/utils/lombok/javac/Javac.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 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 @@ import lombok.javac.JavacTreeMaker.TreeTag;
import lombok.javac.JavacTreeMaker.TypeTag;
import com.sun.tools.javac.code.Source;
+import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree;
@@ -62,8 +63,8 @@ public class Javac {
/** Matches any of the 8 primitive names, such as {@code boolean}. */
private static final Pattern PRIMITIVE_TYPE_NAME_PATTERN = Pattern.compile("^(boolean|byte|short|int|long|float|double|char)$");
- private static final Pattern VERSION_PARSER = Pattern.compile("^(\\d{1,6})\\.(\\d{1,6}).*$");
- private static final Pattern SOURCE_PARSER = Pattern.compile("^JDK(\\d{1,6})_(\\d{1,6}).*$");
+ private static final Pattern VERSION_PARSER = Pattern.compile("^(\\d{1,6})\\.?(\\d{1,6})?.*$");
+ private static final Pattern SOURCE_PARSER = Pattern.compile("^JDK(\\d{1,6})_?(\\d{1,6})?.*$");
private static final AtomicInteger compilerVersion = new AtomicInteger(-1);
@@ -78,11 +79,11 @@ public class Javac {
Matcher m = VERSION_PARSER.matcher(JavaCompiler.version());
if (m.matches()) {
int major = Integer.parseInt(m.group(1));
- int minor = Integer.parseInt(m.group(2));
if (major == 1) {
- compilerVersion.set(minor);
- return minor;
+ int minor = Integer.parseInt(m.group(2));
+ return setVersion(minor);
}
+ if (major >= 9) return setVersion(major);
}
}
@@ -91,16 +92,19 @@ public class Javac {
Matcher m = SOURCE_PARSER.matcher(name);
if (m.matches()) {
int major = Integer.parseInt(m.group(1));
- int minor = Integer.parseInt(m.group(2));
if (major == 1) {
- compilerVersion.set(minor);
- return minor;
+ int minor = Integer.parseInt(m.group(2));
+ return setVersion(minor);
}
+ if (major >= 9) return setVersion(major);
}
}
-
- compilerVersion.set(6);
- return 6;
+ return setVersion(6);
+ }
+
+ private static int setVersion(int version) {
+ compilerVersion.set(version);
+ return version;
}
private static final Class<?> DOCCOMMENTTABLE_CLASS;
@@ -162,9 +166,16 @@ public class Javac {
public static final TypeTag CTC_VOID = typeTag("VOID");
public static final TypeTag CTC_NONE = typeTag("NONE");
public static final TypeTag CTC_BOT = typeTag("BOT");
+ public static final TypeTag CTC_ERROR = typeTag("ERROR");
+ public static final TypeTag CTC_UNKNOWN = typeTag("UNKNOWN");
+ public static final TypeTag CTC_UNDETVAR = typeTag("UNDETVAR");
public static final TypeTag CTC_CLASS = typeTag("CLASS");
public static final TreeTag CTC_NOT_EQUAL = treeTag("NE");
+ public static final TreeTag CTC_LESS_THAN = treeTag("LT");
+ public static final TreeTag CTC_GREATER_THAN = treeTag("GT");
+ public static final TreeTag CTC_LESS_OR_EQUAL= treeTag("LE");
+ public static final TreeTag CTC_GREATER_OR_EQUAL = treeTag("GE");
public static final TreeTag CTC_POS = treeTag("POS");
public static final TreeTag CTC_NEG = treeTag("NEG");
public static final TreeTag CTC_NOT = treeTag("NOT");
@@ -172,10 +183,14 @@ public class Javac {
public static final TreeTag CTC_BITXOR = treeTag("BITXOR");
public static final TreeTag CTC_UNSIGNED_SHIFT_RIGHT = treeTag("USR");
public static final TreeTag CTC_MUL = treeTag("MUL");
+ public static final TreeTag CTC_DIV = treeTag("DIV");
public static final TreeTag CTC_PLUS = treeTag("PLUS");
+ public static final TreeTag CTC_MINUS = treeTag("MINUS");
public static final TreeTag CTC_EQUAL = treeTag("EQ");
public static final TreeTag CTC_PREINC = treeTag("PREINC");
public static final TreeTag CTC_PREDEC = treeTag("PREDEC");
+ public static final TreeTag CTC_POSTINC = treeTag("POSTINC");
+ public static final TreeTag CTC_POSTDEC = treeTag("POSTDEC");
private static final Method getExtendsClause, getEndPosition, storeEnd;
@@ -298,7 +313,21 @@ public class Javac {
JC_NO_TYPE = c;
}
- public static Type createVoidType(JavacTreeMaker maker, TypeTag tag) {
+ private static final Field symtabVoidType = getFieldIfExists(Symtab.class, "voidType");
+
+ private static Field getFieldIfExists(Class<?> c, String fieldName) {
+ try {
+ return c.getField("voidType");
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public static Type createVoidType(Symtab symbolTable, TypeTag tag) {
+ if (symtabVoidType != null) try {
+ return (Type) symtabVoidType.get(symbolTable);
+ } catch (IllegalAccessException ignore) {}
+
if (Javac.getJavaCompilerVersion() < 8) {
return new JCNoType(((Integer) tag.value).intValue());
} else {
diff --git a/src/utils/lombok/javac/JavacTreeMaker.java b/src/utils/lombok/javac/JavacTreeMaker.java
index 12baf5af..5f4fb09c 100644
--- a/src/utils/lombok/javac/JavacTreeMaker.java
+++ b/src/utils/lombok/javac/JavacTreeMaker.java
@@ -226,6 +226,7 @@ public class JavacTreeMaker {
}
public static TypeTag typeTag(Type t) {
+ if (t == null) return Javac.CTC_VOID;
try {
return new TypeTag(getFieldCached(FIELD_CACHE, t, "tag"));
} catch (NoSuchFieldException e) {
diff --git a/src/utils/lombok/javac/PackageName.java b/src/utils/lombok/javac/PackageName.java
new file mode 100644
index 00000000..e4dd6b20
--- /dev/null
+++ b/src/utils/lombok/javac/PackageName.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac;
+
+import java.lang.reflect.Method;
+
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+
+// Supports JDK6-9
+public class PackageName {
+ private static final Method packageNameMethod = getPackageNameMethod();
+
+ private static Method getPackageNameMethod() {
+ try {
+ return JCCompilationUnit.class.getDeclaredMethod("getPackageName");
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public static String getPackageName(JCCompilationUnit cu) {
+ JCTree t = getPackageNode(cu);
+ return t != null ? t.toString() : null;
+ }
+
+ public static JCTree getPackageNode(JCCompilationUnit cu) {
+ if (packageNameMethod != null) try {
+ Object pkg = packageNameMethod.invoke(cu);
+ return (pkg instanceof JCFieldAccess || pkg instanceof JCIdent) ? (JCTree) pkg : null;
+ } catch (Exception e) {}
+ return cu.pid instanceof JCFieldAccess || cu.pid instanceof JCIdent ? cu.pid : null;
+ }
+}
diff --git a/src/utils/lombok/javac/TreeMirrorMaker.java b/src/utils/lombok/javac/TreeMirrorMaker.java
index 23ec2406..8cd8cffe 100644
--- a/src/utils/lombok/javac/TreeMirrorMaker.java
+++ b/src/utils/lombok/javac/TreeMirrorMaker.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2011 The Project Lombok Authors.
+ * Copyright (C) 2010-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,11 +26,15 @@ import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
+import static lombok.javac.Javac.*;
+import lombok.javac.JavacTreeMaker.TypeTag;
+
import com.sun.source.tree.LabeledStatementTree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeCopier;
+import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
/**
@@ -46,7 +50,7 @@ import com.sun.tools.javac.util.List;
public class TreeMirrorMaker extends TreeCopier<Void> {
private final IdentityHashMap<JCTree, JCTree> originalToCopy = new IdentityHashMap<JCTree, JCTree>();
- public TreeMirrorMaker(JavacTreeMaker maker) {
+ public TreeMirrorMaker(JavacTreeMaker maker, Context context) {
super(maker.getUnderlyingTreeMaker());
}
@@ -86,15 +90,32 @@ public class TreeMirrorMaker extends TreeCopier<Void> {
return Collections.unmodifiableMap(originalToCopy);
}
- // Fix for NPE in HandleVal. See http://code.google.com/p/projectlombok/issues/detail?id=205
- // Maybe this should be done elsewhere...
+ // Monitor the following issues when making changes here.
+ // - https://github.com/rzwitserloot/lombok/issues/278
+ // - https://github.com/rzwitserloot/lombok/issues/729
@Override public JCTree visitVariable(VariableTree node, Void p) {
+ JCVariableDecl original = node instanceof JCVariableDecl ? (JCVariableDecl) node : null;
JCVariableDecl copy = (JCVariableDecl) super.visitVariable(node, p);
- copy.sym = ((JCVariableDecl) node).sym;
+ if (original == null) return copy;
+ copy.sym = original.sym;
+ if (copy.sym != null) copy.type = original.type;
+ if (copy.type != null) {
+ boolean wipeSymAndType = copy.type.isErroneous();
+ if (!wipeSymAndType) {
+ TypeTag typeTag = TypeTag.typeTag(copy.type);
+ wipeSymAndType = (CTC_NONE.equals(typeTag) || CTC_ERROR.equals(typeTag) || CTC_UNKNOWN.equals(typeTag) || CTC_UNDETVAR.equals(typeTag));
+ }
+
+ if (wipeSymAndType) {
+ copy.sym = null;
+ copy.type = null;
+ }
+ }
+
return copy;
}
- // Fix for NPE in HandleVal. See http://code.google.com/p/projectlombok/issues/detail?id=299
+ // Fix for NPE in HandleVal. See https://github.com/rzwitserloot/lombok/issues/372
// This and visitVariable is rather hacky but we're working around evident bugs or at least inconsistencies in javac.
@Override public JCTree visitLabeledStatement(LabeledStatementTree node, Void p) {
return node.getStatement().accept(this, p);
diff --git a/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java
index 45f865ad..2fdaddfe 100644
--- a/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java
+++ b/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java
@@ -52,6 +52,16 @@ public class CommentCollectingParserFactory extends ParserFactory {
//Either way this will work out.
}
+ public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) {
+ ScannerFactory scannerFactory = ScannerFactory.instance(context);
+ Lexer lexer = scannerFactory.newScanner(input, true);
+ Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, keepEndPos);
+ return (JavacParser) x;
+ // CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either
+ //javac6's EndPosParser which extends Parser, or javac8's JavacParser which implements Parser.
+ //Either way this will work out.
+ }
+
public static void setInCompiler(JavaCompiler compiler, Context context) {
context.put(CommentCollectingParserFactory.key(), (ParserFactory) null);
Field field;
diff --git a/src/utils/lombok/javac/java9/CommentCollectingParser.java b/src/utils/lombok/javac/java9/CommentCollectingParser.java
new file mode 100644
index 00000000..307be405
--- /dev/null
+++ b/src/utils/lombok/javac/java9/CommentCollectingParser.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.java9;
+
+import static lombok.javac.CommentCatcher.JCCompilationUnit_comments;
+
+import java.util.List;
+
+import lombok.javac.CommentInfo;
+import lombok.javac.java8.CommentCollectingScanner;
+
+import com.sun.tools.javac.parser.JavacParser;
+import com.sun.tools.javac.parser.Lexer;
+import com.sun.tools.javac.parser.ParserFactory;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+
+class CommentCollectingParser extends JavacParser {
+ private final Lexer lexer;
+
+ protected CommentCollectingParser(ParserFactory fac, Lexer S,
+ boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions, boolean parseModuleInfo) {
+ super(fac, S, keepDocComments, keepLineMap, keepEndPositions, parseModuleInfo);
+ lexer = S;
+ }
+
+ public JCCompilationUnit parseCompilationUnit() {
+ JCCompilationUnit result = super.parseCompilationUnit();
+ if (lexer instanceof CommentCollectingScanner) {
+ List<CommentInfo> comments = ((CommentCollectingScanner)lexer).getComments();
+ JCCompilationUnit_comments.set(result, comments);
+ }
+ return result;
+ }
+} \ No newline at end of file
diff --git a/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java
new file mode 100644
index 00000000..5af4a419
--- /dev/null
+++ b/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013-2017 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.java9;
+
+import java.lang.reflect.Field;
+
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.parser.JavacParser;
+import com.sun.tools.javac.parser.Lexer;
+import com.sun.tools.javac.parser.ParserFactory;
+import com.sun.tools.javac.parser.ScannerFactory;
+import com.sun.tools.javac.util.Context;
+
+public class CommentCollectingParserFactory extends ParserFactory {
+ private final Context context;
+
+ static Context.Key<ParserFactory> key() {
+ return parserFactoryKey;
+ }
+
+ protected CommentCollectingParserFactory(Context context) {
+ super(context);
+ this.context = context;
+ }
+
+ public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) {
+ return newParser(input, keepDocComments, keepEndPos, keepLineMap, false);
+ }
+
+ public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) {
+ ScannerFactory scannerFactory = ScannerFactory.instance(context);
+ Lexer lexer = scannerFactory.newScanner(input, true);
+ Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, keepEndPos, parseModuleInfo);
+ return (JavacParser) x;
+ // CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either
+ //javac6's EndPosParser which extends Parser, or javac-9's JavacParser which implements Parser.
+ //Either way this will work out.
+ }
+
+ public static void setInCompiler(JavaCompiler compiler, Context context) {
+ context.put(CommentCollectingParserFactory.key(), (ParserFactory) null);
+ Field field;
+ try {
+ field = JavaCompiler.class.getDeclaredField("parserFactory");
+ field.setAccessible(true);
+ field.set(compiler, new CommentCollectingParserFactory(context));
+ } catch (Exception e) {
+ throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/website/log4j.properties b/src/website/log4j.properties
new file mode 100644
index 00000000..9cafcc3b
--- /dev/null
+++ b/src/website/log4j.properties
@@ -0,0 +1,6 @@
+log4j.rootLogger=INFO, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
diff --git a/src/website/lombok/website/CompileChangelog.java b/src/website/lombok/website/CompileChangelog.java
new file mode 100644
index 00000000..8912434e
--- /dev/null
+++ b/src/website/lombok/website/CompileChangelog.java
@@ -0,0 +1,147 @@
+package lombok.website;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.petebevin.markdown.MarkdownProcessor;
+
+public class CompileChangelog {
+ public static void main(String[] args) {
+ String fileIn = args[0];
+ String fileOut = args[1];
+ boolean edge = args.length > 3 && "-edge".equals(args[2]);
+ boolean latest = args.length > 3 && "-latest".equals(args[2]);
+ String version = args.length > 3 ? args[3] : null;
+
+ try {
+ FileInputStream in = new FileInputStream(fileIn);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ byte[] b = new byte[65536];
+ while (true) {
+ int r = in.read(b);
+ if ( r == -1 ) break;
+ out.write(b, 0, r);
+ }
+ in.close();
+ String markdown = new String(out.toByteArray(), "UTF-8");
+
+ String result;
+ if (edge) {
+ result = buildEdge(sectionByVersion(markdown, version));
+ } else if (latest) {
+ result = buildLatest(sectionByVersion(markdown, version));
+ } else {
+ result = markdownToHtml(sectionStartingAt(markdown, version));
+ }
+
+ FileOutputStream file = new FileOutputStream(fileOut);
+ file.write(result.getBytes("UTF-8"));
+ file.close();
+ System.exit(0);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ public static String getHtmlForEdge(File root, String edgeVersion) throws IOException {
+ File f = new File(root, "doc/changelog.markdown");
+ String raw = readFile(f);
+ return buildEdge(sectionByVersion(raw, edgeVersion));
+ }
+
+ public static String getHtmlForLatest(File root, String latestVersion) throws IOException {
+ File f = new File(root, "doc/changelog.markdown");
+ String raw = readFile(f);
+ return buildLatest(sectionByVersion(raw, latestVersion));
+ }
+
+ public static String getHtml(File root) throws IOException {
+ File f = new File(root, "doc/changelog.markdown");
+ String raw = readFile(f);
+ return markdownToHtml(raw);
+ }
+
+ public static String getHtmlStartingAtSection(File root, String version) throws IOException {
+ File f = new File(root, "doc/changelog.markdown");
+ String raw = readFile(f);
+ return markdownToHtml(sectionStartingAt(raw, version));
+ }
+
+ private static String readFile(File f) throws IOException {
+ byte[] b = new byte[65536];
+ FileInputStream in = new FileInputStream(f);
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ while (true) {
+ int r = in.read(b);
+ if ( r == -1 ) break;
+ out.write(b, 0, r);
+ }
+ in.close();
+ return new String(out.toByteArray(), "UTF-8");
+ } finally {
+ in.close();
+ }
+ }
+
+ private static String markdownToHtml(String markdown) {
+ return new MarkdownProcessor().markdown(markdown);
+ }
+
+ private static String buildEdge(String section) {
+ String latest = section != null ? section : "* No changelog records for this edge release.";
+ return markdownToHtml(latest);
+ }
+
+ private static String buildLatest(String section) {
+ String latest = section != null ? section : "* No changelog records for this release.";
+ String noIssueLinks = latest.replaceAll("\\[[^]]*[Ii]ssue[^]]*\\]\\([^)]*\\)", "");
+ String noLinks = noIssueLinks.replaceAll("\\[([^]]*)\\]\\([^)]*\\)", "$1");
+ return markdownToHtml(noLinks);
+ }
+
+ private static String sectionStartingAt(String markdown, String version) {
+ if (version.toUpperCase().endsWith("-HEAD") || version.toUpperCase().endsWith("-EDGE")) {
+ version = version.substring(0, version.length() - 5);
+ }
+
+ Pattern p = Pattern.compile("^.*###\\s*v(.*)$");
+ BufferedReader br = new BufferedReader(new StringReader(markdown));
+ StringBuilder out = new StringBuilder();
+ int state = 0;
+ try {
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ if (state < 2) {
+ Matcher m = p.matcher(line);
+ if (m.matches()) state = m.group(1).startsWith(version) ? 2 : 1;
+ }
+ if (state != 1) {
+ out.append(line);
+ out.append("\n");
+ }
+ }
+ return out.toString();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static String sectionByVersion(String markdown, String version) {
+ if (version.toUpperCase().endsWith("-HEAD") || version.toUpperCase().endsWith("-EDGE")) {
+ version = version.substring(0, version.length() - 5);
+ }
+
+ Pattern p = Pattern.compile("(?is-m)^.*###\\s*v" + version + ".*?\n(.*?)(?:###\\s*v.*)?$");
+ Matcher m = p.matcher(markdown);
+ return m.matches() ? m.group(1) : null;
+ }
+} \ No newline at end of file
diff --git a/src/website/lombok/website/FetchCurrentVersion.java b/src/website/lombok/website/FetchCurrentVersion.java
new file mode 100644
index 00000000..0d789256
--- /dev/null
+++ b/src/website/lombok/website/FetchCurrentVersion.java
@@ -0,0 +1,33 @@
+package lombok.website;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class FetchCurrentVersion {
+ private FetchCurrentVersion() {}
+
+ private static final Pattern VERSION_PATTERN = Pattern.compile("^.*<\\s*span\\s+id\\s*=\\s*[\"'](currentVersion|currentVersionFull)[\"'](?:\\s+style\\s*=\\s*[\"']display\\s*:\\s*none;?[\"'])?\\s*>\\s*([^\t<]+)\\s*<\\s*/\\s*span\\s*>.*$");
+
+ public static void main(String[] args) throws IOException {
+ System.out.print(fetchVersionFromSite(args.length == 0 || args[0].equals("full")));
+ }
+
+ public static String fetchVersionFromSite(boolean fetchFull) throws IOException {
+ InputStream in = new URL("https://projectlombok.org/download").openStream();
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ Matcher m = VERSION_PATTERN.matcher(line);
+ if (m.matches() && m.group(1).equals("currentVersionFull") == fetchFull) return m.group(2).replace("&quot;", "\"");
+ }
+ throw new IOException("Expected a span with id 'currentVersion'");
+ } finally {
+ in.close();
+ }
+ }
+}
diff --git a/src/website/lombok/website/WebsiteMaker.java b/src/website/lombok/website/WebsiteMaker.java
new file mode 100644
index 00000000..88556b97
--- /dev/null
+++ b/src/website/lombok/website/WebsiteMaker.java
@@ -0,0 +1,403 @@
+package lombok.website;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+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.OutputStreamWriter;
+import java.io.Writer;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.java2html.Java2Html;
+import freemarker.cache.FileTemplateLoader;
+import freemarker.cache.TemplateLoader;
+import freemarker.core.HTMLOutputFormat;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateExceptionHandler;
+
+public class WebsiteMaker {
+ private final String version, fullVersion;
+ private final File baseDir, outputDir;
+
+ public WebsiteMaker(String version, String fullVersion, File baseDir, File outputDir) {
+ this.version = version;
+ this.fullVersion = fullVersion;
+ this.baseDir = baseDir;
+ this.outputDir = outputDir;
+ }
+
+ private static final class VersionFinder {
+ public static String getVersion() {
+ return getVersion0("getVersion");
+ }
+
+ public static String getFullVersion() {
+ return getVersion0("getFullVersion");
+ }
+
+ private static String getVersion0(String mName) {
+ try {
+ Class<?> c = Class.forName("lombok.core.Version");
+ Method m = c.getMethod(mName);
+ return (String) m.invoke(null);
+ } catch (ClassNotFoundException e) {
+ System.err.println("You need to specify the version string, and the full version string, as first 2 arguments.");
+ System.exit(1);
+ return null;
+ } catch (Exception e) {
+ if (e instanceof RuntimeException) throw (RuntimeException) e;
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private static void buildAll(String version, String fullVersion, String argIn, String argOut) throws Exception {
+ File in, out;
+ if (argIn == null) {
+ in = new File(".");
+ if (new File(in, "build.xml").isFile() && new File(in, "website").isDirectory()) in = new File(in, "website");
+ } else {
+ in = new File(argIn);
+ }
+
+ if (argOut == null) {
+ if (new File("./build.xml").isFile() && new File("./website").isDirectory() && new File("./build").isDirectory()) {
+ out = new File("./build/website");
+ } else {
+ out = new File(in, "output");
+ }
+ } else {
+ out = new File(argOut);
+ }
+ WebsiteMaker maker = new WebsiteMaker(version, fullVersion, in, out);
+ maker.buildWebsite();
+ }
+
+ private static void buildChangelog(String version, String fullVersion, String argIn, String argOut) throws Exception {
+ File in, out;
+ if (argIn == null) {
+ in = new File(".");
+ if (new File(in, "build.xml").isFile() && new File(in, "website").isDirectory()) in = new File(in, "website");
+ } else {
+ in = new File(argIn);
+ }
+
+ if (argOut == null) {
+ if (new File("./build.xml").isFile() && new File("./website").isDirectory() && new File("./build").isDirectory()) {
+ out = new File("./build/website/changelog.html");
+ } else {
+ out = new File(in, "output/changelog.html");
+ }
+ } else {
+ out = new File(argOut);
+ }
+ WebsiteMaker maker = new WebsiteMaker(version, fullVersion, in, out.getParentFile());
+ maker.buildChangelog(out);
+ }
+
+ private static void buildDownloadEdge(String version, String fullVersion, String argIn, String argOut) throws Exception {
+ File in, out;
+ if (argIn == null) {
+ in = new File(".");
+ if (new File(in, "build.xml").isFile() && new File(in, "website").isDirectory()) in = new File(in, "website");
+ } else {
+ in = new File(argIn);
+ }
+
+ if (argOut == null) {
+ if (new File("./build.xml").isFile() && new File("./website").isDirectory() && new File("./build").isDirectory()) {
+ out = new File("./build/website-edge/download-edge.html");
+ } else {
+ out = new File(in, "output/download-edge.html");
+ }
+ } else {
+ out = new File(argOut);
+ }
+ WebsiteMaker maker = new WebsiteMaker(version, fullVersion, in, out.getParentFile());
+ maker.buildDownloadEdge(out);
+ }
+
+ private static void buildChangelogLatest(String version, String fullVersion, String argIn, String argOut) throws Exception {
+ File in, out;
+ if (argIn == null) {
+ in = new File(".");
+ if (new File(in, "build.xml").isFile() && new File(in, "website").isDirectory()) in = new File(in, "website");
+ } else {
+ in = new File(argIn);
+ }
+
+ if (argOut == null) {
+ if (new File("./build.xml").isFile() && new File("./website").isDirectory() && new File("./build").isDirectory()) {
+ out = new File("./build/latestchanges.html");
+ } else {
+ out = new File(in, "output/latestchanges.html");
+ }
+ } else {
+ out = new File(argOut);
+ }
+ WebsiteMaker maker = new WebsiteMaker(version, fullVersion, in, out.getParentFile());
+ maker.buildChangelogLatest(out);
+ }
+
+ public static void main(String[] args) throws Exception {
+ String version, fullVersion;
+
+ if (args.length < 2) {
+ version = VersionFinder.getVersion();
+ fullVersion = VersionFinder.getFullVersion();
+ } else {
+ version = args[0];
+ fullVersion = args[1];
+ }
+
+ if (args.length < 3 || args[2].equalsIgnoreCase("all")) {
+ buildAll(version, fullVersion, args.length < 4 ? null : args[3], args.length < 5 ? null : args[4]);
+ } else if (args[2].equalsIgnoreCase("changelog")) {
+ buildChangelog(version, fullVersion, args.length < 4 ? null : args[3], args.length < 5 ? null : args[4]);
+ } else if (args[2].equalsIgnoreCase("download-edge")) {
+ buildDownloadEdge(version, fullVersion, args.length < 4 ? null : args[3], args.length < 5 ? null : args[4]);
+ } else if (args[2].equalsIgnoreCase("changelog-latest")) {
+ buildChangelogLatest(version, fullVersion, args.length < 4 ? null : args[3], args.length < 5 ? null : args[4]);
+ } else {
+ throw new IllegalArgumentException("3rd argument must be one of 'all', 'changelog', 'download-edge', 'changelog-latest'");
+ }
+ }
+
+ private Configuration makeFreemarkerConfig() throws IOException {
+ Configuration freemarkerConfig = new Configuration(Configuration.VERSION_2_3_25);
+ freemarkerConfig.setEncoding(Locale.ENGLISH, "UTF-8");
+ freemarkerConfig.setOutputEncoding("UTF-8");
+ freemarkerConfig.setOutputFormat(HTMLOutputFormat.INSTANCE);
+ freemarkerConfig.setTemplateLoader(createLoader());
+ freemarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+ return freemarkerConfig;
+ }
+
+ public void buildChangelog(File out) throws Exception {
+ Configuration freemarkerConfig = makeFreemarkerConfig();
+ outputDir.mkdirs();
+ convertChangelog(freemarkerConfig, out);
+ }
+
+ public void buildChangelogLatest(File out) throws Exception {
+ outputDir.mkdirs();
+ String htmlForLatest = CompileChangelog.getHtmlForLatest(baseDir.getParentFile(), version);
+ FileOutputStream fos = new FileOutputStream(out);
+ try {
+ BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"));
+ bw.write(htmlForLatest);
+ bw.close();
+ } finally {
+ fos.close();
+ }
+ }
+
+ public void buildDownloadEdge(File out) throws Exception {
+ Configuration freemarkerConfig = makeFreemarkerConfig();
+
+ outputDir.mkdirs();
+ convertDownloadEdge(freemarkerConfig, out);
+ }
+
+ public void buildHtAccess(File out) throws Exception {
+ Configuration freemarkerConfig = new Configuration(Configuration.VERSION_2_3_25);
+ freemarkerConfig.setEncoding(Locale.ENGLISH, "UTF-8");
+ freemarkerConfig.setOutputEncoding("UTF-8");
+ freemarkerConfig.setOutputFormat(HTMLOutputFormat.INSTANCE);
+ freemarkerConfig.setTemplateLoader(createLoader("extra"));
+ freemarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
+
+ outputDir.mkdirs();
+ convertHtAccess(freemarkerConfig, out);
+ }
+
+ public void buildWebsite() throws Exception {
+ Configuration freemarkerConfig = makeFreemarkerConfig();
+
+ outputDir.mkdirs();
+ convertTemplates(freemarkerConfig);
+ buildHtAccess(new File(outputDir, ".htaccess"));
+ }
+
+ private TemplateLoader createLoader() throws IOException {
+ return createLoader("templates");
+ }
+
+ private TemplateLoader createLoader(String base) throws IOException {
+ return new FileTemplateLoader(new File(baseDir, base));
+ }
+
+ private void convertHtAccess(Configuration freemarker, File outFile) throws Exception {
+ Map<String, Object> dataModel = new HashMap<String, Object>();
+ dataModel.put("setupPages", listHtmlNames(new File(outputDir, "setup")));
+ dataModel.put("featurePages", listHtmlNames(new File(outputDir, "features")));
+ dataModel.put("experimentalPages", listHtmlNames(new File(outputDir, "features/experimental")));
+ Template template = freemarker.getTemplate("htaccess");
+ FileOutputStream fileOut = new FileOutputStream(outFile);
+ try {
+ Writer wr = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8"));
+ template.process(dataModel, wr);
+ wr.close();
+ } finally {
+ fileOut.close();
+ }
+ }
+
+ private List<String> listHtmlNames(File dir) {
+ List<String> out = new ArrayList<String>();
+ for (String s : dir.list()) {
+ if (s.endsWith(".html") && !s.equals("index.html")) out.add(s.substring(0, s.length() - 5));
+ }
+ return out;
+ }
+
+ private void convertChangelog(Configuration freemarker, File outFile) throws Exception {
+ Map<String, Object> dataModel = createBasicDataModel();
+
+ Template template = freemarker.getTemplate("changelog.html");
+ FileOutputStream fileOut = new FileOutputStream(outFile);
+ try {
+ Writer wr = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8"));
+ template.process(dataModel, wr);
+ wr.close();
+ } finally {
+ fileOut.close();
+ }
+ }
+
+ private void convertDownloadEdge(Configuration freemarker, File outFile) throws Exception {
+ Map<String, Object> dataModel = createBasicDataModel();
+
+ Template template = freemarker.getTemplate("_download-edge.html");
+ FileOutputStream fileOut = new FileOutputStream(outFile);
+ try {
+ Writer wr = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8"));
+ template.process(dataModel, wr);
+ wr.close();
+ } finally {
+ fileOut.close();
+ }
+ }
+
+ private void convertTemplates(Configuration freemarker) throws Exception {
+ File basePagesLoc = new File(baseDir, "templates");
+ Map<String, Object> dataModel = createBasicDataModel();
+ dataModel.putAll(createExtendedDataModel());
+ convertTemplates_(freemarker, "", basePagesLoc, outputDir, 0, dataModel);
+ }
+
+ private void convertTemplates_(Configuration freemarker, String prefix, File from, File to, int depth, Map<String, Object> dataModel) throws Exception {
+ if (depth > 50) throw new IllegalArgumentException("50 levels is too deep: " + from);
+
+ for (File f : from.listFiles()) {
+ if (f.isDirectory()) convertTemplates_(freemarker, prefix + f.getName() + "/", f, new File(to, f.getName()), depth + 1, dataModel);
+ if (!f.isFile() || !f.getName().endsWith(".html") || f.getName().startsWith("_")) continue;
+ to.mkdirs();
+ Template template = freemarker.getTemplate(prefix + f.getName());
+ FileOutputStream fileOut = new FileOutputStream(new File(to, f.getName()));
+ try {
+ Writer wr = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8"));
+ template.process(dataModel, wr);
+ wr.close();
+ } finally {
+ fileOut.close();
+ }
+ }
+ }
+
+ private Map<String, Object> createBasicDataModel() throws IOException {
+ Map<String, Object> data = new HashMap<String, Object>();
+
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss 'UTC'");
+ sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+ String currentTime = sdf.format(new Date());
+
+ data.put("version", version);
+ data.put("fullVersion", fullVersion);
+ data.put("timestampString", currentTime);
+ data.put("year", "" + new GregorianCalendar().get(Calendar.YEAR));
+ data.put("changelog", CompileChangelog.getHtmlStartingAtSection(baseDir.getParentFile(), version));
+ data.put("changelogEdge", CompileChangelog.getHtmlForEdge(baseDir.getParentFile(), version));
+
+ return data;
+ }
+
+ private static final Pattern LOMBOK_LINK = Pattern.compile("^.*<a(?: (?:id|class|rel|rev|download|target|type)(?:=\"[^\"]*\")?)* href=\"(downloads/[^\"]+)\"(?: (?:id|class|rel|rev|download|target|type)(?:=\"[^\"]*\")?)*>([^<]+)</a>.*$");
+ private Map<String, Object> createExtendedDataModel() throws IOException {
+ Map<String, Object> data = new HashMap<String, Object>();
+
+ data.put("usages", new HtmlMaker(new File(baseDir, "usageExamples")));
+ InputStream in = new URL("https://projectlombok.org/all-versions.html").openStream();
+ ArrayList<List<String>> links = new ArrayList<List<String>>();
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+ for (String line = br.readLine(); line != null; line = br.readLine()) {
+ Matcher m = LOMBOK_LINK.matcher(line);
+ if (m.matches()) links.add(Arrays.asList(m.group(1), m.group(2)));
+ }
+ } finally {
+ in.close();
+ }
+
+ data.put("linksToVersions", links);
+
+ return data;
+ }
+
+ public static class HtmlMaker {
+ private final File usagesDir;
+
+ HtmlMaker(File usagesDir) {
+ this.usagesDir = usagesDir;
+ }
+
+ public String pre(String name) throws IOException {
+ return convert(new File(usagesDir, name + "Example_pre.jpage"));
+ }
+
+ public String post(String name) throws IOException {
+ return convert(new File(usagesDir, name + "Example_post.jpage"));
+ }
+
+ public String convert(File file) throws IOException {
+ String rawJava = readFully(file);
+ return Java2Html.convertToHtml(rawJava);
+ }
+ }
+
+ public static String readFully(File file) throws IOException {
+ FileInputStream fis = new FileInputStream(file);
+ try {
+ InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
+ StringBuilder out = new StringBuilder();
+ char[] b = new char[65536];
+ while (true) {
+ int r = isr.read(b);
+ if (r == -1) break;
+ out.append(b, 0, r);
+ }
+ return out.toString();
+ } finally {
+ fis.close();
+ }
+ }
+}