aboutsummaryrefslogtreecommitdiff
path: root/src/core/lombok
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/lombok')
-rw-r--r--src/core/lombok/AllArgsConstructor.java28
-rw-r--r--src/core/lombok/Builder.java39
-rw-r--r--src/core/lombok/Cleanup.java10
-rw-r--r--src/core/lombok/ConfigurationKeys.java105
-rw-r--r--src/core/lombok/Data.java6
-rw-r--r--src/core/lombok/Delegate.java6
-rw-r--r--src/core/lombok/EqualsAndHashCode.java56
-rw-r--r--src/core/lombok/Getter.java23
-rw-r--r--src/core/lombok/Lombok.java33
-rw-r--r--src/core/lombok/NoArgsConstructor.java19
-rw-r--r--src/core/lombok/RequiredArgsConstructor.java27
-rw-r--r--src/core/lombok/Setter.java24
-rw-r--r--src/core/lombok/Singular.java4
-rw-r--r--src/core/lombok/SneakyThrows.java9
-rw-r--r--src/core/lombok/Synchronized.java6
-rw-r--r--src/core/lombok/ToString.java47
-rw-r--r--src/core/lombok/Value.java6
-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/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/AnnotationProcessor.java66
-rw-r--r--src/core/lombok/core/AnnotationValues.java129
-rw-r--r--src/core/lombok/core/LombokInternalAliasing.java3
-rw-r--r--src/core/lombok/core/LombokNode.java12
-rw-r--r--src/core/lombok/core/PublicApiCreatorApp.java2
-rw-r--r--src/core/lombok/core/Version.java15
-rw-r--r--src/core/lombok/core/configuration/AllowHelper.java37
-rw-r--r--src/core/lombok/core/configuration/ConfigurationApp.java2
-rw-r--r--src/core/lombok/core/configuration/FlagUsageType.java2
-rw-r--r--src/core/lombok/core/configuration/NullCheckExceptionType.java6
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java43
-rw-r--r--src/core/lombok/core/handlers/InclusionExclusionUtils.java228
-rw-r--r--src/core/lombok/core/handlers/singulars.txt6
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java6
-rw-r--r--src/core/lombok/eclipse/EclipseNode.java71
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java282
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java65
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java168
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilderDefault.java48
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java124
-rw-r--r--src/core/lombok/eclipse/handlers/HandleData.java17
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java264
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldDefaults.java8
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java135
-rw-r--r--src/core/lombok/eclipse/handlers/HandleGetter.java24
-rw-r--r--src/core/lombok/eclipse/handlers/HandleLog.java36
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSetter.java72
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSuperBuilder.java836
-rw-r--r--src/core/lombok/eclipse/handlers/HandleToString.java142
-rw-r--r--src/core/lombok/eclipse/handlers/HandleUtilityClass.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleVal.java40
-rw-r--r--src/core/lombok/eclipse/handlers/HandleValue.java17
-rw-r--r--src/core/lombok/eclipse/handlers/HandleWither.java5
-rw-r--r--src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java5
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java39
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java34
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java11
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java43
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java8
-rw-r--r--src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java28
-rw-r--r--src/core/lombok/experimental/Accessors.java18
-rw-r--r--src/core/lombok/experimental/Builder.java140
-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/FieldNameConstants.java40
-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/SuperBuilder.java66
-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/flogger/Flogger.java (renamed from src/core/lombok/experimental/Value.java)56
-rw-r--r--src/core/lombok/extern/java/Log.java15
-rw-r--r--src/core/lombok/extern/jbosslog/JBossLog.java18
-rw-r--r--src/core/lombok/extern/log4j/Log4j.java15
-rw-r--r--src/core/lombok/extern/log4j/Log4j2.java15
-rw-r--r--src/core/lombok/extern/slf4j/Slf4j.java15
-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.java290
-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.java1
-rw-r--r--src/core/lombok/javac/JavacNode.java81
-rw-r--r--src/core/lombok/javac/JavacResolution.java23
-rw-r--r--src/core/lombok/javac/JavacTransformer.java9
-rw-r--r--src/core/lombok/javac/apt/EmptyLombokFileObject.java2
-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.java189
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java173
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilderDefault.java51
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java155
-rw-r--r--src/core/lombok/javac/handlers/HandleData.java20
-rw-r--r--src/core/lombok/javac/handlers/HandleDelegate.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java213
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldDefaults.java8
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldNameConstants.java137
-rw-r--r--src/core/lombok/javac/handlers/HandleGetter.java27
-rw-r--r--src/core/lombok/javac/handlers/HandleLog.java37
-rw-r--r--src/core/lombok/javac/handlers/HandleSetter.java67
-rw-r--r--src/core/lombok/javac/handlers/HandleSuperBuilder.java738
-rw-r--r--src/core/lombok/javac/handlers/HandleToString.java124
-rw-r--r--src/core/lombok/javac/handlers/HandleUtilityClass.java24
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java53
-rw-r--r--src/core/lombok/javac/handlers/HandleValue.java29
-rw-r--r--src/core/lombok/javac/handlers/HandleWither.java12
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java433
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java56
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java39
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java38
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java14
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java45
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java8
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java29
-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
128 files changed, 6130 insertions, 1756 deletions
diff --git a/src/core/lombok/AllArgsConstructor.java b/src/core/lombok/AllArgsConstructor.java
index 3037c9db..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="https://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/Builder.java b/src/core/lombok/Builder.java
index 7a965486..d7a2a109 100644
--- a/src/core/lombok/Builder.java
+++ b/src/core/lombok/Builder.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
@@ -48,15 +48,15 @@ import java.lang.annotation.Target;
* 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="https://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:
*
* <pre>
* &#064;Builder
- * class Example {
- * private int foo;
+ * class Example&lt;T&gt; {
+ * private T foo;
* private final String bar;
* }
* </pre>
@@ -103,14 +103,23 @@ import java.lang.annotation.Target;
* }
* }
* </pre>
+ *
+ * @see Singular
*/
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
public @interface Builder {
- /** Name of the 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 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";
/**
@@ -119,6 +128,8 @@ public @interface Builder {
* Default for {@code @Builder} on types and constructors: {@code (TypeName)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 "";
@@ -126,6 +137,8 @@ public @interface Builder {
* 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 toBuilder() default false;
@@ -142,13 +155,19 @@ public @interface Builder {
@Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface ObtainVia {
- /** Tells lombok to obtain a value with the expression {@code this.value}. */
+ /**
+ * @return Tells lombok to obtain a value with the expression {@code this.value}.
+ */
String field() default "";
- /** Tells lombok to obtain a value with the expression {@code this.method()}. */
+ /**
+ * @return Tells lombok to obtain a value with the expression {@code this.method()}.
+ */
String method() default "";
- /** Tells lombok to obtain a value with the expression {@code SelfType.method(this)}; requires {@code method} to be set. */
+ /**
+ * @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 c95c03c5..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="https://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 04decf69..f5134bbd 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
@@ -41,9 +41,32 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.addGeneratedAnnotation} = {@code true} | {@code false}.
*
- * If unset or {@code true}, lombok generates {@code @javax.annotation.Generated("lombok")} on all fields, methods, and types that are generated.
+ * 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_GENERATED_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.addGeneratedAnnotation", "Generate @javax.annotation.Generated on all generated code (default: true).") {};
+ 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}.
@@ -70,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.
@@ -88,6 +126,13 @@ public class ConfigurationKeys {
public static final ConfigurationKey<FlagUsageType> NO_ARGS_CONSTRUCTOR_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.noArgsConstructor.flagUsage", "Emit a warning or error if @NoArgsConstructor is used.") {};
/**
+ * lombok configuration: {@code lombok.noArgsConstructor.extraPrivate} = {@code true} | {@code false}.
+ *
+ * If {@code true} (default), @Data and @Value will also generate a private no-args constructor, if there isn't already one, setting all fields to their default values.
+ */
+ public static final ConfigurationKey<Boolean> NO_ARGS_CONSTRUCTOR_EXTRA_PRIVATE = new ConfigurationKey<Boolean>("lombok.noArgsConstructor.extraPrivate", "Generate a private no-ars constructor for @Data and @Value (default: true).") {};
+
+ /**
* lombok configuration: {@code lombok.requiredArgsConstructor.flagUsage} = {@code WARNING} | {@code ERROR}.
*
* If set, <em>any</em> usage of {@code @RequiredArgsConstructor} results in a warning / error.
@@ -242,7 +287,7 @@ 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}.
*/
@@ -283,7 +328,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 -----
@@ -344,6 +390,13 @@ public class ConfigurationKeys {
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.flogger.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @Flogger} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> LOG_FLOGGER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.flogger.flagUsage", "Emit a warning or error if @Flogger 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.
@@ -440,6 +493,17 @@ public class ConfigurationKeys {
*/
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 -----
/**
@@ -449,6 +513,28 @@ public class ConfigurationKeys {
*/
public static final ConfigurationKey<FlagUsageType> UTILITY_CLASS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.utilityClass.flagUsage", "Emit a warning or error if @UtilityClass is used.") {};
+ // ----- FieldNameConstants -----
+ /**
+ * lombok configuration: {@code lombok.fieldNameConstants.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @FieldNameConstants} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> FIELD_NAME_CONSTANTS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.fieldNameConstants.flagUsage", "Emit a warning or error if @FieldNameConstants is used.") {};
+
+ /**
+ * lombok configuration: {@code lombok.fieldNameConstants.prefix} = &lt;String: aJavaIdentifierPrefix&gt; (Default: {@code PREFIX_}).
+ *
+ * The names of the constants generated by {@code @FieldNameConstants} will be prefixed with this value.
+ */
+ public static final ConfigurationKey<String> FIELD_NAME_CONSTANTS_PREFIX = new ConfigurationKey<String>("lombok.fieldNameConstants.prefix", "names of constants generated by @FieldNameConstants will be prefixed with this value. (default: 'PREFIX_').") {};
+
+ /**
+ * lombok configuration: {@code lombok.fieldNameConstants.suffix} = &lt;String: aJavaIdentifierPrefix&gt; (Default: nothing).
+ *
+ * The names of the constants generated by {@code @FieldNameConstants} will be suffixed with this value.
+ */
+ public static final ConfigurationKey<String> FIELD_NAME_CONSTANTS_SUFFIX = new ConfigurationKey<String>("lombok.fieldNameConstants.suffix", "names of constants generated by @FieldNameConstants will be suffixed with this value. (default: nothing).") {};
+
// ----- Wither -----
/**
@@ -458,6 +544,15 @@ 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.") {};
+ // ----- SuperBuilder -----
+
+ /**
+ * lombok configuration: {@code lombok.superBuilder.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @SuperBuilder} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> SUPERBUILDER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.superBuilder.flagUsage", "Emit a warning or error if @SuperBuilder is used.") {};
+
// ----- Configuration System -----
/**
diff --git a/src/core/lombok/Data.java b/src/core/lombok/Data.java
index 4c6c2e5d..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="https://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 605815ed..b2fc672d 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-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,15 +29,18 @@ 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="https://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()}.
+ * <p>
+ * Will soon be marked {@code @Deprecated}; use the {@code @EqualsAndHashCode.Exclude} annotation instead.
+ *
+ * @return A list of fields to exclude.
*/
String[] exclude() default {};
@@ -46,25 +49,39 @@ public @interface EqualsAndHashCode {
* Normally, all non-static, non-transient fields are used for identity.
* <p>
* Mutually exclusive with {@link #exclude()}.
+ * <p>
+ * Will soon be marked {@code @Deprecated}; use the {@code @EqualsAndHashCode.Include} annotation together with {@code @EqualsAndHashCode(onlyExplicitlyIncluded = true)}.
+ *
+ * @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 {};
@@ -76,4 +93,27 @@ public @interface EqualsAndHashCode {
@Retention(RetentionPolicy.SOURCE)
@Target({})
@interface AnyAnnotation {}
+
+ /**
+ * Only include fields and methods explicitly marked with {@code @EqualsAndHashCode.Include}.
+ * Normally, all (non-static, non-transient) fields are included by default.
+ */
+ boolean onlyExplicitlyIncluded() default false;
+
+ /**
+ * If present, do not include this field in the generated {@code equals} and {@code hashCode} methods.
+ */
+ @Target(ElementType.FIELD)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Exclude {}
+
+ /**
+ * Configure the behaviour of how this member is treated in the {@code equals} and {@code hashCode} implementation; if on a method, include the method's return value as part of calculating hashCode/equality.
+ */
+ @Target({ElementType.FIELD, ElementType.METHOD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Include {
+ /** Defaults to the method name of the annotated member. If on a method and the name equals the name of a default-included field, this member takes its place. */
+ String replaces() default "";
+ }
}
diff --git a/src/core/lombok/Getter.java b/src/core/lombok/Getter.java
index 906ae60f..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="https://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 ff437bba..672cd1c2 100644
--- a/src/core/lombok/NoArgsConstructor.java
+++ b/src/core/lombok/NoArgsConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2015 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="https://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,22 +47,35 @@ 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;
diff --git a/src/core/lombok/RequiredArgsConstructor.java b/src/core/lombok/RequiredArgsConstructor.java
index 7a9da3f9..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="https://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 ba9e9759..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="https://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
index 15dec4a5..67edab96 100644
--- a/src/core/lombok/Singular.java
+++ b/src/core/lombok/Singular.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * 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
@@ -29,10 +29,10 @@ import java.lang.annotation.Target;
/**
* The singular annotation is used together with {@code @Builder} to create single element 'add' methods in the builder for collections.
- * <p>
*/
@Target({FIELD, PARAMETER})
@Retention(SOURCE)
public @interface Singular {
+ /** @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 489e13e4..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="https://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 9a39c212..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="https://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 b9926148..ae3f071f 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-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,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="https://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,18 @@ 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()}.
+ * <p>
+ * Will soon be marked {@code @Deprecated}; use the {@code @ToString.Exclude} annotation instead.
+ *
+ * @return A list of fields to exclude.
*/
String[] exclude() default {};
@@ -51,18 +57,55 @@ public @interface ToString {
* Normally, all non-static fields are printed.
* <p>
* Mutually exclusive with {@link #exclude()}.
+ * <p>
+ * Will soon be marked {@code @Deprecated}; use the {@code @ToString.Include} annotation together with {@code @ToString(onlyExplicitlyIncluded = true)}.
+ *
+ * @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;
+
+ /**
+ * Only include fields and methods explicitly marked with {@code @ToString.Include}.
+ * Normally, all (non-static) fields are included by default.
+ */
+ boolean onlyExplicitlyIncluded() default false;
+
+ /**
+ * If present, do not include this field in the generated {@code toString}.
+ */
+ @Target(ElementType.FIELD)
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Exclude {}
+
+ /**
+ * Configure the behaviour of how this member is rendered in the {@code toString}; if on a method, include the method's return value in the output.
+ */
+ @Target({ElementType.FIELD, ElementType.METHOD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Include {
+// /** If true and the return value is {@code null}, omit this member entirely from the {@code toString} output. */
+// boolean skipNull() default false; // -- We'll add it later, it requires a complete rework on the toString code we generate.
+
+ /** Higher ranks are printed first. Members of the same rank are printed in the order they appear in the source file. */
+ int rank() default 0;
+
+ /** Defaults to the field / method name of the annotated member. If the name equals the name of a default-included field, this member takes its place. */
+ String name() default "";
+ }
}
diff --git a/src/core/lombok/Value.java b/src/core/lombok/Value.java
index 39154226..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
@@ -31,7 +31,7 @@ import java.lang.annotation.Target;
* <p>
* Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @AllArgsConstructor @ToString @EqualsAndHashCode}.
* <p>
- * Complete documentation is found at <a href="https://projectlombok.org/features/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
@@ -53,6 +53,8 @@ public @interface Value {
* </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/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/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java
index ba5fd4f7..04448ecb 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
@@ -26,6 +26,7 @@ import static lombok.core.Augments.ClassLoader_lombokAlreadyAddedTo;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
@@ -40,6 +41,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;
@@ -47,6 +49,7 @@ import lombok.patcher.ClassRootFinder;
@SupportedAnnotationTypes("*")
public class AnnotationProcessor extends AbstractProcessor {
+
private static String trace(Throwable t) {
StringWriter w = new StringWriter();
t.printStackTrace(new PrintWriter(w, true));
@@ -62,6 +65,42 @@ public class AnnotationProcessor extends AbstractProcessor {
private final List<ProcessorDescriptor> registered = Arrays.asList(new JavacDescriptor(), new EcjDescriptor());
private final List<ProcessorDescriptor> active = new ArrayList<ProcessorDescriptor>();
private final List<String> delayedWarnings = new ArrayList<String>();
+
+ /**
+ * This method is a simplified version of {@link lombok.javac.apt.LombokProcessor.getJavacProcessingEnvironment}
+ * It simply returns the processing environment, but in case of gradle incremental compilation,
+ * the delegate ProcessingEnvironment of the gradle wrapper is returned.
+ */
+ public static ProcessingEnvironment getJavacProcessingEnvironment(ProcessingEnvironment procEnv, List<String> delayedWarnings) {
+ ProcessingEnvironment javacProcEnv = tryRecursivelyObtainJavacProcessingEnvironment(procEnv);
+
+ if (javacProcEnv == null) {
+ delayedWarnings.add("Can't get the delegate of the gradle IncrementalProcessingEnvironment.");
+ }
+
+ return javacProcEnv;
+ }
+
+ private static ProcessingEnvironment tryRecursivelyObtainJavacProcessingEnvironment(ProcessingEnvironment procEnv) {
+ if (procEnv.getClass().getName().equals("com.sun.tools.javac.processing.JavacProcessingEnvironment")) {
+ return procEnv;
+ }
+
+ for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) {
+ try {
+ Field field = procEnvClass.getDeclaredField("delegate");
+ field.setAccessible(true);
+ Object delegate = field.get(procEnv);
+
+ return tryRecursivelyObtainJavacProcessingEnvironment((ProcessingEnvironment) delegate);
+ } catch (final Exception e) {
+ // no valid delegate, try superclass
+ }
+ }
+
+ return null;
+ }
+
static class JavacDescriptor extends ProcessorDescriptor {
private Processor processor;
@@ -71,10 +110,12 @@ public class AnnotationProcessor extends AbstractProcessor {
}
@Override boolean want(ProcessingEnvironment procEnv, List<String> delayedWarnings) {
- if (!procEnv.getClass().getName().equals("com.sun.tools.javac.processing.JavacProcessingEnvironment")) return false;
-
+ ProcessingEnvironment javacProcEnv = getJavacProcessingEnvironment(procEnv, delayedWarnings);
+
+ if (javacProcEnv == null) return false;
+
try {
- ClassLoader classLoader = findAndPatchClassLoader(procEnv);
+ ClassLoader classLoader = findAndPatchClassLoader(javacProcEnv);
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.LombokProcessor is not available. Lombok will not run during this compilation: " + trace(e));
@@ -103,7 +144,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 +204,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/AnnotationValues.java b/src/core/lombok/core/AnnotationValues.java
index d04797cb..a24330fa 100644
--- a/src/core/lombok/core/AnnotationValues.java
+++ b/src/core/lombok/core/AnnotationValues.java
@@ -27,6 +27,7 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -42,7 +43,7 @@ public class AnnotationValues<A extends Annotation> {
private final Class<A> type;
private final Map<String, AnnotationValue> values;
private final LombokNode<?, ?, ?> ast;
-
+
/**
* Represents a single method on the annotation class. For example, the value() method on the Getter annotation.
*/
@@ -50,8 +51,7 @@ public class AnnotationValues<A extends Annotation> {
/** A list of the raw expressions. List is size 1 unless an array is provided. */
public final List<String> raws;
- /** Guesses for each raw expression. If the raw expression is a literal expression, the guess will
- * likely be right. If not, it'll be wrong. */
+ /** Guesses for each raw expression. It's 'primitive' (String or primitive), an AV.ClassLiteral, an AV.FieldSelect, or an array of one of those. */
public final List<Object> valueGuesses;
/** A list of the actual expressions. List is size 1 unless an array is provided. */
@@ -160,6 +160,62 @@ public class AnnotationValues<A extends Annotation> {
private A cachedInstance = null;
+ public List<String> getAsStringList(String methodName) {
+ AnnotationValue v = values.get(methodName);
+
+ if (v == null) {
+ String[] s = getDefaultIf(methodName, String[].class, new String[0]);
+ return Collections.unmodifiableList(Arrays.asList(s));
+ }
+
+ List<String> out = new ArrayList<String>(v.valueGuesses.size());
+ int idx = 0;
+ for (Object guess : v.valueGuesses) {
+ Object result = guess == null ? null : guessToType(guess, String.class, v, idx);
+ if (result == null) {
+ if (v.valueGuesses.size() == 1) {
+ String[] s = getDefaultIf(methodName, String[].class, new String[0]);
+ return Collections.unmodifiableList(Arrays.asList(s));
+ }
+ throw new AnnotationValueDecodeFail(v,
+ "I can't make sense of this annotation value. Try using a fully qualified literal.", idx);
+ }
+ out.add((String) result);
+ }
+
+ return Collections.unmodifiableList(out);
+ }
+
+ public String getAsString(String methodName) {
+ AnnotationValue v = values.get(methodName);
+ if (v == null || v.valueGuesses.size() != 1) {
+ return getDefaultIf(methodName, String.class, "");
+ }
+
+ Object guess = guessToType(v.valueGuesses.get(0), String.class, v, 0);
+ if (guess instanceof String) return (String) guess;
+ return getDefaultIf(methodName, String.class, "");
+ }
+
+ public boolean getAsBoolean(String methodName) {
+ AnnotationValue v = values.get(methodName);
+ if (v == null || v.valueGuesses.size() != 1) {
+ return getDefaultIf(methodName, boolean.class, false);
+ }
+
+ Object guess = guessToType(v.valueGuesses.get(0), boolean.class, v, 0);
+ if (guess instanceof Boolean) return ((Boolean) guess).booleanValue();
+ return getDefaultIf(methodName, boolean.class, false);
+ }
+
+ public <T> T getDefaultIf(String methodName, Class<T> type, T defaultValue) {
+ try {
+ return type.cast(type.getMethod(methodName).getDefaultValue());
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
/**
* Creates an actual annotation instance. You can use this to query any annotation methods, except for
* those annotation methods with class literals, as those can most likely not be turned into Class objects.
@@ -190,7 +246,7 @@ public class AnnotationValues<A extends Annotation> {
if (!isArray && v.valueGuesses.size() > 1) {
throw new AnnotationValueDecodeFail(v,
- "Expected a single value, but " + method.getName() + " has an array of values", -1);
+ "Expected a single value, but " + method.getName() + " has an array of values", -1);
}
if (v.valueGuesses.size() == 0 && !isArray) {
@@ -217,7 +273,7 @@ public class AnnotationValues<A extends Annotation> {
return defaultValue;
}
throw new AnnotationValueDecodeFail(v,
- "I can't make sense of this annotation value. Try using a fully qualified literal.", idx);
+ "I can't make sense of this annotation value. Try using a fully qualified literal.", idx);
}
Array.set(array, idx++, result);
}
@@ -230,48 +286,48 @@ public class AnnotationValues<A extends Annotation> {
}
private Object guessToType(Object guess, Class<?> expected, AnnotationValue v, int pos) {
- if (expected == int.class) {
+ if (expected == int.class || expected == Integer.class) {
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
- return ((Number)guess).intValue();
+ return ((Number) guess).intValue();
}
}
- if (expected == long.class) {
+ if (expected == long.class || expected == Long.class) {
if (guess instanceof Long || guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
- return ((Number)guess).longValue();
+ return ((Number) guess).longValue();
}
}
- if (expected == short.class) {
+ if (expected == short.class || expected == Short.class) {
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
- int intVal = ((Number)guess).intValue();
- int shortVal = ((Number)guess).shortValue();
+ int intVal = ((Number) guess).intValue();
+ int shortVal = ((Number) guess).shortValue();
if (shortVal == intVal) return shortVal;
}
}
- if (expected == byte.class) {
+ if (expected == byte.class || expected == Byte.class) {
if (guess instanceof Integer || guess instanceof Short || guess instanceof Byte) {
- int intVal = ((Number)guess).intValue();
- int byteVal = ((Number)guess).byteValue();
+ int intVal = ((Number) guess).intValue();
+ int byteVal = ((Number) guess).byteValue();
if (byteVal == intVal) return byteVal;
}
}
- if (expected == double.class) {
- if (guess instanceof Number) return ((Number)guess).doubleValue();
+ if (expected == double.class || expected == Double.class) {
+ if (guess instanceof Number) return ((Number) guess).doubleValue();
}
- if (expected == float.class) {
- if (guess instanceof Number) return ((Number)guess).floatValue();
+ if (expected == float.class || expected == Float.class) {
+ if (guess instanceof Number) return ((Number) guess).floatValue();
}
- if (expected == boolean.class) {
- if (guess instanceof Boolean) return ((Boolean)guess).booleanValue();
+ if (expected == boolean.class || expected == Boolean.class) {
+ if (guess instanceof Boolean) return ((Boolean) guess).booleanValue();
}
- if (expected == char.class) {
- if (guess instanceof Character) return ((Character)guess).charValue();
+ if (expected == char.class || expected == Character.class) {
+ if (guess instanceof Character) return ((Character) guess).charValue();
}
if (expected == String.class) {
@@ -279,27 +335,36 @@ public class AnnotationValues<A extends Annotation> {
}
if (Enum.class.isAssignableFrom(expected) ) {
- if (guess instanceof String) {
+ if (guess instanceof FieldSelect) {
+ String fieldSel = ((FieldSelect) guess).getFinalPart();
for (Object enumConstant : expected.getEnumConstants()) {
- String target = ((Enum<?>)enumConstant).name();
- if (target.equals(guess)) return enumConstant;
+ String target = ((Enum<?>) enumConstant).name();
+ if (target.equals(fieldSel)) return enumConstant;
}
throw new AnnotationValueDecodeFail(v,
- "Can't translate " + guess + " to an enum of type " + expected, pos);
+ "Can't translate " + fieldSel + " to an enum of type " + expected, pos);
}
}
- if (Class.class == expected) {
- if (guess instanceof String) try {
- return Class.forName(toFQ((String)guess));
+ if (expected == Class.class) {
+ if (guess instanceof ClassLiteral) try {
+ String classLit = ((ClassLiteral) guess).getClassName();
+ return Class.forName(toFQ(classLit));
} catch (ClassNotFoundException e) {
throw new AnnotationValueDecodeFail(v,
- "Can't translate " + guess + " to a class object.", pos);
+ "Can't translate " + guess + " to a class object.", pos);
}
}
+ if (guess instanceof AnnotationValues) {
+ return ((AnnotationValues<?>) guess).getInstance();
+ }
+
+ if (guess instanceof FieldSelect) throw new AnnotationValueDecodeFail(v,
+ "You must use constant literals in lombok annotations; they cannot be references to (static) fields.", pos);
+
throw new AnnotationValueDecodeFail(v,
- "Can't translate a " + guess.getClass() + " to the expected " + expected, pos);
+ "Can't translate a " + guess.getClass() + " to the expected " + expected, pos);
}
/**
diff --git a/src/core/lombok/core/LombokInternalAliasing.java b/src/core/lombok/core/LombokInternalAliasing.java
index 08764a5c..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
@@ -51,6 +51,7 @@ public class LombokInternalAliasing {
Map<String, String> m2 = new HashMap<String, String>();
m2.put("lombok.experimental.Value", "lombok.Value");
m2.put("lombok.experimental.Builder", "lombok.Builder");
+ m2.put("lombok.experimental.var", "lombok.var");
m2.put("lombok.Delegate", "lombok.experimental.Delegate");
ALIASES = Collections.unmodifiableMap(m2);
}
diff --git a/src/core/lombok/core/LombokNode.java b/src/core/lombok/core/LombokNode.java
index 07c62151..d6708956 100644
--- a/src/core/lombok/core/LombokNode.java
+++ b/src/core/lombok/core/LombokNode.java
@@ -21,6 +21,7 @@
*/
package lombok.core;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -275,4 +276,15 @@ public abstract class LombokNode<A extends AST<A, L, N>, L extends LombokNode<A,
public boolean isStructurallySignificant() {
return isStructurallySignificant;
}
+
+ public abstract boolean hasAnnotation(Class<? extends Annotation> type);
+ public abstract <Z extends Annotation> AnnotationValues<Z> findAnnotation(Class<Z> type);
+
+ public abstract boolean isStatic();
+ public abstract boolean isTransient();
+ public abstract boolean isEnumMember();
+
+ public abstract int countMethodParameters();
+
+ public abstract int getStartPos();
}
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/Version.java b/src/core/lombok/core/Version.java
index ed245c08..70af48e0 100644
--- a/src/core/lombok/core/Version.java
+++ b/src/core/lombok/core/Version.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2016 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,16 @@ 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.16.9";
+ private static final String VERSION = "1.18.1";
private static final String RELEASE_NAME = "Edgy Guinea Pig";
-// private static final String RELEASE_NAME = "Candid Duck";
+// private static final String RELEASE_NAME = "Envious Ferret";
+
+ // Named version history:
+ // Angry Butterfly
+ // Branching Cobra
+ // Candid Duck
+ // Dancing Elephant
+ // Envious Ferret
private Version() {
//Prevent instantiation
@@ -43,7 +50,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/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/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/configuration/NullCheckExceptionType.java b/src/core/lombok/core/configuration/NullCheckExceptionType.java
index 18a332fd..c4bb71f2 100644
--- a/src/core/lombok/core/configuration/NullCheckExceptionType.java
+++ b/src/core/lombok/core/configuration/NullCheckExceptionType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Project Lombok Authors.
+ * 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
@@ -26,7 +26,7 @@ package lombok.core.configuration;
public enum NullCheckExceptionType {
ILLEGAL_ARGUMENT_EXCEPTION {
public String toExceptionMessage(String fieldName) {
- return fieldName + " is null";
+ return fieldName + " is marked @NonNull but is null";
}
@Override public String getExceptionType() {
@@ -35,7 +35,7 @@ public enum NullCheckExceptionType {
},
NULL_POINTER_EXCEPTION {
@Override public String toExceptionMessage(String fieldName) {
- return fieldName;
+ return fieldName + " is marked @NonNull but is null";
}
public String getExceptionType() {
diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
index a9a4be78..0d64c550 100644
--- a/src/core/lombok/core/handlers/HandlerUtil.java
+++ b/src/core/lombok/core/handlers/HandlerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2015 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;
@@ -56,6 +57,10 @@ import lombok.experimental.Wither;
public class HandlerUtil {
private HandlerUtil() {}
+ public enum FieldAccess {
+ GETTER, PREFER_FIELD, ALWAYS_FIELD;
+ }
+
public static int primeForHashcode() {
return 59;
}
@@ -97,13 +102,24 @@ public class HandlerUtil {
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");
}
@@ -159,11 +175,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.
@@ -428,4 +445,16 @@ public class HandlerUtil {
}
return String.format("%s%s", prefix, suffix);
}
+
+ public static String camelCaseToConstant(String fieldName) {
+ if (fieldName == null || fieldName.isEmpty()) return "";
+ StringBuilder b = new StringBuilder();
+ b.append(Character.toUpperCase(fieldName.charAt(0)));
+ for (int i = 1; i < fieldName.length(); i++) {
+ char c = fieldName.charAt(i);
+ if (Character.isUpperCase(c)) b.append('_');
+ b.append(Character.toUpperCase(c));
+ }
+ return b.toString();
+ }
}
diff --git a/src/core/lombok/core/handlers/InclusionExclusionUtils.java b/src/core/lombok/core/handlers/InclusionExclusionUtils.java
new file mode 100644
index 00000000..368b51fc
--- /dev/null
+++ b/src/core/lombok/core/handlers/InclusionExclusionUtils.java
@@ -0,0 +1,228 @@
+/*
+ * 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.core.handlers;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.core.AST;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.LombokNode;
+
+public class InclusionExclusionUtils {
+ private static List<Integer> createListOfNonExistentFields(List<String> list, LombokNode<?, ?, ?> type, boolean excludeStandard, boolean excludeTransient) {
+ boolean[] matched = new boolean[list.size()];
+
+ for (LombokNode<?, ?, ?> child : type.down()) {
+ if (list.isEmpty()) break;
+ if (child.getKind() != Kind.FIELD) continue;
+ if (excludeStandard) {
+ if (child.isStatic()) continue;
+ if (child.getName().startsWith("$")) continue;
+ }
+ if (excludeTransient && child.isTransient()) continue;
+
+ int idx = list.indexOf(child.getName());
+ if (idx > -1) matched[idx] = true;
+ }
+
+ List<Integer> problematic = new ArrayList<Integer>();
+ for (int i = 0 ; i < list.size() ; i++) if (!matched[i]) problematic.add(i);
+
+ return problematic;
+ }
+
+ public static void checkForBogusFieldNames(LombokNode<?, ?, ?> type, AnnotationValues<?> annotation, List<String> excludes, List<String> includes) {
+ if (excludes != null && !excludes.isEmpty()) {
+ for (int i : createListOfNonExistentFields(excludes, type, true, false)) {
+ if (annotation != null) annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
+ }
+ }
+
+ if (includes != null && !includes.isEmpty()) {
+ for (int i : createListOfNonExistentFields(includes, type, false, false)) {
+ if (annotation != null) annotation.setWarning("of", "This field does not exist.", i);
+ }
+ }
+ }
+
+ public static class Included<L, I extends Annotation> {
+ private final L node;
+ private final I inc;
+ private final boolean defaultInclude;
+
+ public Included(L node, I inc, boolean defaultInclude) {
+ this.node = node;
+ this.inc = inc;
+ this.defaultInclude = defaultInclude;
+ }
+
+ public L getNode() {
+ return node;
+ }
+
+ public I getInc() {
+ return inc;
+ }
+
+ public boolean isDefaultInclude() {
+ return defaultInclude;
+ }
+ }
+
+ private static String innerAnnName(Class<? extends Annotation> type) {
+ String name = type.getSimpleName();
+ Class<?> c = type.getEnclosingClass();
+ while (c != null) {
+ name = c.getSimpleName() + "." + name;
+ c = c.getEnclosingClass();
+ }
+ return name;
+ }
+
+ public static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N, I extends Annotation> List<Included<L, I>> handleIncludeExcludeMarking(Class<I> inclType, String replaceName, Class<? extends Annotation> exclType, LombokNode<A, L, N> typeNode, AnnotationValues<?> annotation, LombokNode<A, L, N> annotationNode, boolean includeTransient) {
+ List<String> oldExcludes = (annotation != null && annotation.isExplicit("exclude")) ? annotation.getAsStringList("exclude") : null;
+ List<String> oldIncludes = (annotation != null && annotation.isExplicit("of")) ? annotation.getAsStringList("of") : null;
+
+ boolean onlyExplicitlyIncluded = annotation != null ? annotation.getAsBoolean("onlyExplicitlyIncluded") : false;
+ boolean memberAnnotationMode = onlyExplicitlyIncluded;
+ List<Included<L, I>> members = new ArrayList<Included<L, I>>();
+ List<String> namesToAutoExclude = new ArrayList<String>();
+
+ if (typeNode == null || typeNode.getKind() != Kind.TYPE) return null;
+
+ checkForBogusFieldNames(typeNode, annotation, oldExcludes, oldIncludes);
+ String inclTypeName = innerAnnName(inclType);
+ String exclTypeName = innerAnnName(exclType);
+
+ if (oldExcludes != null && oldIncludes != null) {
+ oldExcludes = null;
+ if (annotation != null) annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
+ }
+
+ for (L child : typeNode.down()) {
+ boolean markExclude = child.getKind() == Kind.FIELD && child.hasAnnotation(exclType);
+ AnnotationValues<I> markInclude = null;
+ if (child.getKind() == Kind.FIELD || child.getKind() == Kind.METHOD) markInclude = child.findAnnotation(inclType);
+
+ if (markExclude || markInclude != null) memberAnnotationMode = true;
+
+ if (markInclude != null && markExclude) {
+ child.addError("@" + exclTypeName + " and @" + inclTypeName + " are mutually exclusive; the @Include annotation will be ignored");
+ markInclude = null;
+ }
+
+ String name = child.getName();
+
+ if (markExclude) {
+ if (onlyExplicitlyIncluded) {
+ child.addWarning("The @Exclude annotation is not needed; 'onlyExplicitlyIncluded' is set, so this member would be excluded anyway");
+ } else if (child.isStatic()) {
+ child.addWarning("The @Exclude annotation is not needed; static fields aren't included anyway");
+ } else if (name.startsWith("$")) {
+ child.addWarning("The @Exclude annotation is not needed; fields that start with $ aren't included anyway");
+ }
+ continue;
+ }
+
+ if (oldExcludes != null && oldExcludes.contains(name)) continue;
+
+ if (markInclude != null) {
+ I inc = markInclude.getInstance();
+ if (child.getKind() == Kind.METHOD) {
+ if (child.countMethodParameters() > 0) {
+ child.addError("Methods included with @" + inclTypeName + " must have no arguments; it will not be included");
+ continue;
+ }
+ String n = replaceName != null ? markInclude.getAsString(replaceName) : "";
+ if (n.isEmpty()) n = name;
+ namesToAutoExclude.add(n);
+ }
+ members.add(new Included<L, I>(child, inc, false));
+ continue;
+ }
+
+ if (onlyExplicitlyIncluded) continue;
+ if (oldIncludes != null) {
+ if (child.getKind() == Kind.FIELD && oldIncludes.contains(name)) members.add(new Included<L, I>(child, null, false));
+ continue;
+ }
+ if (child.getKind() != Kind.FIELD) continue;
+ if (child.isStatic()) continue;
+ if (child.isTransient() && !includeTransient) continue;
+ if (name.startsWith("$")) continue;
+ if (child.isEnumMember()) continue;
+ members.add(new Included<L, I>(child, null, true));
+ }
+
+ /* delete default-included fields with the same name as an explicit inclusion */ {
+ Iterator<Included<L, I>> it = members.iterator();
+ while (it.hasNext()) {
+ Included<L, I> m = it.next();
+ if (m.isDefaultInclude() && namesToAutoExclude.contains(m.getNode().getName())) it.remove();
+ }
+ }
+
+ if (annotation == null || !annotation.isExplicit("exclude")) oldExcludes = null;
+ if (annotation == null || !annotation.isExplicit("of")) oldIncludes = null;
+
+ if (memberAnnotationMode && (oldExcludes != null || oldIncludes != null)) {
+ annotationNode.addError("The old-style 'exclude/of' parameter cannot be used together with the new-style @Include / @Exclude annotations.");
+ return null;
+ }
+
+ return members;
+ }
+
+ public static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> List<Included<L, ToString.Include>> handleToStringMarking(LombokNode<A, L, N> typeNode, AnnotationValues<ToString> annotation, LombokNode<A, L, N> annotationNode) {
+ List<Included<L, ToString.Include>> members = handleIncludeExcludeMarking(ToString.Include.class, "name", ToString.Exclude.class, typeNode, annotation, annotationNode, true);
+
+ Collections.sort(members, new Comparator<Included<L, ToString.Include>>() {
+ @Override public int compare(Included<L, ToString.Include> a, Included<L, ToString.Include> b) {
+ int ra = a.getInc() == null ? 0 : a.getInc().rank();
+ int rb = b.getInc() == null ? 0 : b.getInc().rank();
+ if (ra < rb) return +1;
+ if (ra > rb) return -1;
+
+ int pa = a.getNode().getStartPos();
+ int pb = b.getNode().getStartPos();
+
+ if (pa < pb) return -1;
+ if (pa > pb) return +1;
+
+ return 0;
+ }
+ });
+ return members;
+ }
+
+ public static <A extends AST<A, L, N>, L extends LombokNode<A, L, N>, N> List<Included<L, EqualsAndHashCode.Include>> handleEqualsAndHashCodeMarking(LombokNode<A, L, N> typeNode, AnnotationValues<EqualsAndHashCode> annotation, LombokNode<A, L, N> annotationNode) {
+ return handleIncludeExcludeMarking(EqualsAndHashCode.Include.class, "replaces", EqualsAndHashCode.Exclude.class, typeNode, annotation, annotationNode, false);
+ }
+}
diff --git a/src/core/lombok/core/handlers/singulars.txt b/src/core/lombok/core/handlers/singulars.txt
index 9976c76c..d77308cb 100644
--- a/src/core/lombok/core/handlers/singulars.txt
+++ b/src/core/lombok/core/handlers/singulars.txt
@@ -19,7 +19,7 @@ Mice = mouse
Lice = louse
News = !
# We could add more detail (axemen, boatsmen, boogymen, cavemen, gentlemen, etc, but (A) there's stuff like 'cerumen', and (B) the 'men' ending is common in singulars and other languages.)
-# Therefore, the odds of a mistake are too high, so other than these 2 well known cases, so force the explicit singular.
+# 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
@@ -48,7 +48,9 @@ hives = hive
-shes = sh
-lves = lf
-rves = rf
--ves = fe
+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 6741b33a..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);
@@ -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/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java
index 49867e62..4db1d38d 100644
--- a/src/core/lombok/eclipse/EclipseNode.java
+++ b/src/core/lombok/eclipse/EclipseNode.java
@@ -23,7 +23,9 @@ package lombok.eclipse;
import java.util.List;
+import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
+import lombok.eclipse.handlers.EclipseHandlerUtil;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
@@ -36,6 +38,7 @@ import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
/**
* Eclipse specific version of the LombokNode class.
@@ -184,4 +187,72 @@ public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode,
public boolean isCompleteParse() {
return ast.isCompleteParse();
}
+
+ @Override public boolean hasAnnotation(Class<? extends java.lang.annotation.Annotation> type) {
+ return EclipseHandlerUtil.hasAnnotation(type, this);
+ }
+
+ @Override public <Z extends java.lang.annotation.Annotation> AnnotationValues<Z> findAnnotation(Class<Z> type) {
+ EclipseNode annotation = EclipseHandlerUtil.findAnnotation(type, this);
+ if (annotation == null) return null;
+ return EclipseHandlerUtil.createAnnotation(type, annotation);
+ }
+
+ private Integer getModifiers() {
+ if (node instanceof TypeDeclaration) return ((TypeDeclaration) node).modifiers;
+ if (node instanceof FieldDeclaration) return ((FieldDeclaration) node).modifiers;
+ if (node instanceof LocalDeclaration) return ((LocalDeclaration) node).modifiers;
+ if (node instanceof AbstractMethodDeclaration) return ((AbstractMethodDeclaration) node).modifiers;
+
+ return null;
+ }
+
+ @Override public boolean isStatic() {
+ if (node instanceof TypeDeclaration) {
+ EclipseNode directUp = directUp();
+ if (directUp == null || directUp.getKind() == Kind.COMPILATION_UNIT) return true;
+ if (!(directUp.get() instanceof TypeDeclaration)) return false;
+ TypeDeclaration p = (TypeDeclaration) directUp.get();
+ int f = p.modifiers;
+ if ((ClassFileConstants.AccInterface & f) != 0) return true;
+ if ((ClassFileConstants.AccEnum & f) != 0) return true;
+ }
+
+ if (node instanceof FieldDeclaration) {
+ EclipseNode directUp = directUp();
+ if (directUp != null && directUp.get() instanceof TypeDeclaration) {
+ TypeDeclaration p = (TypeDeclaration) directUp.get();
+ int f = p.modifiers;
+ if ((ClassFileConstants.AccInterface & f) != 0) return true;
+ }
+ }
+
+ Integer i = getModifiers();
+ if (i == null) return false;
+ int f = i.intValue();
+ return (ClassFileConstants.AccStatic & f) != 0;
+ }
+
+ @Override public boolean isTransient() {
+ if (getKind() != Kind.FIELD) return false;
+ Integer i = getModifiers();
+ return i != null && (i.intValue() & ClassFileConstants.AccTransient) != 0;
+ }
+
+ @Override public boolean isEnumMember() {
+ if (getKind() != Kind.FIELD) return false;
+ return ((FieldDeclaration) node).getKind() == 3;
+ }
+
+ @Override public int countMethodParameters() {
+ if (getKind() != Kind.METHOD) return 0;
+
+ Argument[] a = ((AbstractMethodDeclaration) node).arguments;
+ if (a == null) return 0;
+ return a.length;
+ }
+
+ @Override public int getStartPos() {
+ return node.sourceStart;
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 0ff5a7f6..2dce285c 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -167,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;
@@ -196,17 +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()) {
if (child.getKind() != Kind.ANNOTATION) continue;
- for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) {
+ 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));
}
}
}
@@ -443,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.
*
@@ -453,6 +509,16 @@ public class EclipseHandlerUtil {
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);
}
@@ -680,7 +746,7 @@ public class EclipseHandlerUtil {
* Provides AnnotationValues with the data it needs to do its thing.
*/
public static <A extends java.lang.annotation.Annotation> AnnotationValues<A>
- createAnnotation(Class<A> type, final EclipseNode annotationNode) {
+ createAnnotation(Class<A> type, final EclipseNode annotationNode) {
final Annotation annotation = (Annotation) annotationNode.get();
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
@@ -697,7 +763,7 @@ public class EclipseHandlerUtil {
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;
+ expressions = ((ArrayInitializer) rhs).expressions;
} else if (rhs != null) {
expressions = new Expression[] { rhs };
}
@@ -831,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();
@@ -854,10 +920,6 @@ public class EclipseHandlerUtil {
return null;
}
- public enum FieldAccess {
- GETTER, PREFER_FIELD, ALWAYS_FIELD;
- }
-
static boolean lookForGetter(EclipseNode field, FieldAccess fieldAccess) {
if (fieldAccess == FieldAccess.GETTER) return true;
if (fieldAccess == FieldAccess.ALWAYS_FIELD) return false;
@@ -874,11 +936,13 @@ public class EclipseHandlerUtil {
}
static TypeReference getFieldType(EclipseNode field, FieldAccess fieldAccess) {
+ if (field.get() instanceof MethodDeclaration) return ((MethodDeclaration) field.get()).returnType;
+
boolean lookForGetter = lookForGetter(field, fieldAccess);
GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
- return ((FieldDeclaration)field.get()).type;
+ return ((FieldDeclaration) field.get()).type;
}
return getter.type;
@@ -886,7 +950,7 @@ public class EclipseHandlerUtil {
static Expression createFieldAccessor(EclipseNode field, FieldAccess fieldAccess, ASTNode source) {
int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
boolean lookForGetter = lookForGetter(field, fieldAccess);
@@ -954,6 +1018,43 @@ public class EclipseHandlerUtil {
return call;
}
+ static Expression createMethodAccessor(EclipseNode method, ASTNode source) {
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration methodDecl = (MethodDeclaration) method.get();
+ MessageSend call = new MessageSend();
+ setGeneratedBy(call, source);
+ call.sourceStart = pS; call.statementEnd = call.sourceEnd = pE;
+ if ((methodDecl.modifiers & ClassFileConstants.AccStatic) == 0) {
+ call.receiver = new ThisReference(pS, pE);
+ setGeneratedBy(call.receiver, source);
+ } else {
+ EclipseNode containerNode = method.up();
+ if (containerNode != null && containerNode.get() instanceof TypeDeclaration) {
+ call.receiver = new SingleNameReference(((TypeDeclaration) containerNode.get()).name, p);
+ setGeneratedBy(call.receiver, source);
+ }
+ }
+
+ call.selector = methodDecl.selector;
+ return call;
+ }
+
+ static Expression createMethodAccessor(EclipseNode method, ASTNode source, char[] receiver) {
+ int pS = source == null ? 0 : source.sourceStart, pE = source == null ? 0 : source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration methodDecl = (MethodDeclaration) method.get();
+ MessageSend call = new MessageSend();
+ setGeneratedBy(call, source);
+ call.sourceStart = pS; call.statementEnd = call.sourceEnd = pE;
+ call.receiver = new SingleNameReference(receiver, p);
+ setGeneratedBy(call.receiver, source);
+ call.selector = methodDecl.selector;
+ return call;
+ }
+
/** Serves as return value for the methods that check for the existence of fields and methods. */
public enum MemberExistsResult {
NOT_EXISTS, EXISTS_BY_LOMBOK, EXISTS_BY_USER;
@@ -1031,7 +1132,7 @@ public class EclipseHandlerUtil {
public static boolean filterField(FieldDeclaration declaration, boolean skipStatic) {
// Skip the fake fields that represent enum constants.
if (declaration.initialization instanceof AllocationExpression &&
- ((AllocationExpression)declaration.initialization).enumConstant != null) return false;
+ ((AllocationExpression) declaration.initialization).enumConstant != null) return false;
if (declaration.type == null) return false;
@@ -1097,6 +1198,12 @@ public class EclipseHandlerUtil {
return AnnotationValues.of(Accessors.class, field);
}
+
+ public static EclipseNode upToTypeNode(EclipseNode node) {
+ if (node == null) throw new NullPointerException("node");
+ while (node != null && !(node.get() instanceof TypeDeclaration)) node = node.up();
+ return node;
+ }
/**
* Checks if there is a field with the provided name.
@@ -1105,10 +1212,7 @@ public class EclipseHandlerUtil {
* @param node Any node that represents the Type (TypeDeclaration) to look in, or any child node thereof.
*/
public static MemberExistsResult fieldExists(String fieldName, EclipseNode node) {
- while (node != null && !(node.get() instanceof TypeDeclaration)) {
- node = node.up();
- }
-
+ node = upToTypeNode(node);
if (node != null && node.get() instanceof TypeDeclaration) {
TypeDeclaration typeDecl = (TypeDeclaration)node.get();
if (typeDecl.fields != null) for (FieldDeclaration def : typeDecl.fields) {
@@ -1168,9 +1272,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;
}
@@ -1181,6 +1284,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.
@@ -1188,19 +1298,14 @@ public class EclipseHandlerUtil {
* @param node Any node that represents the Type (TypeDeclaration) to look in, or any child node thereof.
*/
public static MemberExistsResult constructorExists(EclipseNode node) {
- while (node != null && !(node.get() instanceof TypeDeclaration)) {
- node = node.up();
- }
-
+ node = upToTypeNode(node);
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;
}
@@ -1325,6 +1430,7 @@ public class EclipseHandlerUtil {
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) {
@@ -1339,24 +1445,29 @@ public class EclipseHandlerUtil {
}
public static Annotation[] addGenerated(EclipseNode node, ASTNode source, Annotation[] originalAnnotationArray) {
- if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_GENERATED_ANNOTATIONS))) return originalAnnotationArray;
- return addAnnotation(source, originalAnnotationArray, JAVAX_ANNOTATION_GENERATED, new StringLiteral(LOMBOK, 0, 0, 0));
+ 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;
}
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 && Arrays.equals(simpleName, lastToken)) 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;
@@ -1606,6 +1717,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
@@ -1628,51 +1747,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();
}
@@ -1684,13 +1822,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();
}
}
@@ -1716,4 +1854,12 @@ public class EclipseHandlerUtil {
private static long[] copy(long[] array) {
return array == null ? null : array.clone();
}
+
+ public static boolean isDirectDescendantOfObject(EclipseNode typeNode) {
+ if (!(typeNode.get() instanceof TypeDeclaration)) throw new IllegalArgumentException("not a type node");
+ TypeDeclaration typeDecl = (TypeDeclaration) typeNode.get();
+ if (typeDecl.superclass == null) return true;
+ String p = typeDecl.superclass.toString();
+ return p.equals("Object") || p.equals("java.lang.Object");
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
index 4cb41d4f..b9b9e07d 100644
--- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
+++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -45,6 +45,9 @@ 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.Reference;
+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;
@@ -53,6 +56,7 @@ 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 org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import lombok.core.LombokImmutableList;
import lombok.core.SpiLoadUtil;
@@ -60,6 +64,14 @@ import lombok.core.TypeLibrary;
import lombok.eclipse.EclipseNode;
public class EclipseSingularsRecipes {
+ public interface TypeReferenceMaker {
+ TypeReference make();
+ }
+
+ public interface StatementMaker {
+ Statement make();
+ }
+
private static final EclipseSingularsRecipes INSTANCE = new EclipseSingularsRecipes();
private final Map<String, EclipseSingularizer> singularizers = new HashMap<String, EclipseSingularizer>();
private final TypeLibrary singularizableTypes = new TypeLibrary();
@@ -132,6 +144,10 @@ public class EclipseSingularsRecipes {
}
}
+ public ASTNode getSource() {
+ return source;
+ }
+
public EclipseNode getAnnotation() {
return annotation;
}
@@ -211,8 +227,36 @@ public class EclipseSingularsRecipes {
}
public abstract List<EclipseNode> generateFields(SingularData data, EclipseNode builderType);
- public abstract void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain);
- public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName);
+
+ /**
+ * Generates the singular, plural, and clear methods for the given {@link SingularData}.
+ * Uses the given {@code builderType} as return type if {@code chain == true}, {@code void} otherwise.
+ * If you need more control over the return type and value, use
+ * {@link #generateMethods(SingularData, boolean, EclipseNode, boolean, TypeReferenceMaker, StatementMaker)}.
+ */
+ public void generateMethods(SingularData data, boolean deprecate, final EclipseNode builderType, boolean fluent, final boolean chain) {
+ TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() {
+ @Override public TypeReference make() {
+ return chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
+ }
+ };
+
+ StatementMaker returnStatementMaker = new StatementMaker() {
+ @Override public ReturnStatement make() {
+ return chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
+ }
+ };
+
+ generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
+ }
+
+ /**
+ * Generates the singular, plural, and clear methods for the given {@link SingularData}.
+ * Uses the given {@code returnTypeMaker} and {@code returnStatementMaker} for the generated methods.
+ */
+ public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker);
+
+ public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable);
public boolean requiresCleaning() {
try {
@@ -303,16 +347,16 @@ public class EclipseSingularsRecipes {
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) {
+ protected Expression getSize(EclipseNode builderType, char[] name, boolean nullGuard, String builderVariable) {
MessageSend invoke = new MessageSend();
- ThisReference thisRef = new ThisReference(0, 0);
+ Reference thisRef = getBuilderReference(builderVariable);
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);
+ Reference cdnThisRef = getBuilderReference(builderVariable);
FieldReference cdnThisDotName = new FieldReference(name, 0L);
cdnThisDotName.receiver = cdnThisRef;
NullLiteral nullLiteral = new NullLiteral(0, 0);
@@ -341,5 +385,14 @@ public class EclipseSingularsRecipes {
return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS);
}
+
+ /** @return a {@code SingleNameReference} to the builder in the variable <code>builderVariable</code>. If {@ code builderVariable == "this"}, a {@code ThisReference} is returned. */
+ protected static Reference getBuilderReference(String builderVariable) {
+ if ("this".equals(builderVariable)) {
+ return new ThisReference(0, 0);
+ } else {
+ return new SingleNameReference(builderVariable.toCharArray(), 0);
+ }
+ }
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 4e0c4218..e1b1af26 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2015 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
@@ -38,6 +38,7 @@ 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;
@@ -73,8 +74,10 @@ import lombok.Builder;
import lombok.Builder.ObtainVia;
import lombok.ConfigurationKeys;
import lombok.Singular;
+import lombok.ToString;
import lombok.core.AST.Kind;
import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.eclipse.Eclipse;
@@ -88,6 +91,8 @@ import lombok.experimental.NonFinal;
@ProviderFor(EclipseAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
+ private HandleConstructor handleConstructor = new HandleConstructor();
+
private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray();
private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray();
@@ -102,9 +107,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
TypeReference type;
char[] rawName;
char[] name;
+ char[] nameOfDefaultProvider;
+ char[] nameOfSetFlag;
SingularData singularData;
ObtainVia obtainVia;
EclipseNode obtainViaNode;
+ EclipseNode originalFieldNode;
List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
}
@@ -127,6 +135,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
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;
@@ -171,25 +189,47 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
TypeDeclaration td = (TypeDeclaration) tdParent.get();
List<EclipseNode> allFields = new ArrayList<EclipseNode>();
- @SuppressWarnings("deprecation")
- boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent));
- for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) {
+ 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;
+ 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, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, null,
+ handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER,
Collections.<Annotation>emptyList(), annotationNode);
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
@@ -334,6 +374,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
bfd.name = arg.name;
bfd.type = arg.type;
bfd.singularData = getSingularData(param, ast);
+ bfd.originalFieldNode = param;
addObtainVia(bfd, param);
builderFields.add(bfd);
}
@@ -395,7 +436,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
ConstructorDeclaration cd = HandleConstructor.createConstructor(
- AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false, null,
+ AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false,
annotationNode, Collections.<Annotation>emptyList());
if (cd != null) injectMethod(builderType, cd);
}
@@ -405,14 +446,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast);
+ 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>();
+ List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>();
for (BuilderFieldData bfd : builderFields) {
- fieldNodes.addAll(bfd.createdFields);
+ for (EclipseNode f : bfd.createdFields) {
+ fieldNodes.add(new Included<EclipseNode, ToString.Include>(f, null, true));
+ }
}
MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, false, ast, FieldAccess.ALWAYS_FIELD);
if (md != null) injectMethod(builderType, md);
@@ -514,7 +557,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return decl;
}
- public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) {
+ 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;
List<Statement> statements = new ArrayList<Statement>();
@@ -530,13 +573,27 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
for (BuilderFieldData bfd : builderFields) {
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
- bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name);
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name, "this");
}
}
List<Expression> args = new ArrayList<Expression>();
for (BuilderFieldData bfd : builderFields) {
- args.add(new SingleNameReference(bfd.name, 0L));
+ 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));
+ }
}
if (addCleaning) {
@@ -563,14 +620,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0);
else
invoke.receiver = new QualifiedThisReference(new SingleTypeReference(type.up().getName().toCharArray(), 0) , 0, 0);
- TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters;
- if (tps != null) {
- TypeReference[] trs = new TypeReference[tps.length];
- for (int i = 0; i < trs.length; i++) {
- trs[i] = new SingleTypeReference(tps[i].name, 0);
- }
- invoke.typeArguments = trs;
- }
+
+ 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)) {
statements.add(invoke);
@@ -583,6 +634,33 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return out;
}
+ private TypeReference[] typeParameterNames(TypeParameter[] typeParameters) {
+ if (typeParameters == null) return null;
+
+ 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 static 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), ((TypeDeclaration) fieldNode.up().get()).scope);
+ return out;
+ }
+
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;
@@ -608,25 +686,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (child.getKind() == Kind.FIELD) existing.add(child);
}
- top:
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)) {
- bfd.createdFields.add(exists);
- continue top;
- }
+ if (Arrays.equals(n, bfd.name)) field = exists;
+ if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) setFlag = exists;
}
- FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0);
- fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
- fd.modifiers = ClassFileConstants.AccPrivate;
- fd.type = copyType(bfd.type);
- fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null);
- bfd.createdFields.add(injectFieldAndMarkGenerated(builderType, fd));
+ 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);
}
}
}
@@ -634,14 +721,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
private static final AbstractMethodDeclaration[] EMPTY = {};
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, bfd.createdFields.get(0), sourceNode, fluent, chain);
+ makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain);
} else {
- bfd.singularData.getSingularizer().generateMethods(bfd.singularData, builderType, fluent, chain);
+ bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, fluent, chain);
}
}
- private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, EclipseNode sourceNode, boolean fluent, boolean 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;
@@ -652,12 +740,12 @@ 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;
+ if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) return;
}
String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
- MethodDeclaration setter = HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
+ MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, chain, ClassFileConstants.AccPublic,
sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
injectMethod(builderType, setter);
}
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
new file mode 100644
index 00000000..d0c597fd
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+import lombok.experimental.SuperBuilder;
+
+@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)
+ && !hasAnnotation(SuperBuilder.class, classWithAnnotatedField)) {
+ annotationNode.addWarning("@Builder.Default requires @Builder or @SuperBuilder 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 85d1d4ed..cab847e6 100644
--- a/src/core/lombok/eclipse/handlers/HandleConstructor.java
+++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2015 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
@@ -42,12 +42,15 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult;
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.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;
@@ -79,6 +82,8 @@ 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");
@@ -92,14 +97,16 @@ public class HandleConstructor {
boolean force = ann.force();
List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList();
- List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode);
+ List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode);
- new HandleConstructor().generateConstructor(typeNode, level, fields, force, 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");
@@ -109,18 +116,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", "@RequiredArgsConstructor(onConstructor=", annotationNode);
+ List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor", annotationNode);
- new HandleConstructor().generateConstructor(
+ handleConstructor.generateConstructor(
typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO,
- suppressConstructorProperties, onConstructor, annotationNode);
+ onConstructor, annotationNode);
}
}
@@ -146,14 +150,17 @@ public class HandleConstructor {
}
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);
}
@@ -162,6 +169,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");
@@ -171,18 +180,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(
+ handleConstructor.generateConstructor(
typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO,
- suppressConstructorProperties, onConstructor, annotationNode);
+ onConstructor, annotationNode);
}
}
@@ -200,28 +206,45 @@ public class HandleConstructor {
return true;
}
+ public enum SkipIfConstructorExists {
+ YES, NO, I_AM_BUILDER;
+ }
+
+ public void generateExtraNoArgsConstructor(EclipseNode typeNode, EclipseNode sourceNode) {
+ if (!isDirectDescendantOfObject(typeNode)) return;
+
+ Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.NO_ARGS_CONSTRUCTOR_EXTRA_PRIVATE);
+ if (v == null || !v) return;
+
+ List<EclipseNode> fields = findFinalFields(typeNode);
+ generate(typeNode, AccessLevel.PRIVATE, fields, true, null, SkipIfConstructorExists.NO, Collections.<Annotation>emptyList(), sourceNode, true);
+ }
+
public void generateRequiredArgsConstructor(
EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode) {
- generateConstructor(typeNode, level, findRequiredFields(typeNode), false, 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), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
- }
-
- public enum SkipIfConstructorExists {
- YES, NO, I_AM_BUILDER;
+ generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, onConstructor, sourceNode);
}
public void generateConstructor(
EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
- Boolean suppressConstructorProperties, List<Annotation> onConstructor, EclipseNode sourceNode) {
+ List<Annotation> onConstructor, EclipseNode sourceNode) {
+ generate(typeNode, level, fields, allToDefault, staticName, skipIfConstructorExists, onConstructor, sourceNode, false);
+ }
+
+ public void generate(
+ EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
+ List<Annotation> onConstructor, EclipseNode sourceNode, boolean noArgs) {
+
ASTNode source = sourceNode.get();
boolean staticConstrRequired = staticName != null && !staticName.equals("");
@@ -253,9 +276,11 @@ public class HandleConstructor {
}
}
+ if (noArgs && noArgsConstructorExists(typeNode)) return;
+
ConstructorDeclaration constr = createConstructor(
staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault,
- suppressConstructorProperties, sourceNode, onConstructor);
+ sourceNode, onConstructor);
injectMethod(typeNode, constr);
if (staticConstrRequired) {
MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.<EclipseNode>emptyList() : fields, source);
@@ -263,6 +288,28 @@ public class HandleConstructor {
}
}
+ private static boolean noArgsConstructorExists(EclipseNode node) {
+ node = EclipseHandlerUtil.upToTypeNode(node);
+
+ if (node != null && node.get() instanceof TypeDeclaration) {
+ TypeDeclaration typeDecl = (TypeDeclaration)node.get();
+ if (typeDecl.methods != null) for (AbstractMethodDeclaration def : typeDecl.methods) {
+ if (def instanceof ConstructorDeclaration) {
+ Argument[] arguments = ((ConstructorDeclaration) def).arguments;
+ if (arguments == null || arguments.length == 0) return true;
+ }
+ }
+ }
+
+ for (EclipseNode child : node.down()) {
+ if (annotationTypeMatches(NoArgsConstructor.class, child)) return true;
+ if (annotationTypeMatches(RequiredArgsConstructor.class, child) && findRequiredFields(node).isEmpty()) return true;
+ if (annotationTypeMatches(AllArgsConstructor.class, child) && findAllFields(node).isEmpty()) return true;
+ }
+
+ return false;
+ }
+
private static final char[][] JAVA_BEANS_CONSTRUCTORPROPERTIES = new char[][] { "java".toCharArray(), "beans".toCharArray(), "ConstructorProperties".toCharArray() };
public static Annotation[] createConstructorProperties(ASTNode source, Collection<EclipseNode> fields) {
if (fields.isEmpty()) return null;
@@ -295,9 +342,9 @@ public class HandleConstructor {
return new Annotation[] { ann };
}
- public static ConstructorDeclaration createConstructor(
+ @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor(
AccessLevel level, EclipseNode type, Collection<EclipseNode> fields, boolean allToDefault,
- Boolean suppressConstructorProperties, EclipseNode sourceNode, List<Annotation> onConstructor) {
+ EclipseNode sourceNode, List<Annotation> onConstructor) {
ASTNode source = sourceNode.get();
TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get());
@@ -307,12 +354,13 @@ public class HandleConstructor {
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);
@@ -353,7 +401,7 @@ public class HandleConstructor {
Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
if (nonNulls.length != 0) {
- Statement nullCheck = generateNullCheck(field, sourceNode);
+ Statement nullCheck = generateNullCheck(parameter, sourceNode);
if (nullCheck != null) nullChecks.add(nullCheck);
}
parameter.annotations = copyAnnotations(source, nonNulls, nullables);
@@ -367,7 +415,7 @@ public class HandleConstructor {
/* Generate annotations that must be put on the generated method, and attach them. */ {
Annotation[] constructorProperties = null;
- if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(type)) {
+ if (!allToDefault && addConstructorProperties && !isLocalType(type)) {
constructorProperties = createConstructorProperties(source, fields);
}
@@ -381,6 +429,8 @@ public class HandleConstructor {
}
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);
diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java
index 0ff65a47..4011890d 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,12 +72,13 @@ 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, Collections.<Annotation>emptyList());
+ handleSetter.generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode);
+ handleToString.generateToStringForType(typeNode, annotationNode);
+ handleConstructor.generateRequiredArgsConstructor(
typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES,
Collections.<Annotation>emptyList(), annotationNode);
+ handleConstructor.generateExtraNoArgsConstructor(typeNode, annotationNode);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index a56eee89..c99b9b5f 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -38,12 +38,13 @@ import lombok.ConfigurationKeys;
import lombok.EqualsAndHashCode;
import lombok.core.AST.Kind;
import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.InclusionExclusionUtils;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.core.AnnotationValues;
import lombok.core.configuration.CallSuperType;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
@@ -57,7 +58,6 @@ 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.FalseLiteral;
-import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression;
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
@@ -67,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;
@@ -98,59 +99,41 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
public static final Set<String> BUILT_IN_TYPES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
"byte", "short", "int", "long", "char", "boolean", "double", "float")));
- public void checkForBogusFieldNames(EclipseNode type, AnnotationValues<EqualsAndHashCode> annotation) {
- if (annotation.isExplicit("exclude")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().exclude()), type, true, true)) {
- annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
- }
- }
- if (annotation.isExplicit("of")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().of()), type, false, false)) {
- annotation.setWarning("of", "This field does not exist.", i);
- }
- }
- }
-
- public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) {
- if (hasAnnotation(EqualsAndHashCode.class, typeNode)) {
- //The annotation will make it happen, so we can skip it.
- return;
- }
-
- generateMethods(typeNode, errorNode, null, null, null, false, FieldAccess.GETTER, new ArrayList<Annotation>());
- }
-
@Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
EqualsAndHashCode ann = annotation.getInstance();
- List<String> excludes = Arrays.asList(ann.exclude());
- List<String> includes = Arrays.asList(ann.of());
- EclipseNode typeNode = annotationNode.up();
+ List<Included<EclipseNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode);
+ if (members == null) return;
- List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam=", annotationNode);
- checkForBogusFieldNames(typeNode, annotation);
+ List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
Boolean callSuper = ann.callSuper();
if (!annotation.isExplicit("callSuper")) callSuper = null;
- if (!annotation.isExplicit("exclude")) excludes = null;
- if (!annotation.isExplicit("of")) includes = null;
-
- if (excludes != null && includes != null) {
- excludes = null;
- annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
- }
Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
- generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess, onParam);
+ generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, fieldAccess, onParam);
}
- public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
- assert excludes == null || includes == null;
+ public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) {
+ if (hasAnnotation(EqualsAndHashCode.class, typeNode)) {
+ //The annotation will make it happen, so we can skip it.
+ return;
+ }
+
+ List<Included<EclipseNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(typeNode, null, null);
+
+ 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, members, null, false, access, new ArrayList<Annotation>());
+ }
+
+ public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, EqualsAndHashCode.Include>> members,
+ Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
TypeDeclaration typeDecl = null;
@@ -168,18 +151,13 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
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.");
}
}
- boolean isDirectDescendantOfObject = true;
-
- if (typeDecl.superclass != null) {
- String p = typeDecl.superclass.toString();
- isDirectDescendantOfObject = p.equals("Object") || p.equals("java.lang.Object");
- }
+ boolean isDirectDescendantOfObject = isDirectDescendantOfObject(typeNode);
if (isDirectDescendantOfObject && callSuper) {
errorNode.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless.");
@@ -205,27 +183,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
}
- List<EclipseNode> nodesForEquality = new ArrayList<EclipseNode>();
- if (includes != null) {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (includes.contains(new String(fieldDecl.name))) nodesForEquality.add(child);
- }
- } else {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (!filterField(fieldDecl)) continue;
-
- //Skip transient fields.
- if ((fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0) continue;
- //Skip excluded fields.
- if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue;
- nodesForEquality.add(child);
- }
- }
-
boolean isFinal = (typeDecl.modifiers & ClassFileConstants.AccFinal) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
@@ -254,7 +211,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
//fallthrough
}
- MethodDeclaration equalsMethod = createEquals(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam);
+ MethodDeclaration equalsMethod = createEquals(typeNode, members, callSuper, errorNode.get(), fieldAccess, needsCanEqual, onParam);
equalsMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope);
injectMethod(typeNode, equalsMethod);
@@ -264,17 +221,16 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
injectMethod(typeNode, canEqualMethod);
}
- MethodDeclaration hashCodeMethod = createHashCode(typeNode, nodesForEquality, callSuper, errorNode.get(), fieldAccess);
+ MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, errorNode.get(), fieldAccess);
hashCodeMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope);
injectMethod(typeNode, hashCodeMethod);
}
- public MethodDeclaration createHashCode(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+ public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long)pS << 32 | pE;
- MethodDeclaration method = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
setGeneratedBy(method, source);
method.modifiers = toEclipseModifier(AccessLevel.PUBLIC);
@@ -291,11 +247,11 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
List<Statement> statements = new ArrayList<Statement>();
- final boolean isEmpty = fields.isEmpty();
+ final boolean isEmpty = members.isEmpty();
/* final int PRIME = X; */ {
- /* Without fields, PRIME isn't used, and that would trigger a 'local variable not used' warning. */
- if (!isEmpty || callSuper) {
+ /* Without members, PRIME isn't used, as that would trigger a 'local variable not used' warning. */
+ if (!isEmpty) {
LocalDeclaration primeDecl = new LocalDeclaration(PRIME, pS, pE);
setGeneratedBy(primeDecl, source);
primeDecl.modifiers |= Modifier.FINAL;
@@ -307,31 +263,38 @@ 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();
+ for (Included<EclipseNode, EqualsAndHashCode.Include> member : members) {
+ EclipseNode memberNode = member.getNode();
+ boolean isMethod = memberNode.getKind() == Kind.METHOD;
+
+ TypeReference fType = getFieldType(memberNode, fieldAccess);
+ char[] dollarFieldName = ((isMethod ? "$$" : "$") + memberNode.getName()).toCharArray();
char[] token = fType.getLastToken();
- Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
+ Expression fieldAccessor = isMethod ? createMethodAccessor(memberNode, source) : createFieldAccessor(memberNode, fieldAccess, source);
if (fType.dimensions() == 0 && token != null) {
if (Arrays.equals(TypeConstants.BOOLEAN, token)) {
/* booleanField ? X : Y */
@@ -458,17 +421,46 @@ 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 & ClassFileConstants.AccStatic) != 0;
EclipseNode tNode = type.up();
+ if (!staticContext && tNode.getKind() == Kind.TYPE && (((TypeDeclaration) tNode.get()).modifiers & ClassFileConstants.AccInterface) != 0) staticContext = true;
+
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();
+ if (!staticContext && tNode.getKind() == Kind.TYPE && (((TypeDeclaration) tNode.get()).modifiers & ClassFileConstants.AccInterface) != 0) staticContext = true;
}
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++) {
@@ -476,16 +468,33 @@ 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;
}
- public MethodDeclaration createEquals(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List<Annotation> onParam) {
+ private int arraySizeOf(Object[] arr) {
+ return arr == null ? 0 : arr.length;
+ }
+
+ public MethodDeclaration createEquals(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, 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);
+ MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
setGeneratedBy(method, source);
method.modifiers = toEclipseModifier(AccessLevel.PUBLIC);
method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
@@ -528,7 +537,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);
@@ -551,30 +560,15 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
char[] otherName = "other".toCharArray();
- /* MyType<?> other = (MyType<?>) o; */ {
- if (!fields.isEmpty() || needsCanEqual) {
+ /* Outer.Inner.MyType<?> other = (Outer.Inner.MyType<?>) o; */ {
+ if (!members.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.initialization = makeCastExpression(oRef, targetType, source);
@@ -636,11 +630,14 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
statements.add(ifSuperEquals);
}
- for (EclipseNode field : fields) {
- TypeReference fType = getFieldType(field, fieldAccess);
+ for (Included<EclipseNode, EqualsAndHashCode.Include> member : members) {
+ EclipseNode memberNode = member.getNode();
+ boolean isMethod = memberNode.getKind() == Kind.METHOD;
+
+ TypeReference fType = getFieldType(memberNode, fieldAccess);
char[] token = fType.getLastToken();
- Expression thisFieldAccessor = createFieldAccessor(field, fieldAccess, source);
- Expression otherFieldAccessor = createFieldAccessor(field, fieldAccess, source, otherName);
+ Expression thisFieldAccessor = isMethod ? createMethodAccessor(memberNode, source) : createFieldAccessor(memberNode, fieldAccess, source);
+ Expression otherFieldAccessor = isMethod ? createMethodAccessor(memberNode, source, otherName) : createFieldAccessor(memberNode, fieldAccess, source, otherName);
if (fType.dimensions() == 0 && token != null) {
if (Arrays.equals(TypeConstants.FLOAT, token)) {
@@ -660,9 +657,9 @@ 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;; */
- char[] thisDollarFieldName = ("this$" + field.getName()).toCharArray();
- char[] otherDollarFieldName = ("other$" + field.getName()).toCharArray();
+ /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */
+ char[] thisDollarFieldName = ("this" + (isMethod ? "$$" : "$") + memberNode.getName()).toCharArray();
+ char[] otherDollarFieldName = ("other" + (isMethod ? "$$" : "$") + memberNode.getName()).toCharArray();
statements.add(createLocalDeclaration(source, thisDollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), thisFieldAccessor));
statements.add(createLocalDeclaration(source, otherDollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), otherFieldAccessor));
@@ -675,7 +672,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
setGeneratedBy(other1, source);
SingleNameReference other2 = new SingleNameReference(otherDollarFieldName, p);
setGeneratedBy(other2, source);
-
NullLiteral nullLiteral = new NullLiteral(pS, pE);
setGeneratedBy(nullLiteral, source);
@@ -738,7 +734,6 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
return method;
}
-
public MethodDeclaration createCanEqual(EclipseNode type, ASTNode source, List<Annotation> onParam) {
/* protected boolean canEqual(final java.lang.Object other) {
* return other instanceof Outer.Inner.MyType;
@@ -749,8 +744,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
char[] otherName = "other".toCharArray();
- MethodDeclaration method = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration method = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
setGeneratedBy(method, source);
method.modifiers = toEclipseModifier(AccessLevel.PROTECTED);
method.returnType = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
@@ -772,7 +766,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 5ea5a210..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
@@ -97,14 +97,16 @@ public class HandleFieldDefaults extends EclipseASTAdapter {
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)) {
- if ((field.modifiers & ClassFileConstants.AccStatic) == 0 || field.initialization != null) {
+ if ((field.modifiers & ClassFileConstants.AccStatic) == 0) {
field.modifiers |= ClassFileConstants.AccFinal;
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
new file mode 100644
index 00000000..c3a28f7f
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
@@ -0,0 +1,135 @@
+/*
+ * 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.eclipse.handlers;
+
+import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+
+import lombok.AccessLevel;
+import lombok.ConfigurationKeys;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.handlers.HandlerUtil;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.experimental.FieldNameConstants;
+
+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.StringLiteral;
+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;
+
+@ProviderFor(EclipseAnnotationHandler.class)
+public class HandleFieldNameConstants extends EclipseAnnotationHandler<FieldNameConstants> {
+ public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, String prefix, String suffix) {
+ 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)) != 0;
+
+ if (typeDecl == null || notAClass) {
+ errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field.");
+ return;
+ }
+
+ for (EclipseNode field : typeNode.down()) {
+ if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level, prefix, suffix);
+ }
+ }
+
+ private void generateFieldNameConstantsForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, String prefix, String suffix) {
+ if (hasAnnotation(FieldNameConstants.class, fieldNode)) return;
+ createFieldNameConstantsForField(level, prefix, suffix, fieldNode, fieldNode, pos, false);
+ }
+
+ private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field) {
+ if (field.getKind() != Kind.FIELD) return false;
+ FieldDeclaration fieldDecl = (FieldDeclaration) field.get();
+ return filterField(fieldDecl);
+ }
+
+ public void handle(AnnotationValues<FieldNameConstants> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_NAME_CONSTANTS_FLAG_USAGE, "@FieldNameConstants");
+
+ EclipseNode node = annotationNode.up();
+ FieldNameConstants annotatationInstance = annotation.getInstance();
+ AccessLevel level = annotatationInstance.level();
+ String prefix = annotatationInstance.prefix();
+ String suffix = annotatationInstance.suffix();
+ if (prefix.equals(" CONFIG DEFAULT ")) prefix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_PREFIX);
+ if (suffix.equals(" CONFIG DEFAULT ")) suffix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_SUFFIX);
+ if (prefix == null) prefix = "FIELD_";
+ if (suffix == null) suffix = "";
+ if (node == null) return;
+
+ switch (node.getKind()) {
+ case FIELD:
+ if (level != AccessLevel.NONE) createFieldNameConstantsForFields(level, prefix, suffix, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true);
+ break;
+ case TYPE:
+ if (level == AccessLevel.NONE) {
+ annotationNode.addWarning("type-level '@FieldNameConstants' does not work with AccessLevel.NONE.");
+ return;
+ }
+ generateFieldNameConstantsForType(node, annotationNode, level, prefix, suffix);
+ break;
+ }
+ }
+
+ private void createFieldNameConstantsForFields(AccessLevel level, String prefix, String suffix, Collection<EclipseNode> fieldNodes, EclipseNode errorNode, ASTNode source, boolean whineIfExists) {
+ for (EclipseNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, prefix, suffix, fieldNode, errorNode, source, whineIfExists);
+ }
+
+ private void createFieldNameConstantsForField(AccessLevel level, String prefix, String suffix, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists) {
+ if (fieldNode.getKind() != Kind.FIELD) {
+ errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field");
+ return;
+ }
+
+ FieldDeclaration field = (FieldDeclaration) fieldNode.get();
+ String fieldName = new String(field.name);
+ String constantName = prefix + HandlerUtil.camelCaseToConstant(fieldName) + suffix;
+ if (constantName.equals(fieldName)) {
+ fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field.");
+ return;
+ }
+
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+ FieldDeclaration fieldConstant = new FieldDeclaration(constantName.toCharArray(), pS,pE);
+ fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ fieldConstant.modifiers = toEclipseModifier(level) | Modifier.STATIC | Modifier.FINAL;
+ fieldConstant.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p,p,p});
+ fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0);
+ injectField(fieldNode.up(), fieldConstant);
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java
index 14f2fb72..d0c2cc23 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
@@ -41,7 +41,6 @@ import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.agent.PatchDelegate;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
@@ -80,7 +79,7 @@ import org.mangosdk.spi.ProviderFor;
public class HandleGetter extends EclipseAnnotationHandler<Getter> {
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
- public boolean generateGetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelGetter) {
+ public boolean generateGetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelGetter, List<Annotation> onMethod) {
if (checkForTypeLevelGetter) {
if (hasAnnotation(Getter.class, typeNode)) {
//The annotation will make it happen, so we can skip it.
@@ -100,12 +99,12 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
}
for (EclipseNode field : typeNode.down()) {
- if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, pos.get(), level, false);
+ if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, pos.get(), level, false, onMethod);
}
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);
@@ -123,13 +122,13 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
* If not, the getter is still generated if it isn't already there, though there will not
* be a warning if its already there. The default access level is used.
*/
- public void generateGetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, boolean lazy) {
+ public void generateGetterForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level, boolean lazy, List<Annotation> onMethod) {
if (hasAnnotation(Getter.class, fieldNode)) {
//The annotation will make it happen, so we can skip it.
return;
}
- createGetterForField(level, fieldNode, fieldNode, pos, false, lazy, Collections.<Annotation>emptyList());
+ createGetterForField(level, fieldNode, fieldNode, pos, false, lazy, onMethod);
}
public void handle(AnnotationValues<Getter> annotation, Annotation ast, EclipseNode annotationNode) {
@@ -148,18 +147,15 @@ 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:
createGetterForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true, lazy, onMethod);
break;
case TYPE:
- if (!onMethod.isEmpty()) {
- annotationNode.addError("'onMethod' is not supported for @Getter on a type.");
- }
if (lazy) annotationNode.addError("'lazy' is not supported for @Getter on a type.");
- generateGetterForType(node, annotationNode, level, false);
+ generateGetterForType(node, annotationNode, level, false, onMethod);
break;
}
}
@@ -183,6 +179,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;
diff --git a/src/core/lombok/eclipse/handlers/HandleLog.java b/src/core/lombok/eclipse/handlers/HandleLog.java
index c49030d8..8c7f7971 100644
--- a/src/core/lombok/eclipse/handlers/HandleLog.java
+++ b/src/core/lombok/eclipse/handlers/HandleLog.java
@@ -109,7 +109,7 @@ public class HandleLog {
private static FieldDeclaration createField(LoggingFramework framework, Annotation source, ClassLiteralAccess loggingType, String logFieldName, boolean useStatic, String loggerTopic) {
int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
// private static final <loggerType> log = <factoryMethod>(<parameter>);
FieldDeclaration fieldDecl = new FieldDeclaration(logFieldName.toCharArray(), 0, -1);
@@ -126,13 +126,15 @@ public class HandleLog {
factoryMethodCall.selector = framework.getLoggerFactoryMethodName().toCharArray();
Expression parameter;
- if (loggerTopic == null || loggerTopic.trim().length() == 0) {
+ if (!framework.passTypeName) {
+ parameter = null;
+ } else if (loggerTopic == null || loggerTopic.trim().length() == 0) {
parameter = framework.createFactoryParameter(loggingType, source);
} else {
parameter = new StringLiteral(loggerTopic.toCharArray(), pS, pE, 0);
}
- factoryMethodCall.arguments = new Expression[] { parameter };
+ factoryMethodCall.arguments = parameter != null ? new Expression[] { parameter } : null;
factoryMethodCall.nameSourcePosition = p;
factoryMethodCall.sourceStart = pS;
factoryMethodCall.sourceEnd = factoryMethodCall.statementEnd = pE;
@@ -240,6 +242,17 @@ public class HandleLog {
}
}
+ /**
+ * Handles the {@link lombok.extern.flogger.Flogger} annotation for Eclipse.
+ */
+ @ProviderFor(EclipseAnnotationHandler.class)
+ public static class HandleFloggerLog extends EclipseAnnotationHandler<lombok.extern.flogger.Flogger> {
+ @Override public void handle(AnnotationValues<lombok.extern.flogger.Flogger> annotation, Annotation source, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.LOG_FLOGGER_FLAG_USAGE, "@Flogger", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log");
+ processAnnotation(LoggingFramework.FLOGGER, annotation, source, annotationNode, "");
+ }
+ }
+
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"),
@@ -278,18 +291,31 @@ public class HandleLog {
// 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 static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
+ FLOGGER("com.google.common.flogger.FluentLogger", "com.google.common.flogger.FluentLogger", "forEnclosingClass", "@Flogger", false),
;
private final String loggerTypeName;
private final String loggerFactoryTypeName;
private final String loggerFactoryMethodName;
private final String annotationAsString;
-
+ private final boolean passTypeName;
+
+ LoggingFramework(String loggerTypeName, String loggerFactoryTypeName, String loggerFactoryMethodName, String annotationAsString, boolean passTypeName) {
+ this.loggerTypeName = loggerTypeName;
+ this.loggerFactoryTypeName = loggerFactoryTypeName;
+ this.loggerFactoryMethodName = loggerFactoryMethodName;
+ this.annotationAsString = annotationAsString;
+ this.passTypeName = passTypeName;
+ }
+
LoggingFramework(String loggerTypeName, String loggerFactoryTypeName, String loggerFactoryMethodName, String annotationAsString) {
this.loggerTypeName = loggerTypeName;
this.loggerFactoryTypeName = loggerFactoryTypeName;
this.loggerFactoryMethodName = loggerFactoryMethodName;
this.annotationAsString = annotationAsString;
+ this.passTypeName = true;
}
final String getAnnotationAsString() {
@@ -308,7 +334,7 @@ public class HandleLog {
return loggerFactoryMethodName;
}
- Expression createFactoryParameter(ClassLiteralAccess loggingType, Annotation source){
+ Expression createFactoryParameter(ClassLiteralAccess loggingType, Annotation source) {
TypeReference copy = copyType(loggingType.type, source);
ClassLiteralAccess result = new ClassLiteralAccess(source.sourceEnd, copy);
setGeneratedBy(result, source);
diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java
index 1fcf751d..d4df0deb 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
@@ -24,11 +24,12 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.Eclipse.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.toAllSetterNames;
+import static lombok.eclipse.handlers.EclipseHandlerUtil.toSetterName;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import lombok.AccessLevel;
@@ -38,7 +39,6 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
@@ -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;
@@ -63,7 +64,7 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleSetter extends EclipseAnnotationHandler<Setter> {
- public boolean generateSetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelSetter) {
+ public boolean generateSetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelSetter, List<Annotation> onMethod, List<Annotation> onParam) {
if (checkForTypeLevelSetter) {
if (hasAnnotation(Setter.class, typeNode)) {
//The annotation will make it happen, so we can skip it.
@@ -90,7 +91,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
//Skip final fields.
if ((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) continue;
- generateSetterForField(field, pos, level);
+ generateSetterForField(field, pos, level, onMethod, onParam);
}
return true;
}
@@ -107,39 +108,30 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
* If not, the setter is still generated if it isn't already there, though there will not
* be a warning if its already there. The default access level is used.
*/
- public void generateSetterForField(EclipseNode fieldNode, EclipseNode sourceNode, AccessLevel level) {
+ public void generateSetterForField(EclipseNode fieldNode, EclipseNode sourceNode, AccessLevel level, List<Annotation> onMethod, List<Annotation> onParam) {
if (hasAnnotation(Setter.class, fieldNode)) {
//The annotation will make it happen, so we can skip it.
return;
}
-
- List<Annotation> empty = Collections.emptyList();
-
- createSetterForField(level, fieldNode, sourceNode, false, empty, empty);
+ createSetterForField(level, fieldNode, sourceNode, false, onMethod, onParam);
}
- public void handle(AnnotationValues<Setter> annotation, Annotation ast, EclipseNode annotationNode) {
+ @Override public void handle(AnnotationValues<Setter> annotation, Annotation ast, EclipseNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.SETTER_FLAG_USAGE, "@Setter");
EclipseNode node = annotationNode.up();
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:
createSetterForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, true, onMethod, onParam);
break;
case TYPE:
- if (!onMethod.isEmpty()) {
- annotationNode.addError("'onMethod' is not supported for @Setter on a type.");
- }
- if (!onParam.isEmpty()) {
- annotationNode.addError("'onParam' is not supported for @Setter on a type.");
- }
- generateSetterForType(node, annotationNode, level, false);
+ generateSetterForType(node, annotationNode, level, false, onMethod, onParam);
break;
}
}
@@ -192,28 +184,40 @@ 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, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) {
+ ASTNode source = sourceNode.get();
+ int pS = source.sourceStart, pE = source.sourceEnd;
+
+ TypeReference returnType = null;
+ ReturnStatement returnThis = null;
+ if (shouldReturnThis) {
+ returnType = cloneSelfType(fieldNode, source);
+ ThisReference thisRef = new ThisReference(pS, pE);
+ returnThis = new ReturnStatement(thisRef, pS, pE);
+ }
+
+ return createSetter(parent, deprecate, fieldNode, name, booleanFieldToSet, returnType, returnThis, modifier, sourceNode, onMethod, onParam);
+ }
- 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, TypeReference returnType, Statement returnStatement, 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;
long p = (long)pS << 32 | pE;
MethodDeclaration method = new MethodDeclaration(parent.compilationResult);
method.modifiers = modifier;
- if (shouldReturnThis) {
- method.returnType = cloneSelfType(fieldNode, source);
- }
-
- if (method.returnType == null) {
+ if (returnType != null) {
+ method.returnType = returnType;
+ } else {
method.returnType = TypeReference.baseTypeReference(TypeIds.T_void, 0);
method.returnType.sourceStart = pS; method.returnType.sourceEnd = pE;
- shouldReturnThis = false;
}
Annotation[] deprecated = null;
- if (isFieldDeprecated(fieldNode)) {
+ if (isFieldDeprecated(fieldNode) || deprecate) {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated);
@@ -243,10 +247,12 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
statements.add(assignment);
}
- if (shouldReturnThis) {
- ThisReference thisRef = new ThisReference(pS, pE);
- ReturnStatement returnThis = new ReturnStatement(thisRef, pS, pE);
- statements.add(returnThis);
+ if (booleanFieldToSet != null) {
+ statements.add(new Assignment(new SingleNameReference(booleanFieldToSet, p), new TrueLiteral(pS, pE), pE));
+ }
+
+ if (returnType != null && returnStatement != null) {
+ statements.add(returnStatement);
}
method.statements = statements.toArray(new Statement[0]);
param.annotations = copyAnnotations(source, nonNulls, nullables, onParam.toArray(new Annotation[0]));
diff --git a/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
new file mode 100644
index 00000000..6b0275e4
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleSuperBuilder.java
@@ -0,0 +1,836 @@
+/*
+ * 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
+ * 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.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.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.IfStatement;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+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;
+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.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
+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.ToString;
+import lombok.core.AST.Kind;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
+import lombok.experimental.NonFinal;
+import lombok.experimental.SuperBuilder;
+
+@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 HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
+ private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray();
+ private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray();
+ private static final char[] SET_PREFIX = "$set".toCharArray();
+ private static final char[] SELF_METHOD_NAME = "self".toCharArray();
+
+ private static final AbstractMethodDeclaration[] EMPTY_METHODS = {};
+
+ private static class BuilderFieldData {
+ TypeReference type;
+ char[] rawName;
+ char[] name;
+ char[] nameOfSetFlag;
+ SingularData singularData;
+ ObtainVia obtainVia;
+ EclipseNode obtainViaNode;
+ EclipseNode originalFieldNode;
+
+ List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
+ }
+
+ @Override
+ public void handle(AnnotationValues<SuperBuilder> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
+
+ long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
+
+ SuperBuilder builderInstance = annotation.getInstance();
+
+ String builderMethodName = builderInstance.builderMethodName();
+ String buildMethodName = builderInstance.buildMethodName();
+
+ if (builderMethodName == null) builderMethodName = "builder";
+ if (buildMethodName == null) buildMethodName = "build";
+
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
+ if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
+
+ EclipseNode tdParent = annotationNode.up();
+
+ java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
+ TypeReference returnType;
+ TypeParameter[] typeParams;
+
+ boolean addCleaning = false;
+
+ if (!(tdParent.get() instanceof TypeDeclaration)) {
+ annotationNode.addError("@SuperBuilder is only supported on types.");
+ return;
+ }
+ TypeDeclaration td = (TypeDeclaration) tdParent.get();
+
+ // Gather all fields of the class that should be set by the builder.
+ List<EclipseNode> allFields = new ArrayList<EclipseNode>();
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent));
+ for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) {
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ 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.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX);
+ // The @Builder annotation removes the initializing expression on the field and moves
+ // it to a method called "$default$FIELDNAME". This method is then called upon building.
+ // We do NOT do this, because this is unexpected and may lead to bugs when using other
+ // constructors (see, e.g., issue #1347).
+ // Instead, we keep the init expression and only set a new value in the builder-based
+ // constructor if it was set in the builder. Drawback is that the init expression is
+ // always executed, even if it was unnecessary because its value is overwritten by the
+ // builder.
+ // TODO: Once the issue is resolved in @Builder, we can adapt the solution here.
+ }
+ addObtainVia(bfd, fieldNode);
+ builderFields.add(bfd);
+ allFields.add(fieldNode);
+ }
+
+ // Set the names of the builder classes.
+ String builderClassName = String.valueOf(td.name) + "Builder";
+ String builderImplClassName = builderClassName + "Impl";
+
+ typeParams = td.typeParameters != null ? td.typeParameters : new TypeParameter[0];
+ returnType = namePlusTypeParamsToTypeReference(td.name, typeParams, p);
+
+ // <C, B> are the generics for our builder.
+ String classGenericName = "C";
+ String builderGenericName = "B";
+ // If these generics' names collide with any generics on the annotated class, modify them.
+ // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder.
+ java.util.List<String> typeParamStrings = new ArrayList<String>();
+ for (TypeParameter typeParam : typeParams) typeParamStrings.add(typeParam.toString());
+ classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings);
+ builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings);
+
+ TypeReference extendsClause = td.superclass;
+ TypeReference superclassBuilderClass = null;
+ TypeReference[] typeArguments = new TypeReference[] {
+ new SingleTypeReference(classGenericName.toCharArray(), 0),
+ new SingleTypeReference(builderGenericName.toCharArray(), 0)
+ };
+ if (extendsClause instanceof QualifiedTypeReference) {
+ QualifiedTypeReference qualifiedTypeReference = (QualifiedTypeReference)extendsClause;
+ String superclassClassName = String.valueOf(qualifiedTypeReference.getLastToken());
+ String superclassBuilderClassName = superclassClassName + "Builder";
+
+ char[][] tokens = Arrays.copyOf(qualifiedTypeReference.tokens, qualifiedTypeReference.tokens.length + 1);
+ tokens[tokens.length] = superclassBuilderClassName.toCharArray();
+ long[] poss = new long[tokens.length];
+ Arrays.fill(poss, p);
+
+ TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause);
+
+ // Every token may potentially have type args. Here, we only have
+ // type args for the last token, the superclass' builder.
+ TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][];
+ typeArgsForTokens[typeArgsForTokens.length-1] = mergeTypeReferences(superclassTypeArgs, typeArguments);
+
+ superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss);
+ } else if (extendsClause != null) {
+ String superClass = String.valueOf(extendsClause.getTypeName()[0]);
+ String superclassBuilderClassName = superClass + "Builder";
+
+ char[][] tokens = new char[][] {superClass.toCharArray(), superclassBuilderClassName.toCharArray()};
+ long[] poss = new long[tokens.length];
+ Arrays.fill(poss, p);
+
+ TypeReference[] superclassTypeArgs = getTypeParametersFrom(extendsClause);
+
+ // Every token may potentially have type args. Here, we only have
+ // type args for the last token, the superclass' builder.
+ TypeReference[][] typeArgsForTokens = new TypeReference[tokens.length][];
+ typeArgsForTokens[typeArgsForTokens.length-1] = mergeTypeReferences(superclassTypeArgs, typeArguments);
+
+ superclassBuilderClass = new ParameterizedQualifiedTypeReference(tokens, typeArgsForTokens, 0, poss);
+ }
+ // If there is no superclass, superclassBuilderClassExpression is still == null at this point.
+ // You can use it to check whether to inherit or not.
+
+ generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
+ superclassBuilderClass != null);
+
+ // Create the abstract builder class.
+ EclipseNode builderType = findInnerClass(tdParent, builderClassName);
+ if (builderType == null) {
+ builderType = generateBuilderAbstractClass(tdParent, builderClassName, superclassBuilderClass,
+ typeParams, ast, classGenericName, builderGenericName);
+ } else {
+ annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
+ return;
+ }
+
+ // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary.
+ 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;
+ }
+ }
+ }
+
+ // Generate the fields in the abstract builder class that hold the values for the instance.
+ 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);
+ }
+
+ // Generate abstract self() and build() methods in the abstract builder.
+ injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClass != null, builderGenericName));
+ injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClass != null, classGenericName, ast));
+
+ // Create the setter methods in the abstract builder.
+ for (BuilderFieldData bfd : builderFields) {
+ generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName);
+ }
+
+ // Create the toString() method for the abstract builder.
+ if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) {
+ List<Included<EclipseNode, ToString.Include>> fieldNodes = new ArrayList<Included<EclipseNode, ToString.Include>>();
+ for (BuilderFieldData bfd : builderFields) {
+ for (EclipseNode f : bfd.createdFields) {
+ fieldNodes.add(new Included<EclipseNode, ToString.Include>(f, null, true));
+ }
+ }
+ // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder.
+ MethodDeclaration md = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD);
+ if (md != null) {
+ injectMethod(builderType, md);
+ }
+ }
+
+ if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
+
+ if ((td.modifiers & ClassFileConstants.AccAbstract) != 0) {
+ // Only non-abstract classes get the Builder implementation.
+ return;
+ }
+
+ // Create the builder implementation class.
+ EclipseNode builderImplType = findInnerClass(tdParent, builderImplClassName);
+ if (builderImplType == null) {
+ builderImplType = generateBuilderImplClass(tdParent, builderImplClassName, builderClassName, typeParams, ast);
+ } else {
+ annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
+ return;
+ }
+
+ // Create the self() and build() methods in the BuilderImpl.
+ injectMethod(builderImplType, generateSelfMethod(builderImplType));
+ injectMethod(builderImplType, generateBuildMethod(tdParent, buildMethodName, returnType, ast));
+
+ // Add the builder() method to the annotated class.
+ if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+ MethodDeclaration md = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, tdParent, typeParams, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
+ private EclipseNode generateBuilderAbstractClass(EclipseNode tdParent, String builderClass,
+ TypeReference superclassBuilderClass, TypeParameter[] typeParams,
+ ASTNode source, String classGenericName, String builderGenericName) {
+
+ 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 | ClassFileConstants.AccAbstract;
+ builder.name = builderClass.toCharArray();
+
+ // Keep any type params of the annotated class.
+ builder.typeParameters = Arrays.copyOf(copyTypeParams(typeParams, source), typeParams.length + 2);
+ // Add builder-specific type params required for inheritable builders.
+ // 1. The return type for the build() method, named "C", which extends the annotated class.
+ TypeParameter o = new TypeParameter();
+ o.name = classGenericName.toCharArray();
+ o.type = cloneSelfType(tdParent, source);
+ builder.typeParameters[builder.typeParameters.length - 2] = o;
+ // 2. The return type for all setter methods, named "B", which extends this builder class.
+ o = new TypeParameter();
+ o.name = builderGenericName.toCharArray();
+ TypeReference[] typerefs = appendBuilderTypeReferences(typeParams, classGenericName, builderGenericName);
+ o.type = new ParameterizedSingleTypeReference(builderClass.toCharArray(), typerefs, 0, 0);
+ builder.typeParameters[builder.typeParameters.length - 1] = o;
+
+ builder.superclass = copyType(superclassBuilderClass, source);
+
+ builder.createDefaultConstructor(false, true);
+
+ builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return injectType(tdParent, builder);
+ }
+
+ private EclipseNode generateBuilderImplClass(EclipseNode tdParent, String builderImplClass, String builderAbstractClass, 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.AccPrivate | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal;
+ builder.name = builderImplClass.toCharArray();
+
+ // Add type params if there are any.
+ if (typeParams != null && typeParams.length > 0) builder.typeParameters = copyTypeParams(typeParams, source);
+
+ if (builderAbstractClass != null) {
+ // Extend the abstract builder.
+ // 1. Add any type params of the annotated class.
+ TypeReference[] typeArgs = new TypeReference[typeParams.length + 2];
+ for (int i = 0; i < typeParams.length; i++) {
+ typeArgs[i] = new SingleTypeReference(typeParams[i].name, 0);
+ }
+ // 2. The return type for the build() method (named "C" in the abstract builder), which is the annotated class.
+ // 3. The return type for all setter methods (named "B" in the abstract builder), which is this builder class.
+ typeArgs[typeArgs.length - 2] = cloneSelfType(tdParent, source);
+ typeArgs[typeArgs.length - 1] = createTypeReferenceWithTypeParameters(builderImplClass, typeParams);
+ builder.superclass = new ParameterizedSingleTypeReference(builderAbstractClass.toCharArray(), typeArgs, 0, 0);
+ }
+
+ builder.createDefaultConstructor(false, true);
+
+ builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return injectType(tdParent, builder);
+ }
+
+ /**
+ * Generates a constructor that has a builder as the only parameter.
+ * The values from the builder are used to initialize the fields of new instances.
+ *
+ * @param typeNode
+ * the type (with the {@code @Builder} annotation) for which a
+ * constructor should be generated.
+ * @param typeParams
+ * @param builderFields a list of fields in the builder which should be assigned to new instances.
+ * @param source the annotation (used for setting source code locations for the generated code).
+ * @param callBuilderBasedSuperConstructor
+ * If {@code true}, the constructor will explicitly call a super
+ * constructor with the builder as argument. Requires
+ * {@code builderClassAsParameter != null}.
+ */
+ private void generateBuilderBasedConstructor(EclipseNode typeNode, TypeParameter[] typeParams, List<BuilderFieldData> builderFields,
+ EclipseNode sourceNode, String builderClassName, boolean callBuilderBasedSuperConstructor) {
+
+ 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 = toEclipseModifier(AccessLevel.PROTECTED);
+ constructor.selector = typeDeclaration.name;
+ if (callBuilderBasedSuperConstructor) {
+ constructor.constructorCall = new ExplicitConstructorCall(ExplicitConstructorCall.Super);
+ constructor.constructorCall.arguments = new Expression[] {new SingleNameReference("b".toCharArray(), p)};
+ } else {
+ 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;
+
+ TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND)};
+ TypeReference builderType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
+ constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, builderType, Modifier.FINAL)};
+
+ List<Statement> statements = new ArrayList<Statement>();
+ List<Statement> nullChecks = new ArrayList<Statement>();
+
+ for (BuilderFieldData fieldNode : builderFields) {
+ char[] fieldName = removePrefixFromField(fieldNode.originalFieldNode);
+ FieldReference thisX = new FieldReference(fieldNode.rawName, p);
+ int s = (int) (p >> 32);
+ int e = (int) p;
+ thisX.receiver = new ThisReference(s, e);
+
+ Expression assignmentExpr;
+ if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) {
+ fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, "b");
+ assignmentExpr = new SingleNameReference(fieldNode.name, p);
+ } else {
+ char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldName};
+ long[] positions = new long[] {p, p};
+ assignmentExpr = new QualifiedNameReference(variableInBuilder, positions, s, e);
+ }
+ Statement assignment = new Assignment(thisX, assignmentExpr, (int) p);
+
+ // In case of @Builder.Default, only set the value if it really was set in the builder.
+ if (fieldNode.nameOfSetFlag != null) {
+ char[][] variableInBuilder = new char[][] {"b".toCharArray(), fieldNode.nameOfSetFlag};
+ long[] positions = new long[] {p, p};
+ QualifiedNameReference builderRef = new QualifiedNameReference(variableInBuilder, positions, s, e);
+ assignment = new IfStatement(builderRef, assignment, s, e);
+ }
+ statements.add(assignment);
+
+ Annotation[] nonNulls = findAnnotations((FieldDeclaration)fieldNode.originalFieldNode.get(), NON_NULL_PATTERN);
+ if (nonNulls.length != 0) {
+ Statement nullCheck = generateNullCheck((FieldDeclaration)fieldNode.originalFieldNode.get(), sourceNode);
+ if (nullCheck != null) {
+ nullChecks.add(nullCheck);
+ }
+ }
+ }
+
+ nullChecks.addAll(statements);
+ constructor.statements = nullChecks.isEmpty() ? null : nullChecks.toArray(new Statement[nullChecks.size()]);
+
+ constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
+
+ injectMethod(typeNode, constructor);
+ }
+
+ private MethodDeclaration generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) {
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ out.selector = builderMethodName.toCharArray();
+ out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+
+ // Add type params if there are any.
+ if (typeParams != null && typeParams.length > 0) out.typeParameters = copyTypeParams(typeParams, source);
+
+ TypeReference[] wildcards = new TypeReference[] {new Wildcard(Wildcard.UNBOUND), new Wildcard(Wildcard.UNBOUND) };
+ out.returnType = new ParameterizedSingleTypeReference(builderClassName.toCharArray(), mergeToTypeReferences(typeParams, wildcards), 0, p);
+
+ AllocationExpression invoke = new AllocationExpression();
+ invoke.type = namePlusTypeParamsToTypeReference(builderImplClassName.toCharArray(), typeParams, p);
+ out.statements = new Statement[] {new ReturnStatement(invoke, pS, pE)};
+
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
+ return out;
+ }
+
+ private MethodDeclaration generateAbstractSelfMethod(EclipseNode tdParent, boolean override, String builderGenericName) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+ out.selector = SELF_METHOD_NAME;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.modifiers = ClassFileConstants.AccAbstract | ClassFileConstants.AccProtected | ExtraCompilerModifiers.AccSemicolonBody;
+ if (override) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, tdParent.get())};
+ out.returnType = new SingleTypeReference(builderGenericName.toCharArray(), 0);
+ return out;
+ }
+
+ private MethodDeclaration generateSelfMethod(EclipseNode builderImplType) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) builderImplType.top().get()).compilationResult);
+ out.selector = SELF_METHOD_NAME;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.modifiers = ClassFileConstants.AccProtected;
+ out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, builderImplType.get())};
+ out.returnType = new SingleTypeReference(builderImplType.getName().toCharArray(), 0);
+ out.statements = new Statement[] {new ReturnStatement(new ThisReference(0, 0), 0, 0)};
+ return out;
+ }
+
+ private MethodDeclaration generateAbstractBuildMethod(EclipseNode tdParent, String methodName, boolean override,
+ String classGenericName, ASTNode source) {
+
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+
+ out.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract | ExtraCompilerModifiers.AccSemicolonBody;
+ out.selector = methodName.toCharArray();
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = new SingleTypeReference(classGenericName.toCharArray(), 0);
+ if (override) out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)};
+ out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return out;
+ }
+
+ private MethodDeclaration generateBuildMethod(EclipseNode tdParent, String name, TypeReference returnType, ASTNode source) {
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) tdParent.top().get()).compilationResult);
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ List<Statement> statements = new ArrayList<Statement>();
+
+ out.modifiers = ClassFileConstants.AccPublic;
+ out.selector = name.toCharArray();
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = returnType;
+ out.annotations = new Annotation[] {makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source)};
+
+ AllocationExpression allocationStatement = new AllocationExpression();
+ allocationStatement.type = copyType(out.returnType);
+ // Use a constructor that only has this builder as parameter.
+ allocationStatement.arguments = new Expression[] {new ThisReference(0, 0)};
+ statements.add(new ReturnStatement(allocationStatement, 0, 0));
+ out.statements = statements.isEmpty() ? null : statements.toArray(new Statement[statements.size()]);
+ out.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
+ return out;
+ }
+
+ private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) {
+ List<Statement> statements = new ArrayList<Statement>();
+
+ for (BuilderFieldData bfd : builderFields) {
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendCleaningCode(bfd.singularData, builderType, statements);
+ }
+ }
+
+ 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;
+ }
+
+ private 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 void generateSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, final String builderGenericName) {
+ boolean deprecate = isFieldDeprecated(bfd.originalFieldNode);
+
+ TypeReferenceMaker returnTypeMaker = new TypeReferenceMaker() {
+ @Override public TypeReference make() {
+ return new SingleTypeReference(builderGenericName.toCharArray(), 0);
+ }
+ };
+
+ StatementMaker returnStatementMaker = new StatementMaker() {
+ @Override public ReturnStatement make() {
+ MessageSend returnCall = new MessageSend();
+ returnCall.receiver = ThisReference.implicitThis();
+ returnCall.selector = SELF_METHOD_NAME;
+ return new ReturnStatement(returnCall, 0, 0);
+ }
+ };
+
+ if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) {
+ generateSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, returnTypeMaker.make(), returnStatementMaker.make(), sourceNode);
+ } else {
+ bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, true, returnTypeMaker, returnStatementMaker);
+ }
+ }
+
+ private void generateSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, TypeReference returnType, Statement returnStatement, EclipseNode sourceNode) {
+ TypeDeclaration td = (TypeDeclaration) builderType.get();
+ AbstractMethodDeclaration[] existing = td.methods;
+ if (existing == null) existing = EMPTY_METHODS;
+ int len = existing.length;
+ FieldDeclaration fd = (FieldDeclaration) fieldNode.get();
+ char[] name = fd.name;
+
+ for (int i = 0; i < len; i++) {
+ if (!(existing[i] instanceof MethodDeclaration)) continue;
+ char[] existingName = existing[i].selector;
+ if (Arrays.equals(name, existingName) && !isTolerate(fieldNode, existing[i])) return;
+ }
+
+ String setterName = fieldNode.getName();
+
+ MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic,
+ sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ injectMethod(builderType, setter);
+ }
+
+ 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;
+ }
+
+ private String generateNonclashingNameFor(String classGenericName, java.util.List<String> typeParamStrings) {
+ if (!typeParamStrings.contains(classGenericName)) return classGenericName;
+ int counter = 2;
+ while (typeParamStrings.contains(classGenericName + counter)) counter++;
+ return classGenericName + counter;
+ }
+
+ private TypeReference[] appendBuilderTypeReferences(TypeParameter[] typeParams, String classGenericName, String builderGenericName) {
+ TypeReference[] typeReferencesToAppend = new TypeReference[2];
+ typeReferencesToAppend[typeReferencesToAppend.length - 2] = new SingleTypeReference(classGenericName.toCharArray(), 0);
+ typeReferencesToAppend[typeReferencesToAppend.length - 1] = new SingleTypeReference(builderGenericName.toCharArray(), 0);
+ return mergeToTypeReferences(typeParams, typeReferencesToAppend);
+ }
+
+ private TypeReference[] getTypeParametersFrom(TypeReference typeRef) {
+ TypeReference[][] typeArgss = null;
+ if (typeRef instanceof ParameterizedQualifiedTypeReference) {
+ typeArgss = ((ParameterizedQualifiedTypeReference) typeRef).typeArguments;
+ } else if (typeRef instanceof ParameterizedSingleTypeReference) {
+ typeArgss = new TypeReference[][] {((ParameterizedSingleTypeReference) typeRef).typeArguments};
+ }
+ TypeReference[] typeArgs = new TypeReference[0];
+ if (typeArgss != null && typeArgss.length > 0) {
+ typeArgs = typeArgss[typeArgss.length - 1];
+ }
+ return typeArgs;
+ }
+
+ private static SingleTypeReference createTypeReferenceWithTypeParameters(String referenceName, TypeParameter[] typeParams) {
+ if (typeParams.length > 0) {
+ TypeReference[] typerefs = new TypeReference[typeParams.length];
+ for (int i = 0; i < typeParams.length; i++) {
+ typerefs[i] = new SingleTypeReference(typeParams[i].name, 0);
+ }
+ return new ParameterizedSingleTypeReference(referenceName.toCharArray(), typerefs, 0, 0);
+ } else {
+ return new SingleTypeReference(referenceName.toCharArray(), 0);
+ }
+ }
+
+ private TypeReference[] mergeToTypeReferences(TypeParameter[] typeParams, TypeReference[] typeReferencesToAppend) {
+ TypeReference[] typerefs = new TypeReference[typeParams.length + typeReferencesToAppend.length];
+ for (int i = 0; i < typeParams.length; i++) {
+ typerefs[i] = new SingleTypeReference(typeParams[i].name, 0);
+ }
+ for (int i = 0; i < typeReferencesToAppend.length; i++) {
+ typerefs[typeParams.length + i] = typeReferencesToAppend[i];
+ }
+ return typerefs;
+ }
+
+ private TypeReference[] mergeTypeReferences(TypeReference[] refs1, TypeReference[] refs2) {
+ TypeReference[] result = new TypeReference[refs1.length + refs2.length];
+ for (int i = 0; i < refs1.length; i++) result[i] = refs1[i];
+ for (int i = 0; i < refs2.length; i++) result[refs1.length + i] = refs2[i];
+ return result;
+ }
+
+ private EclipseNode findInnerClass(EclipseNode parent, String name) {
+ char[] c = name.toCharArray();
+ for (EclipseNode child : parent.down()) {
+ if (child.getKind() != Kind.TYPE) continue;
+ TypeDeclaration td = (TypeDeclaration) child.get();
+ if (Arrays.equals(td.name, c)) return child;
+ }
+ return null;
+ }
+
+ 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;
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java
index a4ed254a..02a19f8d 100644
--- a/src/core/lombok/eclipse/handlers/HandleToString.java
+++ b/src/core/lombok/eclipse/handlers/HandleToString.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
@@ -24,7 +24,6 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -37,17 +36,17 @@ import lombok.ConfigurationKeys;
import lombok.ToString;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
+import lombok.core.handlers.InclusionExclusionUtils;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
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.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
@@ -70,17 +69,25 @@ import org.mangosdk.spi.ProviderFor;
*/
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleToString extends EclipseAnnotationHandler<ToString> {
- public void checkForBogusFieldNames(EclipseNode type, AnnotationValues<ToString> annotation) {
- if (annotation.isExplicit("exclude")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().exclude()), type, true, false)) {
- annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
- }
- }
- if (annotation.isExplicit("of")) {
- for (int i : createListOfNonExistentFields(Arrays.asList(annotation.getInstance().of()), type, false, false)) {
- annotation.setWarning("of", "This field does not exist.", i);
- }
- }
+ public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
+
+ ToString ann = annotation.getInstance();
+ List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode);
+ if (members == null) return;
+
+ Boolean callSuper = ann.callSuper();
+
+ if (!annotation.isExplicit("callSuper")) callSuper = null;
+
+ Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
+ boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
+ FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
+
+ Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
+ boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
+
+ generateToString(annotationNode.up(), annotationNode, members, includeFieldNames, callSuper, true, fieldAccess);
}
public void generateToStringForType(EclipseNode typeNode, EclipseNode errorNode) {
@@ -94,41 +101,17 @@ 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);
- }
-
- public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) {
- handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
- ToString ann = annotation.getInstance();
- List<String> excludes = Arrays.asList(ann.exclude());
- List<String> includes = Arrays.asList(ann.of());
- EclipseNode typeNode = annotationNode.up();
- Boolean callSuper = ann.callSuper();
-
- if (!annotation.isExplicit("callSuper")) callSuper = null;
- if (!annotation.isExplicit("exclude")) excludes = null;
- if (!annotation.isExplicit("of")) includes = null;
+ Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
+ FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
- if (excludes != null && includes != null) {
- excludes = null;
- annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
- }
-
- checkForBogusFieldNames(typeNode, annotation);
-
- Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
- boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
- FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
-
- Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
- boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
-
- generateToString(typeNode, annotationNode, excludes, includes, includeFieldNames, callSuper, true, fieldAccess);
+ List<Included<EclipseNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null);
+ generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access);
}
- public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes,
- boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
+ public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, ToString.Include>> members,
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
+
TypeDeclaration typeDecl = null;
if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get();
@@ -136,39 +119,20 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
boolean notAClass = (modifiers &
(ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0;
- if (typeDecl == null || notAClass) {
- errorNode.addError("@ToString is only supported on a class or enum.");
- }
-
if (callSuper == null) {
try {
callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
}
- List<EclipseNode> nodesForToString = new ArrayList<EclipseNode>();
- if (includes != null) {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (includes.contains(new String(fieldDecl.name))) nodesForToString.add(child);
- }
- } else {
- for (EclipseNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
- if (!filterField(fieldDecl)) continue;
-
- //Skip excluded fields.
- if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue;
-
- nodesForToString.add(child);
- }
+ if (typeDecl == null || notAClass) {
+ errorNode.addError("@ToString is only supported on a class or enum.");
+ return;
}
switch (methodExists("toString", typeNode, 0)) {
case NOT_EXISTS:
- MethodDeclaration toString = createToString(typeNode, nodesForToString, includeFieldNames, callSuper, errorNode.get(), fieldAccess);
+ MethodDeclaration toString = createToString(typeNode, members, includeFieldNames, callSuper, errorNode.get(), fieldAccess);
injectMethod(typeNode, toString);
break;
case EXISTS_BY_LOMBOK:
@@ -181,8 +145,9 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
}
- public static MethodDeclaration createToString(EclipseNode type, Collection<EclipseNode> fields,
- boolean includeFieldNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+ public static MethodDeclaration createToString(EclipseNode type, Collection<Included<EclipseNode, ToString.Include>> members,
+ boolean includeNames, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+
String typeName = getTypeName(type);
char[] suffix = ")".toCharArray();
String infixS = ", ";
@@ -195,10 +160,13 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
if (callSuper) {
prefix = (typeName + "(super=").toCharArray();
- } else if (fields.isEmpty()) {
+ } else if (members.isEmpty()) {
prefix = (typeName + "()").toCharArray();
- } else if (includeFieldNames) {
- prefix = (typeName + "(" + new String(((FieldDeclaration)fields.iterator().next().get()).name) + "=").toCharArray();
+ } else if (includeNames) {
+ Included<EclipseNode, ToString.Include> firstMember = members.iterator().next();
+ String name = firstMember.getInc() == null ? "" : firstMember.getInc().name();
+ if (name.isEmpty()) name = firstMember.getNode().getName();
+ prefix = (typeName + "(" + name + "=").toCharArray();
} else {
prefix = (typeName + "(").toCharArray();
}
@@ -219,29 +187,35 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
first = false;
}
- for (EclipseNode field : fields) {
- TypeReference fieldType = getFieldType(field, fieldAccess);
- Expression fieldAccessor = createFieldAccessor(field, fieldAccess, source);
+ for (Included<EclipseNode, ToString.Include> member : members) {
+ EclipseNode memberNode = member.getNode();
+
+ TypeReference fieldType = getFieldType(memberNode, fieldAccess);
+ Expression memberAccessor;
+ if (memberNode.getKind() == Kind.METHOD) {
+ memberAccessor = createMethodAccessor(memberNode, source);
+ } else {
+ memberAccessor = createFieldAccessor(memberNode, fieldAccess, source);
+ }
// The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
boolean fieldBaseTypeIsPrimitive = BUILT_IN_TYPES.contains(new String(fieldType.getLastToken()));
+ @SuppressWarnings("unused")
boolean fieldIsPrimitive = fieldType.dimensions() == 0 && fieldBaseTypeIsPrimitive;
boolean fieldIsPrimitiveArray = fieldType.dimensions() == 1 && fieldBaseTypeIsPrimitive;
boolean fieldIsObjectArray = fieldType.dimensions() > 0 && !fieldIsPrimitiveArray;
- @SuppressWarnings("unused")
- boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
Expression ex;
if (fieldIsPrimitiveArray || fieldIsObjectArray) {
MessageSend arrayToString = new MessageSend();
arrayToString.sourceStart = pS; arrayToString.sourceEnd = pE;
arrayToString.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA, TypeConstants.UTIL, "Arrays".toCharArray());
- arrayToString.arguments = new Expression[] { fieldAccessor };
+ arrayToString.arguments = new Expression[] { memberAccessor };
setGeneratedBy(arrayToString.arguments[0], source);
arrayToString.selector = (fieldIsObjectArray ? "deepToString" : "toString").toCharArray();
ex = arrayToString;
} else {
- ex = fieldAccessor;
+ ex = memberAccessor;
}
setGeneratedBy(ex, source);
@@ -254,8 +228,10 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> {
}
StringLiteral fieldNameLiteral;
- if (includeFieldNames) {
- char[] namePlusEqualsSign = (infixS + field.getName() + "=").toCharArray();
+ if (includeNames) {
+ String n = member.getInc() == null ? "" : member.getInc().name();
+ if (n.isEmpty()) n = memberNode.getName();
+ char[] namePlusEqualsSign = (infixS + n + "=").toCharArray();
fieldNameLiteral = new StringLiteral(namePlusEqualsSign, pS, pE, 0);
} else {
fieldNameLiteral = new StringLiteral(infix, pS, pE, 0);
diff --git a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
index 199ce102..959c1d20 100644
--- a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
+++ b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
@@ -49,6 +49,7 @@ 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;
@@ -57,6 +58,7 @@ 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) {
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..2e0338a8 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,11 @@ 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, Collections.<Annotation>emptyList());
+ handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode);
+ handleToString.generateToStringForType(typeNode, annotationNode);
+ handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES,
Collections.<Annotation>emptyList(), annotationNode);
+ handleConstructor.generateExtraNoArgsConstructor(typeNode, annotationNode);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java
index d8ac8df6..c035fc26 100644
--- a/src/core/lombok/eclipse/handlers/HandleWither.java
+++ b/src/core/lombok/eclipse/handlers/HandleWither.java
@@ -36,7 +36,6 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
-import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess;
import lombok.experimental.Wither;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
@@ -127,8 +126,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", "@Wither(onMethod=", annotationNode);
- List<Annotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(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:
diff --git a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
index b42761f4..89964914 100644
--- a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
+++ b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java
@@ -369,11 +369,6 @@ public final class SetGeneratedByVisitor extends ASTVisitor {
return super.visit(node, scope);
}
- @Override public boolean visit(ArrayInitializer node, ClassScope scope) {
- fixPositions(setGeneratedBy(node, source));
- return super.visit(node, scope);
- }
-
@Override public boolean visit(ArrayQualifiedTypeReference node, BlockScope scope) {
fixPositions(setGeneratedBy(node, source));
return super.visit(node, scope);
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
index 242bde1f..f1687c9c 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -34,7 +34,10 @@ import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
+import 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;
@@ -51,13 +54,11 @@ 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) {
@@ -96,21 +97,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
- @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
- TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateClearMethod(returnType, returnStatement, data, builderType);
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
+ generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType);
}
- void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode 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;
@@ -121,10 +114,12 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
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(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ 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++) {
@@ -159,12 +154,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
}
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(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ 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;
@@ -190,12 +186,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
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) {
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable) {
TypeReference varType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
String simpleTypeName = getSimpleTargetTypeName(data);
int agrumentsCount = getTypeArgumentsCount();
@@ -214,14 +211,14 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
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);
+ thisDotField.receiver = getBuilderReference(builderVariable);
invokeBuild.receiver = thisDotField;
}
Expression isNull; {
//this.pluralName == null
FieldReference thisDotField = new FieldReference(data.getPluralName(), 0L);
- thisDotField.receiver = new ThisReference(0, 0);
+ thisDotField.receiver = getBuilderReference(builderVariable);
isNull = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.EQUAL_EQUAL);
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
index 2d8083d3..5f86a4dc 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -31,7 +31,10 @@ import java.util.List;
import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
+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;
@@ -44,14 +47,12 @@ 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) {
@@ -87,26 +88,18 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
- @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.generateMethods(data, builderType, fluent, chain);
+ guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
return;
}
- TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateClearMethod(returnType, returnStatement, data, builderType);
+ generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType);
}
- private void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode 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;
@@ -122,10 +115,11 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
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(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ 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;
@@ -148,12 +142,13 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
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(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ 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;
@@ -178,6 +173,7 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
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
index 576115b0..f512bacf 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSingularizer.java
@@ -45,7 +45,6 @@ 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;
@@ -56,9 +55,9 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu
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) {
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable);
return;
}
@@ -76,7 +75,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu
/* 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);
+ thisDotField.receiver = getBuilderReference(builderVariable);
MessageSend thisDotFieldGet0 = new MessageSend();
thisDotFieldGet0.receiver = thisDotField;
thisDotFieldGet0.selector = new char[] {'g', 'e', 't'};
@@ -97,7 +96,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu
Expression argToUnmodifiable;
/* new j.u.ArrayList<Generics>(this.pluralName); */ {
FieldReference thisDotPluralName = new FieldReference(data.getPluralName(), 0L);
- thisDotPluralName.receiver = new ThisReference(0, 0);
+ thisDotPluralName.receiver = getBuilderReference(builderVariable);
TypeReference targetTypeExpr = new QualifiedTypeReference(JAVA_UTIL_ARRAYLIST, NULL_POSS);
targetTypeExpr = addTypeArgs(1, false, builderType, targetTypeExpr, data.getTypeArgs());
AllocationExpression constructorCall = new AllocationExpression();
@@ -117,7 +116,7 @@ public class EclipseJavaUtilListSingularizer extends EclipseJavaUtilListSetSingu
SwitchStatement switchStat = new SwitchStatement();
switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]);
- switchStat.expression = getSize(builderType, data.getPluralName(), true);
+ switchStat.expression = getSize(builderType, data.getPluralName(), true, builderVariable);
TypeReference localShadowerType = new QualifiedTypeReference(Eclipse.fromQualifiedName(data.getTargetFqn()), NULL_POSS);
localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs());
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
index ef9e2a76..69c2186a 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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.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;
@@ -44,13 +45,11 @@ 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;
@@ -58,6 +57,8 @@ import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer;
import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.StatementMaker;
+import lombok.eclipse.handlers.EclipseSingularsRecipes.TypeReferenceMaker;
@ProviderFor(EclipseSingularizer.class)
public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer {
@@ -132,26 +133,18 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
return Arrays.asList(keyFieldNode, valueFieldNode);
}
- @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, TypeReferenceMaker returnTypeMaker, StatementMaker returnStatementMaker) {
if (useGuavaInstead(builderType)) {
- guavaMapSingularizer.generateMethods(data, builderType, fluent, chain);
+ guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, returnTypeMaker, returnStatementMaker);
return;
}
- TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateSingularMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generatePluralMethod(returnType, returnStatement, data, builderType, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0);
- returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null;
- generateClearMethod(returnType, returnStatement, data, builderType);
+ generateSingularMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generatePluralMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, fluent);
+ generateClearMethod(deprecate, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType);
}
- private void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode 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;
@@ -178,10 +171,12 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
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(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ 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;
@@ -225,12 +220,13 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
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(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) {
+ 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;
@@ -288,21 +284,22 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
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) {
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaMapSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ guavaMapSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable);
return;
}
if (data.getTargetFqn().equals("java.util.Map")) {
- statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap"));
+ statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", builderVariable));
} else {
- statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap"));
+ statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, true, true, false, true, "TreeMap", builderVariable));
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
index 2d16eae0..200e615e 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSetSingularizer.java
@@ -37,16 +37,16 @@ public class EclipseJavaUtilSetSingularizer extends EclipseJavaUtilListSetSingul
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) {
+ @Override public void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName);
+ guavaListSetSingularizer.appendBuildCode(data, builderType, statements, targetVariableName, builderVariable);
return;
}
if (data.getTargetFqn().equals("java.util.Set")) {
- statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, false, "emptySet", "singleton", "LinkedHashSet"));
+ statements.addAll(createJavaUtilSetMapInitialCapacitySwitchStatements(data, builderType, false, "emptySet", "singleton", "LinkedHashSet", builderVariable));
} else {
- statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, false, true, false, true, "TreeSet"));
+ statements.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, false, true, false, true, "TreeSet", builderVariable));
}
}
}
diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
index 6661f4af..8bcfa65d 100644
--- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
+++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java
@@ -90,7 +90,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
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) {
+ protected List<Statement> createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, String builderVariable) {
List<Statement> switchContents = new ArrayList<Statement>();
char[] keyName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName();
@@ -112,7 +112,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
/* !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);
+ thisDotKey.receiver = getBuilderReference(builderVariable);
MessageSend thisDotKeyGet0 = new MessageSend();
thisDotKeyGet0.receiver = thisDotKey;
thisDotKeyGet0.selector = new char[] {'g', 'e', 't'};
@@ -122,7 +122,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
if (mapMode) {
char[] valueName = (new String(data.getPluralName()) + "$value").toCharArray();
FieldReference thisDotValue = new FieldReference(valueName, 0L);
- thisDotValue.receiver = new ThisReference(0, 0);
+ thisDotValue.receiver = getBuilderReference(builderVariable);
MessageSend thisDotValueGet0 = new MessageSend();
thisDotValueGet0.receiver = thisDotValue;
thisDotValueGet0.selector = new char[] {'g', 'e', 't'};
@@ -143,12 +143,12 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
{ // default:
switchContents.add(new CaseStatement(null, 0, 0));
- switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType));
+ switchContents.addAll(createJavaUtilSimpleCreationAndFillStatements(data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, builderVariable));
}
SwitchStatement switchStat = new SwitchStatement();
switchStat.statements = switchContents.toArray(new Statement[switchContents.size()]);
- switchStat.expression = getSize(builderType, keyName, true);
+ switchStat.expression = getSize(builderType, keyName, true, builderVariable);
TypeReference localShadowerType = new QualifiedTypeReference(fromQualifiedName(data.getTargetFqn()), NULL_POSS);
localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs());
@@ -157,7 +157,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
return Arrays.asList(varDefStat, switchStat);
}
- protected List<Statement> createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType) {
+ protected List<Statement> createJavaUtilSimpleCreationAndFillStatements(SingularData data, EclipseNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, String builderVariable) {
char[] varName = mapMode ? (new String(data.getPluralName()) + "$key").toCharArray() : data.getPluralName();
Statement createStat; {
@@ -166,11 +166,11 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
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);
+ Expression lessThanCutoff = new BinaryExpression(getSize(builderType, varName, nullGuard, builderVariable), 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 sizeFormulaLeft = new BinaryExpression(makeIntLiteral(new char[] {'1'}, null), getSize(builderType, varName, nullGuard, builderVariable), OperatorIds.PLUS);
+ Expression sizeFormulaRightLeft = new BinaryExpression(getSize(builderType, varName, nullGuard, builderVariable), 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);
@@ -203,9 +203,9 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
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);
+ thisDotKey.receiver = getBuilderReference(builderVariable);
FieldReference thisDotValue = new FieldReference((new String(data.getPluralName()) + "$value").toCharArray(), 0L);
- thisDotValue.receiver = new ThisReference(0, 0);
+ thisDotValue.receiver = getBuilderReference(builderVariable);
MessageSend keyArg = new MessageSend();
keyArg.receiver = thisDotKey;
keyArg.arguments = new Expression[] {new SingleNameReference(iVar, 0L)};
@@ -219,7 +219,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
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 checkExpr = new BinaryExpression(new SingleNameReference(iVar, 0L), getSize(builderType, varName, nullGuard, builderVariable), 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 {
@@ -228,14 +228,14 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer {
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);
+ thisDotPluralname.receiver = getBuilderReference(builderVariable);
pluralnameDotAddAll.arguments = new Expression[] {thisDotPluralname};
fillStat = pluralnameDotAddAll;
}
if (nullGuard) {
FieldReference thisDotField = new FieldReference(varName, 0L);
- thisDotField.receiver = new ThisReference(0, 0);
+ thisDotField.receiver = getBuilderReference(builderVariable);
Expression cond = new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL);
fillStat = new IfStatement(cond, fillStat, 0, 0);
}
diff --git a/src/core/lombok/experimental/Accessors.java b/src/core/lombok/experimental/Accessors.java
index 7d9fdc5c..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="https://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/Builder.java b/src/core/lombok/experimental/Builder.java
deleted file mode 100644
index 4d5e0f67..00000000
--- a/src/core/lombok/experimental/Builder.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package lombok.experimental;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class
- * that contains a member which is annotated with {@code @Builder}.
- * <p>
- * If a member is annotated, it must be either a constructor or a 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
- * 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 / 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 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="https://projectlombok.org/features/experimental/Builder.html">the project lombok features page for &#64;Builder</a>.
- * <p>
- * <p>
- * Before:
- *
- * <pre>
- * &#064;Builder
- * class Example {
- * private int foo;
- * private final String bar;
- * }
- * </pre>
- *
- * After:
- *
- * <pre>
- * class Example&lt;T&gt; {
- * private T foo;
- * private final String bar;
- *
- * private Example(T foo, String bar) {
- * this.foo = foo;
- * this.bar = bar;
- * }
- *
- * public static &lt;T&gt; ExampleBuilder&lt;T&gt; builder() {
- * return new ExampleBuilder&lt;T&gt;();
- * }
- *
- * public static class ExampleBuilder&lt;T&gt; {
- * private T foo;
- * private String bar;
- *
- * private ExampleBuilder() {}
- *
- * public ExampleBuilder foo(T foo) {
- * this.foo = foo;
- * return this;
- * }
- *
- * public ExampleBuilder bar(String bar) {
- * this.bar = bar;
- * return this;
- * }
- *
- * &#064;java.lang.Override public String toString() {
- * return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")";
- * }
- *
- * public Example build() {
- * return new Example(foo, bar);
- * }
- * }
- * }
- * </pre>
- *
- * @deprecated {@link lombok.Builder} has been promoted to the main package, so use that one instead.
- */
-@Target({TYPE, METHOD, CONSTRUCTOR})
-@Retention(SOURCE)
-@Deprecated
-public @interface Builder {
- /** Name of the method that creates a new builder instance. Default: {@code builder}. */
- String builderMethodName() default "builder";
-
- /** 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.
- * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
- * Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}.
- */
- 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>
- */
- boolean fluent() default true;
-
- /**
- * 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.
- * <p>
- * <strong>Default: true</strong>
- */
- boolean chain() default true;
-}
diff --git a/src/core/lombok/experimental/Delegate.java b/src/core/lombok/experimental/Delegate.java
index d94389b0..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="https://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 3e64cf78..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="https://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 dbc4993b..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="https://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/FieldNameConstants.java b/src/core/lombok/experimental/FieldNameConstants.java
new file mode 100644
index 00000000..31c2970c
--- /dev/null
+++ b/src/core/lombok/experimental/FieldNameConstants.java
@@ -0,0 +1,40 @@
+/*
+ * 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.experimental;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import lombok.AccessLevel;
+
+/**
+ * Generates String constants containing the field name for each field.
+ */
+@Target({ElementType.TYPE, ElementType.FIELD})
+@Retention(RetentionPolicy.SOURCE)
+public @interface FieldNameConstants {
+ lombok.AccessLevel level() default AccessLevel.PUBLIC;
+ String prefix() default " CONFIG DEFAULT ";
+ String suffix() default " CONFIG DEFAULT ";
+}
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/SuperBuilder.java b/src/core/lombok/experimental/SuperBuilder.java
new file mode 100644
index 00000000..26127a5f
--- /dev/null
+++ b/src/core/lombok/experimental/SuperBuilder.java
@@ -0,0 +1,66 @@
+/*
+ * 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.experimental;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import lombok.Singular;
+
+/**
+ * The SuperBuilder annotation creates a so-called 'builder' aspect to the class that is annotated with {@code @SuperBuilder}, but which works well when extending.
+ * It is similar to {@code @Builder}, except it is only legal on types, is less configurable, but allows you to {@code extends} other builder-able classes.
+ * <p>
+ * All classes in the hierarchy must be annotated with {@code @SuperBuilder}.
+ * <p>
+ * Lombok generates 2 inner 'builder' classes, which extend the parent class' builder class (unless your class doesn't have an extends clause).
+ * Lombok also generates a static method named {@code builder()}, and a protected constructor that takes 1 argument of the builderclass type.
+ * <p>
+ * The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
+ * 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.
+ * <p>
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/SuperBuilder">the project lombok features page for &#64;SuperBuilder</a>.
+ *
+ * @see Singular
+ */
+@Target(TYPE)
+@Retention(SOURCE)
+public @interface SuperBuilder {
+ /** @return Name of the method that creates a new builder instance. Default: {@code builder}. */
+ String builderMethodName() default "builder";
+
+ /** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */
+ String buildMethodName() default "build";
+
+ // toBuilder also requires a two-stage system where each class gets its own toBuilder but calls on a second method (and also calls parentclass's method)
+ // to fill the builder, as this class does not know what fields to pass on to the builder. Let's consider this, but only for milestone 2.
+ /*
+ * If true, generate an instance method to obtain a builder that is initialized with the values of this instance.
+ *
+ * @return Whether to generate a {@code toBuilder()} method.
+ */
+// boolean toBuilder() default false;
+}
diff --git a/src/core/lombok/experimental/Wither.java b/src/core/lombok/experimental/Wither.java
index b4131187..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="https://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 d85e2969..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="https://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 777f4b35..fa3d6f09 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="https://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,22 +46,21 @@ 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
+ * @see lombok.extern.flogger.Flogger &#64;Flogger
*/
@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/experimental/Value.java b/src/core/lombok/extern/flogger/Flogger.java
index 46ec7a05..ecbfd28c 100644
--- a/src/core/lombok/experimental/Value.java
+++ b/src/core/lombok/extern/flogger/Flogger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 The Project Lombok Authors.
+ * 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
@@ -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.extern.flogger;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -27,34 +27,36 @@ 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.
+ * Causes lombok to generate a logger field.
* <p>
- * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
* <p>
- * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
+ * Example:
+ * <pre>
+ * &#64;Flogger
+ * public class LogExample {
+ * }
+ * </pre>
*
- * @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.
+ * will generate:
+ *
+ * <pre>
+ * public class LogExample {
+ * private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
+ * }
+ * </pre>
+ *
+ * This annotation is valid for classes and enumerations.<br>
+ * @see <a href="https://google.github.io/flogger/">com.google.common.flogger</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.slf4j.XSlf4j &#64;XSlf4j
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
*/
-@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 "";
+@Target(ElementType.TYPE)
+public @interface Flogger {
}
diff --git a/src/core/lombok/extern/java/Log.java b/src/core/lombok/extern/java/Log.java
index d5515283..9a1ee412 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="https://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 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
+ * @see lombok.extern.flogger.Flogger &#64;Flogger
*/
@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
index 2735290c..684585e0 100644
--- a/src/core/lombok/extern/jbosslog/JBossLog.java
+++ b/src/core/lombok/extern/jbosslog/JBossLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Project Lombok Authors.
+ * 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
@@ -29,7 +29,7 @@ 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.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,22 +46,20 @@ import java.lang.annotation.Target;
* }
* </pre>
*
- * This annotation is valid for classes and enumerations.<br />
- * @see org.jboss.logging.Logger org.jboss.logging.Logger
- * @see org.jboss.logging.Logger#getLogger(java.lang.Class) org.jboss.logging.Logger.getLogger(Class target)
+ * 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.Slf4j &#64;Slf4j
* @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
- * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
+ * @see lombok.extern.flogger.Flogger &#64;Flogger
* */
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface JBossLog {
-
- /**
- * 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/Log4j.java b/src/core/lombok/extern/log4j/Log4j.java
index 6c6ef1da..249ef71a 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="https://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,22 +46,21 @@ 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
+ * @see lombok.extern.flogger.Flogger &#64;Flogger
*/
@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 a1b2e0e7..a6aa90c0 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="https://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,22 +46,21 @@ 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.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
+ * @see lombok.extern.flogger.Flogger &#64;Flogger
*/
@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 efa6105f..347d81d2 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="https://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,22 +46,21 @@ 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
+ * @see lombok.extern.flogger.Flogger &#64;Flogger
*/
@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 ce23ab9e..4d53a1eb 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="https://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.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
+ * @see lombok.extern.flogger.Flogger &#64;Flogger
*/
@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 da61361d..4ca2c050 100644
--- a/src/core/lombok/javac/JavacAST.java
+++ b/src/core/lombok/javac/JavacAST.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 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,11 +28,14 @@ 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.Source;
@@ -46,8 +49,6 @@ 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;
@@ -63,12 +64,12 @@ import com.sun.tools.javac.util.Name;
* 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;
/**
@@ -79,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), PackageName.getPackageName(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);
@@ -104,27 +105,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
return cu.sourcefile == null ? null : cu.sourcefile.toString();
}
- // jdk9 support, types have changed, names stay the same
- static class PackageName {
- private static final Method packageNameMethod;
-
- static {
- Method m = null;
- try {
- m = JCCompilationUnit.class.getDeclaredMethod("getPackageName");
- } catch (Exception e) {}
- packageNameMethod = m;
- }
-
- static String getPackageName(JCCompilationUnit cu) {
- try {
- Object pkg = packageNameMethod.invoke(cu);
- return (pkg instanceof JCFieldAccess || pkg instanceof JCIdent) ? pkg.toString() : null;
- } catch (Exception e) {}
- return null;
- }
- }
-
public Context getContext() {
return context;
}
@@ -146,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;
}
@@ -207,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.
}
@@ -226,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));
@@ -315,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));
}
@@ -333,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>();
@@ -354,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);
@@ -388,28 +396,27 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> {
oldSource = log.useSource(newSource);
if (pos == null) pos = astObject.pos();
}
- if (pos != null && attemptToRemoveErrorsInRange) {
+ if (pos != null && node != null && attemptToRemoveErrorsInRange) {
removeFromDeferredDiagnostics(pos.getStartPosition(), node.getEndPosition(pos));
}
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);
}
}
@@ -447,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 0f789f45..468d8c7b 100644
--- a/src/core/lombok/javac/JavacImportList.java
+++ b/src/core/lombok/javac/JavacImportList.java
@@ -26,7 +26,6 @@ import java.util.Collection;
import lombok.core.ImportList;
import lombok.core.LombokInternalAliasing;
-import lombok.javac.JavacAST.PackageName;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
diff --git a/src/core/lombok/javac/JavacNode.java b/src/core/lombok/javac/JavacNode.java
index 727692ac..2bce6e3a 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
@@ -21,12 +21,17 @@
*/
package lombok.javac;
+import java.lang.annotation.Annotation;
import java.util.List;
+import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
+import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
+import lombok.javac.handlers.JavacHandlerUtil;
+import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
@@ -35,6 +40,7 @@ import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Name;
@@ -51,6 +57,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);
@@ -248,4 +261,70 @@ public class JavacNode extends lombok.core.LombokNode<JavacAST, JavacNode, JCTre
public void addWarning(String message, DiagnosticPosition pos) {
ast.printMessage(Diagnostic.Kind.WARNING, message, null, pos, false);
}
+
+ @Override public boolean hasAnnotation(Class<? extends Annotation> type) {
+ return JavacHandlerUtil.hasAnnotationAndDeleteIfNeccessary(type, this);
+ }
+
+ @Override public <Z extends Annotation> AnnotationValues<Z> findAnnotation(Class<Z> type) {
+ JavacNode annotation = JavacHandlerUtil.findAnnotation(type, this, true);
+ if (annotation == null) return null;
+ return JavacHandlerUtil.createAnnotation(type, annotation);
+ }
+
+ private JCModifiers getModifiers() {
+ if (node instanceof JCClassDecl) return ((JCClassDecl) node).getModifiers();
+ if (node instanceof JCMethodDecl) return ((JCMethodDecl) node).getModifiers();
+ if (node instanceof JCVariableDecl) return ((JCVariableDecl) node).getModifiers();
+ return null;
+ }
+
+ @Override public boolean isStatic() {
+ if (node instanceof JCClassDecl) {
+ JavacNode directUp = directUp();
+ if (directUp == null || directUp.getKind() == Kind.COMPILATION_UNIT) return true;
+ if (!(directUp.get() instanceof JCClassDecl)) return false;
+ JCClassDecl p = (JCClassDecl) directUp.get();
+ long f = p.mods.flags;
+ if ((Flags.INTERFACE & f) != 0) return true;
+ if ((Flags.ENUM & f) != 0) return true;
+ }
+
+ if (node instanceof JCVariableDecl) {
+ JavacNode directUp = directUp();
+ if (directUp != null && directUp.get() instanceof JCClassDecl) {
+ JCClassDecl p = (JCClassDecl) directUp.get();
+ long f = p.mods.flags;
+ if ((Flags.INTERFACE & f) != 0) return true;
+ }
+ }
+
+ JCModifiers mods = getModifiers();
+ if (mods == null) return false;
+ return (mods.flags & Flags.STATIC) != 0;
+ }
+
+ @Override public boolean isEnumMember() {
+ if (getKind() != Kind.FIELD) return false;
+ JCModifiers mods = getModifiers();
+ return mods != null && (Flags.ENUM & mods.flags) != 0;
+ }
+
+ @Override public boolean isTransient() {
+ if (getKind() != Kind.FIELD) return false;
+ JCModifiers mods = getModifiers();
+ return mods != null && (Flags.TRANSIENT & mods.flags) != 0;
+ }
+
+ @Override public int countMethodParameters() {
+ if (getKind() != Kind.METHOD) return 0;
+
+ com.sun.tools.javac.util.List<JCVariableDecl> params = ((JCMethodDecl) node).params;
+ if (params == null) return 0;
+ return params.size();
+ }
+
+ @Override public int getStartPos() {
+ return node.getPreferredPosition();
+ }
}
diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java
index 67dbaac6..8cc239e1 100644
--- a/src/core/lombok/javac/JavacResolution.java
+++ b/src/core/lombok/javac/JavacResolution.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2015 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
@@ -31,6 +31,7 @@ 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;
@@ -59,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;
@@ -142,9 +144,14 @@ public class JavacResolution {
TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker(), node.getContext());
JCTree copy = mirrorMaker.copy(finder.copyAt());
-
- memberEnterAndAttribute(copy, finder.get(), node.getContext());
- 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();
}
@@ -222,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");
}
@@ -261,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);
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 5a3a7def..84bb00e4 100644
--- a/src/core/lombok/javac/apt/EmptyLombokFileObject.java
+++ b/src/core/lombok/javac/apt/EmptyLombokFileObject.java
@@ -57,7 +57,7 @@ class EmptyLombokFileObject implements LombokFileObject {
}
@Override public URI toUri() {
- return URI.create(name);
+ return URI.create("file:///" + (name.startsWith("/") ? name.substring(1) : name));
}
@Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
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
index 6547c143..04b494bf 100644
--- a/src/core/lombok/javac/apt/LombokProcessor.java
+++ b/src/core/lombok/javac/apt/LombokProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -54,6 +54,8 @@ 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;
@@ -68,7 +70,9 @@ import com.sun.tools.javac.util.Context;
@SupportedAnnotationTypes("*")
public class LombokProcessor extends AbstractProcessor {
- private JavacProcessingEnvironment processingEnv;
+ private ProcessingEnvironment processingEnv;
+ private JavacProcessingEnvironment javacProcessingEnv;
+ private JavacFiler javacFiler;
private JavacTransformer transformer;
private Trees trees;
private boolean lombokDisabled = false;
@@ -80,11 +84,14 @@ public class LombokProcessor extends AbstractProcessor {
lombokDisabled = true;
return;
}
-
- this.processingEnv = (JavacProcessingEnvironment) procEnv;
+
+ this.processingEnv = procEnv;
+ this.javacProcessingEnv = getJavacProcessingEnvironment(procEnv);
+ this.javacFiler = getJavacFiler(procEnv.getFiler());
+
placePostCompileAndDontMakeForceRoundDummiesHook();
- transformer = new JavacTransformer(procEnv.getMessager());
- trees = Trees.instance(procEnv);
+ trees = Trees.instance(javacProcessingEnv);
+ transformer = new JavacTransformer(procEnv.getMessager(), trees);
SortedSet<Long> p = transformer.getPriorities();
if (p.isEmpty()) {
this.priorityLevels = new long[] {0L};
@@ -97,11 +104,55 @@ public class LombokProcessor extends AbstractProcessor {
}
}
+ 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.javacProcessingEnv);
+ 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();
+ Context context = javacProcessingEnv.getContext();
disablePartialReparseInNetBeansEditor(context);
try {
Method keyMethod = Context.class.getDeclaredMethod("key", Class.class);
@@ -112,27 +163,52 @@ public class LombokProcessor extends AbstractProcessor {
@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);
+
+ JavaFileManager newFilerManager = new InterceptingJavaFileManager(originalFiler, receiver);
+ ht.put(key, newFilerManager);
Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager");
filerFileManagerField.setAccessible(true);
- filerFileManagerField.set(processingEnv.getFiler(), newFiler);
+ filerFileManagerField.set(javacFiler, newFilerManager);
+
+ replaceFileManagerJdk9(context, newFilerManager);
}
} 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);
+ f.set(javacProcessingEnv, true);
} catch (NoSuchFieldException e) {
// only NetBeans has it
} catch (Throwable t) {
@@ -206,10 +282,10 @@ public class LombokProcessor extends AbstractProcessor {
try {
Field f = JavacProcessingEnvironment.class.getDeclaredField("processorClassLoader");
f.setAccessible(true);
- ClassLoader unwrapped = (ClassLoader) f.get(processingEnv);
+ ClassLoader unwrapped = (ClassLoader) f.get(javacProcessingEnv);
if (unwrapped == null) return;
ClassLoader wrapped = wrapClassLoader(unwrapped);
- f.set(processingEnv, wrapped);
+ f.set(javacProcessingEnv, 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) {
@@ -230,7 +306,10 @@ public class LombokProcessor extends AbstractProcessor {
// 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;
@@ -247,7 +326,7 @@ public class LombokProcessor extends AbstractProcessor {
if (prioOfCu == null || prioOfCu != prio) continue;
cusForThisRound.add(entry.getKey());
}
- transformer.transform(prio, processingEnv.getContext(), cusForThisRound);
+ transformer.transform(prio, javacProcessingEnv.getContext(), cusForThisRound);
}
// Step 3: Push up all CUs to the next level. Set level to null if there is no next level.
@@ -273,20 +352,22 @@ public class LombokProcessor extends AbstractProcessor {
if (newLevels.isEmpty()) return false;
newLevels.retainAll(priorityLevelsRequiringResolutionReset);
- if (!newLevels.isEmpty()){
+ if (!newLevels.isEmpty()) {
// Force a new round to reset resolution. The next round will cause this method (process) to be called again.
- forceNewRound((JavacFiler) processingEnv.getFiler());
+ forceNewRound(randomModuleName, javacFiler);
return false;
}
- // None of the new levels need resolution, so just keep going.
+ // None of the new levels need resolution, so just keep going.
}
}
private int dummyCount = 0;
- private void forceNewRound(JavacFiler filer) {
+ private void forceNewRound(String randomModuleName, JavacFiler filer) {
if (!filer.newFiles()) {
try {
- JavaFileObject dummy = filer.createSourceFile("lombok.dummy.ForceNewRound" + (dummyCount++));
+ 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) {
@@ -297,6 +378,19 @@ public class LombokProcessor extends AbstractProcessor {
}
}
+ 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;
@@ -310,4 +404,57 @@ public class LombokProcessor extends AbstractProcessor {
@Override public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
+
+ /**
+ * This class casts the given processing environment to a JavacProcessingEnvironment. In case of
+ * gradle incremental compilation, the delegate ProcessingEnvironment of the gradle wrapper is returned.
+ */
+ public JavacProcessingEnvironment getJavacProcessingEnvironment(Object procEnv) {
+ if (procEnv instanceof JavacProcessingEnvironment) {
+ return (JavacProcessingEnvironment) procEnv;
+ }
+
+ // try to find a "delegate" field in the object, and use this to try to obtain a JavacProcessingEnvironment
+ for (Class<?> procEnvClass = procEnv.getClass(); procEnvClass != null; procEnvClass = procEnvClass.getSuperclass()) {
+ try {
+ return getJavacProcessingEnvironment(tryGetDelegateField(procEnvClass, procEnv));
+ } catch (final Exception e) {
+ // delegate field was not found, try on superclass
+ }
+ }
+
+ processingEnv.getMessager().printMessage(Kind.WARNING,
+ "Can't get the delegate of the gradle IncrementalProcessingEnvironment. Lombok won't work.");
+ return null;
+ }
+
+ /**
+ * This class returns the given filer as a JavacFiler. In case the case that the filer is no
+ * JavacFiler (e.g. the Gradle IncrementalFiler), its "delegate" field is used to get the JavacFiler
+ * (directly or through a delegate field again)
+ */
+ public JavacFiler getJavacFiler(Object filer) {
+ if (filer instanceof JavacFiler) {
+ return (JavacFiler) filer;
+ }
+
+ // try to find a "delegate" field in the object, and use this to check for a JavacFiler
+ for (Class<?> filerClass = filer.getClass(); filerClass != null; filerClass = filerClass.getSuperclass()) {
+ try {
+ return getJavacFiler(tryGetDelegateField(filerClass, filer));
+ } catch (final Exception e) {
+ // delegate field was not found, try on superclass
+ }
+ }
+
+ processingEnv.getMessager().printMessage(Kind.WARNING,
+ "Can't get a JavacFiler from " + filer.getClass().getName() + ". Lombok won't work.");
+ return null;
+ }
+
+ private Object tryGetDelegateField(Class<?> delegateClass, Object instance) throws Exception {
+ Field field = delegateClass.getDeclaredField("delegate");
+ field.setAccessible(true);
+ return field.get(instance);
+ }
}
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index b83e3d5f..d56d6ac2 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2015 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
@@ -21,7 +21,6 @@
*/
package lombok.javac.handlers;
-import java.lang.annotation.Annotation;
import java.util.ArrayList;
import javax.lang.model.element.Modifier;
@@ -55,10 +54,12 @@ import lombok.Builder;
import lombok.Builder.ObtainVia;
import lombok.ConfigurationKeys;
import lombok.Singular;
+import lombok.ToString;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.experimental.NonFinal;
import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
@@ -75,6 +76,8 @@ import static lombok.javac.JavacTreeMaker.TypeTag.*;
@ProviderFor(JavacAnnotationHandler.class)
@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
public class HandleBuilder extends JavacAnnotationHandler<Builder> {
+ private 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;
@@ -85,9 +88,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
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>();
}
@@ -116,9 +122,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (!checkName("builderClassName", builderClassName, annotationNode)) return;
}
- @SuppressWarnings("deprecation")
- Class<? extends Annotation> oldExperimentalBuilder = lombok.experimental.Builder.class;
- deleteAnnotationIfNeccessary(annotationNode, Builder.class, oldExperimentalBuilder);
+ deleteAnnotationIfNeccessary(annotationNode, Builder.class, "lombok.experimental.Builder");
JavacNode parent = annotationNode.up();
@@ -137,25 +141,46 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
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;
+ 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(), false, 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;
@@ -187,7 +212,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
thrownExceptions = jmd.thrown;
nameOfBuilderMethod = jmd.name;
if (returnType instanceof JCTypeApply) {
- returnType = ((JCTypeApply) returnType).clazz;
+ returnType = cloneType(tdParent.getTreeMaker(), returnType, ast, annotationNode.getContext());
}
if (builderClassName.isEmpty()) {
if (returnType instanceof JCFieldAccess) {
@@ -207,7 +232,16 @@ 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";
@@ -228,11 +262,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
tpOnRet = ((JCTypeApply) fullReturnType).arguments;
}
- if (returnType instanceof JCIdent) {
- simpleName = ((JCIdent) returnType).name;
+ JCExpression namingType = returnType;
+ if (returnType instanceof JCTypeApply) namingType = ((JCTypeApply) returnType).clazz;
+
+ if (namingType instanceof JCIdent) {
+ simpleName = ((JCIdent) namingType).name;
pkg = null;
- } else if (returnType instanceof JCFieldAccess) {
- JCFieldAccess jcfa = (JCFieldAccess) returnType;
+ } else if (namingType instanceof JCFieldAccess) {
+ JCFieldAccess jcfa = (JCFieldAccess) namingType;
simpleName = jcfa.name;
pkg = unpack(jcfa.selected);
if (pkg.startsWith("ERR:")) {
@@ -241,7 +278,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return;
}
} else {
- annotationNode.addError("Expected a (parameterized) type here instead of a " + returnType.getClass().getName());
+ annotationNode.addError("Expected a (parameterized) type here instead of a " + namingType.getClass().getName());
return;
}
@@ -270,8 +307,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (pos == -1 || tpOnType.size() <= pos) {
- annotationNode.addError("**" + returnType.getClass().toString());
-// 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.");
+ 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);
@@ -291,6 +327,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
bfd.rawName = raw.name;
bfd.type = raw.vartype;
bfd.singularData = getSingularData(param);
+ bfd.originalFieldNode = param;
addObtainVia(bfd, param);
builderFields.add(bfd);
}
@@ -298,7 +335,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JavacNode builderType = findInnerClass(tdParent, builderClassName);
if (builderType == null) {
- builderType = makeBuilderClass(isStatic, 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)) {
@@ -320,7 +357,6 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
}
}
-
}
for (BuilderFieldData bfd : builderFields) {
@@ -350,7 +386,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, null, annotationNode);
+ JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, annotationNode);
if (cd != null) injectMethod(builderType, cd);
}
@@ -359,15 +395,18 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning);
+ 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>();
+ java.util.List<Included<JavacNode, ToString.Include>> fieldNodes = new ArrayList<Included<JavacNode, ToString.Include>>();
for (BuilderFieldData bfd : builderFields) {
- fieldNodes.addAll(bfd.createdFields);
+ for (JavacNode f : bfd.createdFields) {
+ fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true));
+ }
}
+
JCMethodDecl md = HandleToString.createToString(builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast);
if (md != null) injectMethod(builderType, md);
}
@@ -375,7 +414,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
- JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams);
+ JCMethodDecl md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, annotationNode, tdParent, typeParams);
recursiveSetGeneratedBy(md, ast, annotationNode.getContext());
if (md != null) injectMethod(tdParent, md);
}
@@ -479,9 +518,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
}
}
- statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, false))));
+ 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(maker, CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ 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);
@@ -496,7 +535,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
*/
}
- private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) {
+ 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;
@@ -511,31 +550,33 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
for (BuilderFieldData bfd : builderFields) {
if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
- bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name);
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name, "this");
}
}
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
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 (addCleaning) {
- statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true))));
+ 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);
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 callee = maker.Ident(((JCClassDecl) type.up().get()).name);
- if (!isStatic)
- callee = maker.Select(callee, type.up().toName("this"));
+ 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))) {
@@ -550,7 +591,19 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
}
- public JCMethodDecl generateBuilderMethod(boolean isStatic, 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>();
@@ -564,7 +617,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
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(maker, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ 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 void generateBuilderFields(JavacNode builderType, java.util.List<BuilderFieldData> builderFields, JCTree source) {
@@ -574,48 +627,58 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (child.getKind() == Kind.FIELD) existing.add(child);
}
- top:
for (int i = len - 1; i >= 0; i--) {
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)) {
- bfd.createdFields.add(exists);
- continue top;
- }
+ if (n.equals(bfd.name)) field = exists;
+ if (n.equals(bfd.nameOfSetFlag)) setFlag = exists;
}
JavacTreeMaker maker = builderType.getTreeMaker();
- JCModifiers mods = maker.Modifiers(Flags.PRIVATE);
- JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null);
- bfd.createdFields.add(injectFieldAndMarkGenerated(builderType, newField));
+ 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);
}
}
}
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, fieldNode.createdFields.get(0), source, fluent, chain);
+ makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, fluent, chain);
} else {
- fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, builderType, source.get(), fluent, chain);
+ fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain);
}
}
- private void makeSimpleSetterMethodForBuilder(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;
+ JCMethodDecl methodDecl = (JCMethodDecl) child.get();
+ Name existingName = methodDecl.name;
+ if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return;
}
String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
JavacTreeMaker maker = fieldNode.getTreeMaker();
- JCMethodDecl newMethod = 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);
}
@@ -628,12 +691,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return null;
}
- public JavacNode makeBuilderClass(boolean isStatic, 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();
int modifiers = Flags.PUBLIC;
if (isStatic) modifiers |= Flags.STATIC;
JCModifiers mods = maker.Modifiers(modifiers);
- JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(maker, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
+ JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), null, List.<JCExpression>nil(), List.<JCTree>nil());
return injectType(tdParent, builder);
}
diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java
new file mode 100644
index 00000000..af45a620
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleBuilderDefault.java
@@ -0,0 +1,51 @@
+/*
+ * 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.experimental.SuperBuilder;
+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)
+ && !hasAnnotation(SuperBuilder.class, classWithAnnotatedField)) {
+ annotationNode.addWarning("@Builder.Default requires @Builder or @SuperBuilder 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 4a4ec49c..32eb43b2 100644
--- a/src/core/lombok/javac/handlers/HandleConstructor.java
+++ b/src/core/lombok/javac/handlers/HandleConstructor.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
@@ -22,25 +22,14 @@
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.javac.JavacAnnotationHandler;
-import lombok.javac.JavacNode;
-import lombok.javac.JavacTreeMaker;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
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.JCBlock;
@@ -58,9 +47,26 @@ import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.ConfigurationKeys;
+import lombok.NoArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.delombok.LombokOptionsFactory;
+import lombok.javac.Javac;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
+
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");
@@ -68,19 +74,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();
boolean force = ann.force();
List<JavacNode> fields = force ? findFinalFields(typeNode) : List.<JavacNode>nil();
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, null, annotationNode);
+ 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");
@@ -88,19 +96,16 @@ 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), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
+ handleConstructor.generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode);
}
}
@@ -131,6 +136,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");
@@ -138,22 +145,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), false, 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;
@@ -165,7 +173,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();
}
@@ -184,19 +192,33 @@ public class HandleConstructor {
return true;
}
- public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
- generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, source);
- }
-
public enum SkipIfConstructorExists {
YES, NO, I_AM_BUILDER;
}
+ public void generateExtraNoArgsConstructor(JavacNode typeNode, JavacNode source) {
+ if (!isDirectDescendantOfObject(typeNode)) return;
+
+ Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.NO_ARGS_CONSTRUCTOR_EXTRA_PRIVATE);
+ if (v == null || !v) return;
+
+ List<JavacNode> fields = findFinalFields(typeNode);
+ generate(typeNode, AccessLevel.PRIVATE, List.<JCAnnotation>nil(), fields, true, null, SkipIfConstructorExists.NO, source, true);
+ }
+
+ public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
+ generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, source);
+ }
+
public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
- generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), false, 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, boolean allToDefault, 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) {
+ generate(typeNode, level, onConstructor, fields, allToDefault, staticName, skipIfConstructorExists, source, false);
+ }
+
+ private void generate(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source, boolean noArgs) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
@@ -225,14 +247,49 @@ public class HandleConstructor {
}
}
- JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, suppressConstructorProperties, source);
- injectMethod(typeNode, constr);
+ if (noArgs && noArgsConstructorExists(typeNode)) return;
+
+ 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) {
+ 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);
+ injectMethod(typeNode, staticConstr, argTypes_, returnType);
}
}
+ private static boolean noArgsConstructorExists(JavacNode node) {
+ node = upToTypeNode(node);
+
+ if (node != null && node.get() instanceof JCClassDecl) {
+ for (JCTree def : ((JCClassDecl) node.get()).defs) {
+ if (def instanceof JCMethodDecl) {
+ JCMethodDecl md = (JCMethodDecl) def;
+ if (md.name.contentEquals("<init>") && md.params.size() == 0) return true;
+ }
+ }
+ }
+
+ for (JavacNode child : node.down()) {
+ if (annotationTypeMatches(NoArgsConstructor.class, child)) return true;
+ if (annotationTypeMatches(RequiredArgsConstructor.class, child) && findRequiredFields(node).isEmpty()) return true;
+ if (annotationTypeMatches(AllArgsConstructor.class, child) && findAllFields(node).isEmpty()) return true;
+ }
+
+ return false;
+ }
+
public static void addConstructorProperties(JCModifiers mods, JavacNode node, List<JavacNode> fields) {
if (fields.isEmpty()) return;
JavacTreeMaker maker = node.getTreeMaker();
@@ -247,18 +304,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 allToDefault, 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>();
@@ -276,7 +335,7 @@ public class HandleConstructor {
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, source);
+ JCStatement nullCheck = generateNullCheck(maker, fieldNode, param, source);
if (nullCheck != null) nullChecks.append(nullCheck);
}
}
@@ -286,7 +345,7 @@ public class HandleConstructor {
}
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
- if (!allToDefault && !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));
diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java
index 9ecf8754..15c9c9e7 100644
--- a/src/core/lombok/javac/handlers/HandleData.java
+++ b/src/core/lombok/javac/handlers/HandleData.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
@@ -34,12 +34,19 @@ import lombok.javac.handlers.HandleConstructor.SkipIfConstructorExists;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.util.List;
/**
* Handles the {@code lombok.Data} annotation for javac.
*/
@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");
@@ -55,10 +62,11 @@ 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);
+ handleConstructor.generateExtraNoArgsConstructor(typeNode, annotationNode);
+ handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true, List.<JCAnnotation>nil());
+ handleSetter.generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ 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 680e7745..49bef769 100644
--- a/src/core/lombok/javac/handlers/HandleDelegate.java
+++ b/src/core/lombok/javac/handlers/HandleDelegate.java
@@ -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));
}
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 2265bf36..aa0fe633 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.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,7 +21,7 @@
*/
package lombok.javac.handlers;
-import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.core.handlers.HandlerUtil.handleFlagUsage;
import static lombok.javac.Javac.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
@@ -29,19 +29,6 @@ import java.util.ArrayList;
import java.util.Arrays;
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;
-import lombok.javac.JavacAnnotationHandler;
-import lombok.javac.JavacNode;
-import lombok.javac.JavacTreeMaker;
-import lombok.javac.handlers.JavacHandlerUtil.FieldAccess;
-import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
-
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.BoundKind;
@@ -66,6 +53,20 @@ 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.EqualsAndHashCode;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.configuration.CallSuperType;
+import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.HandlerUtil.FieldAccess;
+import lombok.core.handlers.InclusionExclusionUtils;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
+
/**
* Handles the {@code lombok.EqualsAndHashCode} annotation for javac.
*/
@@ -74,45 +75,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
private static final String RESULT_NAME = "result";
private static final String PRIME_NAME = "PRIME";
- public void checkForBogusFieldNames(JavacNode type, AnnotationValues<EqualsAndHashCode> annotation) {
- if (annotation.isExplicit("exclude")) {
- for (int i : createListOfNonExistentFields(List.from(annotation.getInstance().exclude()), type, true, true)) {
- annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
- }
- }
- if (annotation.isExplicit("of")) {
- for (int i : createListOfNonExistentFields(List.from(annotation.getInstance().of()), type, false, false)) {
- annotation.setWarning("of", "This field does not exist.", i);
- }
- }
- }
-
@Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
deleteAnnotationIfNeccessary(annotationNode, EqualsAndHashCode.class);
EqualsAndHashCode ann = annotation.getInstance();
- List<String> excludes = List.from(ann.exclude());
- List<String> includes = List.from(ann.of());
+ java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode);
JavacNode typeNode = annotationNode.up();
- List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam=", annotationNode);
- checkForBogusFieldNames(typeNode, annotation);
+ List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@EqualsAndHashCode(onParam", annotationNode);
Boolean callSuper = ann.callSuper();
if (!annotation.isExplicit("callSuper")) callSuper = null;
- if (!annotation.isExplicit("exclude")) excludes = null;
- if (!annotation.isExplicit("of")) includes = null;
-
- if (excludes != null && includes != null) {
- excludes = null;
- annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
- }
Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
- generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess, onParam);
+ generateMethods(typeNode, annotationNode, members, callSuper, true, fieldAccess, onParam);
}
public void generateEqualsAndHashCodeForType(JavacNode typeNode, JavacNode source) {
@@ -121,10 +100,15 @@ 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;
+
+ java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(typeNode, null, null);
+
+ generateMethods(typeNode, source, members, null, false, access, List.<JCAnnotation>nil());
}
- public void generateMethods(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes,
+ public void generateMethods(JavacNode typeNode, JavacNode source, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members,
Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
boolean notAClass = true;
@@ -138,7 +122,6 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
return;
}
- boolean isDirectDescendantOfObject = true;
boolean implicitCallSuper = callSuper == null;
if (callSuper == null) {
try {
@@ -148,11 +131,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
}
- JCTree extending = Javac.getExtendsClause((JCClassDecl)typeNode.get());
- if (extending != null) {
- String p = extending.toString();
- isDirectDescendantOfObject = p.equals("Object") || p.equals("java.lang.Object");
- }
+ boolean isDirectDescendantOfObject = isDirectDescendantOfObject(typeNode);
if (isDirectDescendantOfObject && callSuper) {
source.addError("Generating equals/hashCode with a supercall to java.lang.Object is pointless.");
@@ -178,29 +157,6 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
}
- ListBuffer<JavacNode> nodesForEquality = new ListBuffer<JavacNode>();
- if (includes != null) {
- for (JavacNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
- if (includes.contains(fieldDecl.name.toString())) nodesForEquality.append(child);
- }
- } else {
- for (JavacNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
- //Skip static fields.
- if ((fieldDecl.mods.flags & Flags.STATIC) != 0) continue;
- //Skip transient fields.
- if ((fieldDecl.mods.flags & Flags.TRANSIENT) != 0) continue;
- //Skip excluded fields.
- if (excludes != null && excludes.contains(fieldDecl.name.toString())) continue;
- //Skip fields that start with $
- if (fieldDecl.name.toString().startsWith("$")) continue;
- nodesForEquality.append(child);
- }
- }
-
boolean isFinal = (((JCClassDecl) typeNode.get()).mods.flags & Flags.FINAL) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
@@ -229,7 +185,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
//fallthrough
}
- JCMethodDecl equalsMethod = createEquals(typeNode, nodesForEquality.toList(), callSuper, fieldAccess, needsCanEqual, source.get(), onParam);
+ JCMethodDecl equalsMethod = createEquals(typeNode, members, callSuper, fieldAccess, needsCanEqual, source.get(), onParam);
+
injectMethod(typeNode, equalsMethod);
if (needsCanEqual && canEqualExists == MemberExistsResult.NOT_EXISTS) {
@@ -237,11 +194,11 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
injectMethod(typeNode, canEqualMethod);
}
- JCMethodDecl hashCodeMethod = createHashCode(typeNode, nodesForEquality.toList(), callSuper, fieldAccess, source.get());
+ JCMethodDecl hashCodeMethod = createHashCode(typeNode, members, callSuper, fieldAccess, source.get());
injectMethod(typeNode, hashCodeMethod);
}
- public JCMethodDecl createHashCode(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+ public JCMethodDecl createHashCode(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
JavacTreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
@@ -254,26 +211,31 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext());
/* final int PRIME = X; */ {
- if (!fields.isEmpty() || callSuper) {
+ if (!members.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("$");
- for (JavacNode fieldNode : fields) {
- JCExpression fType = getFieldType(fieldNode, fieldAccess);
- JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
+ for (Included<JavacNode, EqualsAndHashCode.Include> member : members) {
+ JavacNode memberNode = member.getNode();
+ JCExpression fType = getFieldType(memberNode, fieldAccess);
+ boolean isMethod = memberNode.getKind() == Kind.METHOD;
+
+ JCExpression fieldAccessor = isMethod ? createMethodAccessor(maker, memberNode) : createFieldAccessor(maker, memberNode, fieldAccess);
if (fType instanceof JCPrimitiveTypeTree) {
switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) {
case BOOLEAN:
@@ -282,7 +244,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse())))));
break;
case LONG: {
- Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
+ Name dollarFieldName = memberNode.toName((isMethod ? "$$" : "$") + memberNode.getName());
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))));
}
@@ -296,7 +258,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
break;
case DOUBLE: {
/* longToIntForHashCode(Double.doubleToLongBits(this.fieldName)) */
- Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
+ Name dollarFieldName = memberNode.toName((isMethod ? "$$" : "$") + memberNode.getName());
JCExpression init = maker.Apply(
List.<JCExpression>nil(),
genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"),
@@ -326,7 +288,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* final java.lang.Object $fieldName = this.fieldName; */
/* ($fieldName == null ? NULL_PRIME : $fieldName.hashCode()) */
- Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
+ Name dollarFieldName = memberNode.toName((isMethod ? "$$" : "$") + memberNode.getName());
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")),
@@ -362,29 +324,50 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
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;
}
- public JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JCTree source, List<JCAnnotation> onParam) {
+ 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, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, 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");
@@ -407,27 +390,13 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* if (!(o instanceof Outer.Inner.MyType)) return false; */ {
- JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.Parens(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; */ {
- 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());
- }
+ /* Outer.Inner.MyType<?> other = (Outer.Inner.MyType<?>) o; */ {
+ if (!members.isEmpty() || needsCanEqual) {
+ 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))));
@@ -455,12 +424,13 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
statements.append(maker.If(superNotEqual, returnBool(maker, false), null));
}
- Name thisDollar = typeNode.toName("this$");
- Name otherDollar = typeNode.toName("other$");
- for (JavacNode fieldNode : fields) {
- JCExpression fType = getFieldType(fieldNode, fieldAccess);
- JCExpression thisFieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
- JCExpression otherFieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess, maker.Ident(otherName));
+ for (Included<JavacNode, EqualsAndHashCode.Include> member : members) {
+ JavacNode memberNode = member.getNode();
+ boolean isMethod = memberNode.getKind() == Kind.METHOD;
+
+ JCExpression fType = getFieldType(memberNode, fieldAccess);
+ JCExpression thisFieldAccessor = isMethod ? createMethodAccessor(maker, memberNode) : createFieldAccessor(maker, memberNode, fieldAccess);
+ JCExpression otherFieldAccessor = isMethod ? createMethodAccessor(maker, memberNode, maker.Ident(otherName)) : createFieldAccessor(maker, memberNode, fieldAccess, maker.Ident(otherName));
if (fType instanceof JCPrimitiveTypeTree) {
switch (((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind()) {
case FLOAT:
@@ -491,9 +461,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* 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;
- Name thisDollarFieldName = thisDollar.append(fieldName);
- Name otherDollarFieldName = otherDollar.append(fieldName);
+ Name thisDollarFieldName = memberNode.toName("this" + (isMethod ? "$$" : "$") + memberNode.getName());
+ Name otherDollarFieldName = memberNode.toName("other" + (isMethod ? "$$" : "$") + memberNode.getName());
statements.append(maker.VarDef(maker.Modifiers(finalFlag), thisDollarFieldName, genJavaLangTypeRef(typeNode, "Object"), thisFieldAccessor));
statements.append(maker.VarDef(maker.Modifiers(finalFlag), otherDollarFieldName, genJavaLangTypeRef(typeNode, "Object"), otherFieldAccessor));
@@ -532,7 +501,7 @@ 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());
}
diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
index 12c22059..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-2015 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
@@ -84,14 +84,16 @@ public class HandleFieldDefaults extends JavacASTAdapter {
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)) {
- if ((field.mods.flags & Flags.STATIC) == 0 || field.init != null) {
+ if ((field.mods.flags & Flags.STATIC) == 0) {
field.mods.flags |= Flags.FINAL;
}
}
diff --git a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java
new file mode 100644
index 00000000..8ff136fc
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java
@@ -0,0 +1,137 @@
+/*
+ * 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.javac.handlers;
+
+import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+
+import lombok.AccessLevel;
+import lombok.ConfigurationKeys;
+import lombok.core.AST.Kind;
+import lombok.core.handlers.HandlerUtil;
+import lombok.core.AnnotationValues;
+import lombok.experimental.FieldNameConstants;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+
+@ProviderFor(JavacAnnotationHandler.class)
+public class HandleFieldNameConstants extends JavacAnnotationHandler<FieldNameConstants> {
+ public void generateFieldNameConstantsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, String prefix, String suffix) {
+ 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)) != 0;
+
+ if (typeDecl == null || notAClass) {
+ errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field.");
+ return;
+ }
+
+ for (JavacNode field : typeNode.down()) {
+ if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level, prefix, suffix);
+ }
+ }
+
+ private void generateFieldNameConstantsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, String prefix, String suffix) {
+ if (hasAnnotation(FieldNameConstants.class, fieldNode)) return;
+ createFieldNameConstantsForField(level, prefix, suffix, fieldNode, fieldNode, false);
+ }
+
+ private boolean fieldQualifiesForFieldNameConstantsGeneration(JavacNode field) {
+ if (field.getKind() != Kind.FIELD) return false;
+ JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
+ if (fieldDecl.name.toString().startsWith("$")) return false;
+ if ((fieldDecl.mods.flags & Flags.STATIC) != 0) return false;
+ return true;
+ }
+
+ public void handle(AnnotationValues<FieldNameConstants> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_NAME_CONSTANTS_FLAG_USAGE, "@FieldNameConstants");
+
+ Collection<JavacNode> fields = annotationNode.upFromAnnotationToFields();
+ deleteAnnotationIfNeccessary(annotationNode, FieldNameConstants.class);
+ deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
+ JavacNode node = annotationNode.up();
+ FieldNameConstants annotatationInstance = annotation.getInstance();
+ AccessLevel level = annotatationInstance.level();
+ String prefix = annotatationInstance.prefix();
+ String suffix = annotatationInstance.suffix();
+ if (prefix.equals(" CONFIG DEFAULT ")) prefix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_PREFIX);
+ if (suffix.equals(" CONFIG DEFAULT ")) suffix = annotationNode.getAst().readConfiguration(ConfigurationKeys.FIELD_NAME_CONSTANTS_SUFFIX);
+ if (prefix == null) prefix = "FIELD_";
+ if (suffix == null) suffix = "";
+ if (node == null) return;
+ switch (node.getKind()) {
+ case FIELD:
+ if (level != AccessLevel.NONE) createFieldNameConstantsForFields(level, prefix, suffix, fields, annotationNode, annotationNode, true);
+ break;
+ case TYPE:
+ if (level == AccessLevel.NONE) {
+ annotationNode.addWarning("type-level '@FieldNameConstants' does not work with AccessLevel.NONE.");
+ return;
+ }
+ generateFieldNameConstantsForType(node, annotationNode, level, prefix, suffix);
+ break;
+ }
+ }
+
+ private void createFieldNameConstantsForFields(AccessLevel level, String prefix, String suffix, Collection<JavacNode> fieldNodes, JavacNode annotationNode, JavacNode errorNode, boolean whineIfExists) {
+ for (JavacNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, prefix, suffix, fieldNode, errorNode, whineIfExists);
+ }
+
+ private void createFieldNameConstantsForField(AccessLevel level, String prefix, String suffix, JavacNode fieldNode, JavacNode source, boolean whineIfExists) {
+ if (fieldNode.getKind() != Kind.FIELD) {
+ source.addError("@FieldNameConstants is only supported on a class, an enum, or a field");
+ return;
+ }
+
+ JCVariableDecl field = (JCVariableDecl) fieldNode.get();
+ String fieldName = field.name.toString();
+ String constantName = prefix + HandlerUtil.camelCaseToConstant(fieldName) + suffix;
+ if (constantName.equals(fieldName)) {
+ fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field.");
+ return;
+ }
+
+ JavacTreeMaker treeMaker = fieldNode.getTreeMaker();
+ JCModifiers modifiers = treeMaker.Modifiers(toJavacModifier(level) | Modifier.STATIC | Modifier.FINAL);
+ JCExpression returnType = chainDots(fieldNode, "java", "lang", "String");
+ JCExpression init = treeMaker.Literal(fieldNode.getName());
+ JCVariableDecl fieldConstant = treeMaker.VarDef(modifiers, fieldNode.toName(constantName), returnType, init);
+ injectField(fieldNode.up(), fieldConstant);
+ }
+} \ No newline at end of file
diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java
index a330dbc1..4fc6155c 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
@@ -41,11 +41,11 @@ import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.JavacTreeMaker.TypeTag;
-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;
@@ -71,7 +71,7 @@ import com.sun.tools.javac.util.Name;
*/
@ProviderFor(JavacAnnotationHandler.class)
public class HandleGetter extends JavacAnnotationHandler<Getter> {
- public void generateGetterForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelGetter) {
+ public void generateGetterForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelGetter, List<JCAnnotation> onMethod) {
if (checkForTypeLevelGetter) {
if (hasAnnotation(Getter.class, typeNode)) {
//The annotation will make it happen, so we can skip it.
@@ -90,11 +90,11 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
}
for (JavacNode field : typeNode.down()) {
- if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, errorNode.get(), level, false);
+ if (fieldQualifiesForGetterGeneration(field)) generateGetterForField(field, errorNode.get(), level, false, onMethod);
}
}
- 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 $
@@ -119,12 +119,12 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
* @param fieldNode The node representing the field you want a getter for.
* @param pos The node responsible for generating the getter (the {@code @Data} or {@code @Getter} annotation).
*/
- public void generateGetterForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean lazy) {
+ public void generateGetterForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean lazy, List<JCAnnotation> onMethod) {
if (hasAnnotation(Getter.class, fieldNode)) {
//The annotation will make it happen, so we can skip it.
return;
}
- createGetterForField(level, fieldNode, fieldNode, false, lazy, List.<JCAnnotation>nil());
+ createGetterForField(level, fieldNode, fieldNode, false, lazy, onMethod);
}
@Override public void handle(AnnotationValues<Getter> annotation, JCAnnotation ast, JavacNode annotationNode) {
@@ -146,18 +146,15 @@ 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:
createGetterForFields(level, fields, annotationNode, true, lazy, onMethod);
break;
case TYPE:
- if (!onMethod.isEmpty()) {
- annotationNode.addError("'onMethod' is not supported for @Getter on a type.");
- }
if (lazy) annotationNode.addError("'lazy' is not supported for @Getter on a type.");
- generateGetterForType(node, annotationNode, level, false);
+ generateGetterForType(node, annotationNode, level, false, onMethod);
break;
}
}
@@ -182,6 +179,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 +216,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/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java
index d0d709e3..6d742e76 100644
--- a/src/core/lombok/javac/handlers/HandleLog.java
+++ b/src/core/lombok/javac/handlers/HandleLog.java
@@ -91,16 +91,18 @@ public class HandleLog {
// private static final <loggerType> log = <factoryMethod>(<parameter>);
JCExpression loggerType = chainDotsString(typeNode, framework.getLoggerTypeName());
JCExpression factoryMethod = chainDotsString(typeNode, framework.getLoggerFactoryMethodName());
-
+
JCExpression loggerName;
- if (loggerTopic == null || loggerTopic.trim().length() == 0) {
+ if (!framework.passTypeName) {
+ loggerName = null;
+ } else if (loggerTopic == null || loggerTopic.trim().length() == 0) {
loggerName = framework.createFactoryParameter(typeNode, loggingType);
} else {
loggerName = maker.Literal(loggerTopic);
}
-
- JCMethodInvocation factoryMethodCall = maker.Apply(List.<JCExpression>nil(), factoryMethod, List.<JCExpression>of(loggerName));
-
+
+ JCMethodInvocation factoryMethodCall = maker.Apply(List.<JCExpression>nil(), factoryMethod, loggerName != null ? List.<JCExpression>of(loggerName) : List.<JCExpression>nil());
+
JCVariableDecl fieldDecl = recursiveSetGeneratedBy(maker.VarDef(
maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (useStatic ? Flags.STATIC : 0)),
typeNode.toName(logFieldName), loggerType, factoryMethodCall), source, typeNode.getContext());
@@ -186,6 +188,17 @@ public class HandleLog {
}
}
+ /**
+ * Handles the {@link lombok.extern.flogger.Flogger} annotation for javac.
+ */
+ @ProviderFor(JavacAnnotationHandler.class)
+ public static class HandleFloggerLog extends JavacAnnotationHandler<lombok.extern.flogger.Flogger> {
+ @Override public void handle(AnnotationValues<lombok.extern.flogger.Flogger> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleFlagUsage(annotationNode, ConfigurationKeys.LOG_FLOGGER_FLAG_USAGE, "@Flogger", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log");
+ processAnnotation(LoggingFramework.FLOGGER, annotation, annotationNode, "");
+ }
+ }
+
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"),
@@ -212,17 +225,29 @@ public class HandleLog {
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")
+ JBOSSLOG(lombok.extern.jbosslog.JBossLog.class, "org.jboss.logging.Logger", "org.jboss.logging.Logger.getLogger"),
+
+ // private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
+ FLOGGER(lombok.extern.flogger.Flogger.class, "com.google.common.flogger.FluentLogger", "com.google.common.flogger.FluentLogger.forEnclosingClass", false),
;
private final Class<? extends Annotation> annotationClass;
private final String loggerTypeName;
private final String loggerFactoryName;
+ private final boolean passTypeName;
+
+ LoggingFramework(Class<? extends Annotation> annotationClass, String loggerTypeName, String loggerFactoryName, boolean passTypeName) {
+ this.annotationClass = annotationClass;
+ this.loggerTypeName = loggerTypeName;
+ this.loggerFactoryName = loggerFactoryName;
+ this.passTypeName = passTypeName;
+ }
LoggingFramework(Class<? extends Annotation> annotationClass, String loggerTypeName, String loggerFactoryName) {
this.annotationClass = annotationClass;
this.loggerTypeName = loggerTypeName;
this.loggerFactoryName = loggerFactoryName;
+ this.passTypeName = true;
}
final Class<? extends Annotation> getAnnotationClass() {
diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java
index 3c4329b2..0ddaa7d7 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
@@ -36,11 +36,12 @@ import lombok.javac.Javac;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
-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;
@@ -60,7 +61,7 @@ import com.sun.tools.javac.util.Name;
*/
@ProviderFor(JavacAnnotationHandler.class)
public class HandleSetter extends JavacAnnotationHandler<Setter> {
- public void generateSetterForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelSetter) {
+ public void generateSetterForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelSetter, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
if (checkForTypeLevelSetter) {
if (hasAnnotation(Setter.class, typeNode)) {
//The annotation will make it happen, so we can skip it.
@@ -88,7 +89,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
//Skip final fields.
if ((fieldDecl.mods.flags & Flags.FINAL) != 0) continue;
- generateSetterForField(field, errorNode, level);
+ generateSetterForField(field, errorNode, level, onMethod, onParam);
}
}
@@ -107,13 +108,13 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
* @param fieldNode The node representing the field you want a setter for.
* @param pos The node responsible for generating the setter (the {@code @Data} or {@code @Setter} annotation).
*/
- public void generateSetterForField(JavacNode fieldNode, JavacNode sourceNode, AccessLevel level) {
+ public void generateSetterForField(JavacNode fieldNode, JavacNode sourceNode, AccessLevel level, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
if (hasAnnotation(Setter.class, fieldNode)) {
//The annotation will make it happen, so we can skip it.
return;
}
- createSetterForField(level, fieldNode, sourceNode, false, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+ createSetterForField(level, fieldNode, sourceNode, false, onMethod, onParam);
}
@Override public void handle(AnnotationValues<Setter> annotation, JCAnnotation ast, JavacNode annotationNode) {
@@ -127,17 +128,15 @@ 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:
createSetterForFields(level, fields, annotationNode, true, onMethod, onParam);
break;
case TYPE:
- if (!onMethod.isEmpty()) annotationNode.addError("'onMethod' is not supported for @Setter on a type.");
- if (!onParam.isEmpty()) annotationNode.addError("'onParam' is not supported for @Setter on a type.");
- generateSetterForType(node, annotationNode, level, false);
+ generateSetterForType(node, annotationNode, level, false, onMethod, onParam);
break;
}
}
@@ -154,7 +153,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 +187,37 @@ 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, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
+ JCExpression returnType = null;
+ JCReturn returnStatement = null;
+ if (shouldReturnThis) {
+ returnType = cloneSelfType(field);
+ returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this")));
+ }
+
+ return createSetter(access, deprecate, field, treeMaker, setterName, booleanFieldToSet, returnType, returnStatement, 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, JCExpression methodType, JCStatement returnStatement, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) {
if (setterName == null) return null;
JCVariableDecl fieldDecl = (JCVariableDecl) field.get();
@@ -223,21 +243,18 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
statements.append(treeMaker.Exec(assign));
}
- JCExpression methodType = null;
- if (shouldReturnThis) {
- methodType = cloneSelfType(field);
+ if (booleanFieldToSet != null) {
+ JCAssign setBool = treeMaker.Assign(treeMaker.Ident(booleanFieldToSet), treeMaker.Literal(CTC_BOOLEAN, 1));
+ statements.append(treeMaker.Exec(setBool));
}
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;
+ methodType = treeMaker.Type(Javac.createVoidType(field.getSymbolTable(), CTC_VOID));
+ returnStatement = null;
}
- if (shouldReturnThis) {
- JCReturn returnStatement = treeMaker.Return(treeMaker.Ident(field.toName("this")));
- statements.append(returnStatement);
- }
+ if (returnStatement != null) statements.append(returnStatement);
JCBlock methodBody = treeMaker.Block(0, statements.toList());
List<JCTypeParameter> methodGenericParams = List.nil();
@@ -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/HandleSuperBuilder.java b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
new file mode 100644
index 00000000..38fec2f6
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleSuperBuilder.java
@@ -0,0 +1,738 @@
+/*
+ * 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
+ * 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.Javac.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.ArrayList;
+
+import org.mangosdk.spi.ProviderFor;
+
+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.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.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCTypeApply;
+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.util.List;
+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.ToString;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.core.HandlerPriority;
+import lombok.core.handlers.HandlerUtil;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
+import lombok.experimental.NonFinal;
+import lombok.experimental.SuperBuilder;
+import lombok.javac.Javac;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
+import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
+import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
+import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+
+@ProviderFor(JavacAnnotationHandler.class)
+@HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes.
+public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
+ private static final String SELF_METHOD = "self";
+
+ private static class BuilderFieldData {
+ JCExpression type;
+ Name rawName;
+ Name name;
+ Name nameOfSetFlag;
+ SingularData singularData;
+ ObtainVia obtainVia;
+ JavacNode obtainViaNode;
+ JavacNode originalFieldNode;
+
+ java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
+ }
+
+ @Override
+ public void handle(AnnotationValues<SuperBuilder> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.SUPERBUILDER_FLAG_USAGE, "@SuperBuilder");
+
+ SuperBuilder superbuilderAnnotation = annotation.getInstance();
+ deleteAnnotationIfNeccessary(annotationNode, SuperBuilder.class);
+
+ String builderMethodName = superbuilderAnnotation.builderMethodName();
+ String buildMethodName = superbuilderAnnotation.buildMethodName();
+
+ if (builderMethodName == null) builderMethodName = "builder";
+ if (buildMethodName == null) buildMethodName = "build";
+
+ if (!checkName("builderMethodName", builderMethodName, annotationNode)) return;
+ if (!checkName("buildMethodName", buildMethodName, annotationNode)) return;
+
+ JavacNode tdParent = annotationNode.up();
+
+ java.util.List<BuilderFieldData> builderFields = new ArrayList<BuilderFieldData>();
+ JCExpression returnType;
+ List<JCTypeParameter> typeParams = List.nil();
+ List<JCExpression> thrownExceptions = List.nil();
+ List<JCExpression> superclassTypeParams = List.nil();
+
+ boolean addCleaning = false;
+
+ if (!(tdParent.get() instanceof JCClassDecl)) {
+ annotationNode.addError("@SuperBuilder is only supported on types.");
+ return;
+ }
+
+ // Gather all fields of the class that should be set by the builder.
+ JCClassDecl td = (JCClassDecl) tdParent.get();
+ ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>();
+ boolean valuePresent = (hasAnnotation(lombok.Value.class, tdParent) || hasAnnotation("lombok.experimental.Value", tdParent));
+ for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) {
+ JCVariableDecl fd = (JCVariableDecl) fieldNode.get();
+ 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("@SuperBuilder 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.nameOfSetFlag = tdParent.toName(bfd.name + "$set");
+ // The @Builder annotation removes the initializing expression on the field and moves
+ // it to a method called "$default$FIELDNAME". This method is then called upon building.
+ // We do NOT do this, because this is unexpected and may lead to bugs when using other
+ // constructors (see, e.g., issue #1347).
+ // Instead, we keep the init expression and only set a new value in the builder-based
+ // constructor if it was set in the builder. Drawback is that the init expression is
+ // always executed, even if it was unnecessary because its value is overwritten by the
+ // builder.
+ // TODO: Once the issue is resolved in @Builder, we can adapt the solution here.
+ }
+ addObtainVia(bfd, fieldNode);
+ builderFields.add(bfd);
+ allFields.append(fieldNode);
+ }
+
+ // Set the names of the builder classes.
+ String builderClassName = td.name.toString() + "Builder";
+ String builderImplClassName = builderClassName + "Impl";
+ JCTree extendsClause = Javac.getExtendsClause(td);
+ JCExpression superclassBuilderClassExpression = null;
+ if (extendsClause instanceof JCTypeApply) {
+ // Remember the type arguments, because we need them for the extends clause of our abstract builder class.
+ superclassTypeParams = ((JCTypeApply) extendsClause).getTypeArguments();
+ // A class name with a generics type, e.g., "Superclass<A>".
+ extendsClause = ((JCTypeApply) extendsClause).getType();
+ }
+ if (extendsClause instanceof JCFieldAccess) {
+ Name superclassClassName = ((JCFieldAccess)extendsClause).getIdentifier();
+ String superclassBuilderClassName = superclassClassName + "Builder";
+ superclassBuilderClassExpression = tdParent.getTreeMaker().Select((JCFieldAccess) extendsClause,
+ tdParent.toName(superclassBuilderClassName));
+ } else if (extendsClause != null) {
+ String superclassBuilderClassName = extendsClause.toString() + "Builder";
+ superclassBuilderClassExpression = chainDots(tdParent, extendsClause.toString(), superclassBuilderClassName);
+ }
+ // If there is no superclass, superclassBuilderClassExpression is still == null at this point.
+ // You can use it to check whether to inherit or not.
+
+ returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
+ typeParams = td.typarams;
+
+ // <C, B> are the generics for our builder.
+ String classGenericName = "C";
+ String builderGenericName = "B";
+ // If these generics' names collide with any generics on the annotated class, modify them.
+ // For instance, if there are generics <B, B2, C> on the annotated class, use "C2" and "B3" for our builder.
+ java.util.List<String> typeParamStrings = new ArrayList<String>();
+ for (JCTypeParameter typeParam : typeParams) typeParamStrings.add(typeParam.getName().toString());
+ classGenericName = generateNonclashingNameFor(classGenericName, typeParamStrings);
+ builderGenericName = generateNonclashingNameFor(builderGenericName, typeParamStrings);
+
+ thrownExceptions = List.nil();
+
+ // Check validity of @ObtainVia fields, and add check if adding cleaning for @Singular is necessary.
+ 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;
+ }
+ }
+ }
+
+ // Create the abstract builder class.
+ JavacNode builderType = findInnerClass(tdParent, builderClassName);
+ if (builderType == null) {
+ builderType = generateBuilderAbstractClass(annotationNode, tdParent, builderClassName, superclassBuilderClassExpression,
+ typeParams, superclassTypeParams, ast, classGenericName, builderGenericName);
+ } else {
+ annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
+ return;
+ }
+
+ // Generate the fields in the abstract builder class that hold the values for the instance.
+ 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);
+ }
+
+ // Generate abstract self() and build() methods in the abstract builder.
+ injectMethod(builderType, generateAbstractSelfMethod(tdParent, superclassBuilderClassExpression != null, builderGenericName));
+ injectMethod(builderType, generateAbstractBuildMethod(tdParent, buildMethodName, superclassBuilderClassExpression != null, classGenericName));
+
+ // Create the setter methods in the abstract builder.
+ for (BuilderFieldData bfd : builderFields) {
+ generateSetterMethodsForBuilder(builderType, bfd, annotationNode, builderGenericName);
+ }
+
+ // Create the toString() method for the abstract builder.
+ java.util.List<Included<JavacNode, ToString.Include>> fieldNodes = new ArrayList<Included<JavacNode, ToString.Include>>();
+ for (BuilderFieldData bfd : builderFields) {
+ for (JavacNode f : bfd.createdFields) {
+ fieldNodes.add(new Included<JavacNode, ToString.Include>(f, null, true));
+ }
+ }
+
+ // Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder.
+ JCMethodDecl toStringMethod = HandleToString.createToString(builderType, fieldNodes, true, superclassBuilderClassExpression != null, FieldAccess.ALWAYS_FIELD, ast);
+ if (toStringMethod != null) injectMethod(builderType, toStringMethod);
+
+ // If clean methods are requested, add them now.
+ if (addCleaning) injectMethod(builderType, generateCleanMethod(builderFields, builderType, ast));
+
+ recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
+
+ if ((td.mods.flags & Flags.ABSTRACT) == 0) {
+ // Only non-abstract classes get the Builder implementation.
+
+ // Create the builder implementation class.
+ JavacNode builderImplType = findInnerClass(tdParent, builderImplClassName);
+ if (builderImplType == null) {
+ builderImplType = generateBuilderImplClass(annotationNode, tdParent, builderImplClassName, builderClassName, typeParams, ast);
+ } else {
+ annotationNode.addError("@SuperBuilder does not support customized builders. Use @Builder instead.");
+ return;
+ }
+
+ // Create a simple constructor for the BuilderImpl class.
+ JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), builderImplType, List.<JavacNode>nil(), false, annotationNode);
+ if (cd != null) injectMethod(builderImplType, cd);
+
+ // Create the self() and build() methods in the BuilderImpl.
+ injectMethod(builderImplType, generateSelfMethod(builderImplType));
+ injectMethod(builderImplType, generateBuildMethod(buildMethodName, returnType, builderImplType, thrownExceptions));
+
+ recursiveSetGeneratedBy(builderImplType.get(), ast, annotationNode.getContext());
+ }
+
+ // Generate a constructor in the annotated class that takes a builder as argument.
+ generateBuilderBasedConstructor(tdParent, typeParams, builderFields, annotationNode, builderClassName,
+ superclassBuilderClassExpression != null);
+
+ if ((td.mods.flags & Flags.ABSTRACT) == 0) {
+ // Only non-abstract classes get the Builder implementation and the builder() method.
+
+ // Add the builder() method to the annotated class.
+ // Allow users to specify their own builder() methods, e.g., to provide default values.
+ if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) {
+ JCMethodDecl builderMethod = generateBuilderMethod(builderMethodName, builderClassName, builderImplClassName, annotationNode, tdParent, typeParams);
+ recursiveSetGeneratedBy(builderMethod, ast, annotationNode.getContext());
+ if (builderMethod != null) injectMethod(tdParent, builderMethod);
+ }
+ }
+ }
+
+ /**
+ * Creates and returns the abstract builder class and injects it into the annotated class.
+ */
+ private JavacNode generateBuilderAbstractClass(JavacNode source, JavacNode tdParent, String builderClass,
+ JCExpression superclassBuilderClassExpression, List<JCTypeParameter> typeParams,
+ List<JCExpression> superclassTypeParams, JCAnnotation ast, String classGenericName, String builderGenericName) {
+
+ JavacTreeMaker maker = tdParent.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.ABSTRACT | Flags.PUBLIC);
+
+ // Keep any type params of the annotated class.
+ ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>();
+ allTypeParams.addAll(copyTypeParams(source, typeParams));
+ // Add builder-specific type params required for inheritable builders.
+ // 1. The return type for the build() method, named "C", which extends the annotated class.
+ JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName()));
+ if (typeParams.nonEmpty()) {
+ // Add type params of the annotated class.
+ annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList());
+ }
+ allTypeParams.add(maker.TypeParameter(tdParent.toName(classGenericName), List.<JCExpression>of(annotatedClass)));
+ // 2. The return type for all setter methods, named "B", which extends this builder class.
+ Name builderClassName = tdParent.toName(builderClass);
+ ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(typeParams, maker);
+ typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName)));
+ typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName)));
+ JCTypeApply typeApply = maker.TypeApply(maker.Ident(builderClassName), typeParamsForBuilder.toList());
+ allTypeParams.add(maker.TypeParameter(tdParent.toName(builderGenericName), List.<JCExpression>of(typeApply)));
+
+ JCExpression extending = null;
+ if (superclassBuilderClassExpression != null) {
+ // If the annotated class extends another class, we want this builder to extend the builder of the superclass.
+ // 1. Add the type parameters of the superclass.
+ typeParamsForBuilder = getTypeParamExpressions(superclassTypeParams, maker);
+ // 2. Add the builder type params <C, B>.
+ typeParamsForBuilder.add(maker.Ident(tdParent.toName(classGenericName)));
+ typeParamsForBuilder.add(maker.Ident(tdParent.toName(builderGenericName)));
+ extending = maker.TypeApply(superclassBuilderClassExpression, typeParamsForBuilder.toList());
+ }
+
+ JCClassDecl builder = maker.ClassDef(mods, builderClassName, allTypeParams.toList(), extending, List.<JCExpression>nil(), List.<JCTree>nil());
+ return injectType(tdParent, builder);
+ }
+
+ /**
+ * Creates and returns the concrete builder implementation class and injects it into the annotated class.
+ */
+ private JavacNode generateBuilderImplClass(JavacNode source, JavacNode tdParent, String builderImplClass, String builderAbstractClass, List<JCTypeParameter> typeParams, JCAnnotation ast) {
+ JavacTreeMaker maker = tdParent.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.STATIC | Flags.PRIVATE | Flags.FINAL);
+
+ // Extend the abstract builder.
+ JCExpression extending = maker.Ident(tdParent.toName(builderAbstractClass));
+ // Add any type params of the annotated class.
+ ListBuffer<JCTypeParameter> allTypeParams = new ListBuffer<JCTypeParameter>();
+ allTypeParams.addAll(copyTypeParams(source, typeParams));
+ // Add builder-specific type params required for inheritable builders.
+ // 1. The return type for the build() method (named "C" in the abstract builder), which is the annotated class.
+ JCExpression annotatedClass = maker.Ident(tdParent.toName(tdParent.getName()));
+ if (typeParams.nonEmpty()) {
+ // Add type params of the annotated class.
+ annotatedClass = maker.TypeApply(annotatedClass, getTypeParamExpressions(typeParams, maker).toList());
+ }
+ // 2. The return type for all setter methods (named "B" in the abstract builder), which is this builder class.
+ JCExpression builderImplClassExpression = maker.Ident(tdParent.toName(builderImplClass));
+ if (typeParams.nonEmpty()) {
+ builderImplClassExpression = maker.TypeApply(builderImplClassExpression, getTypeParamExpressions(typeParams, maker).toList());
+ }
+ ListBuffer<JCExpression> typeParamsForBuilder = getTypeParamExpressions(typeParams, maker);
+ typeParamsForBuilder.add(annotatedClass);
+ typeParamsForBuilder.add(builderImplClassExpression);
+ extending = maker.TypeApply(extending, typeParamsForBuilder.toList());
+
+ JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderImplClass), copyTypeParams(source, typeParams), extending, List.<JCExpression>nil(), List.<JCTree>nil());
+ return injectType(tdParent, builder);
+ }
+
+ /**
+ * Generates a constructor that has a builder as the only parameter.
+ * The values from the builder are used to initialize the fields of new instances.
+ *
+ * @param typeNode
+ * the type (with the {@code @Builder} annotation) for which a
+ * constructor should be generated.
+ * @param typeParams
+ * @param builderFields a list of fields in the builder which should be assigned to new instances.
+ * @param source the annotation (used for setting source code locations for the generated code).
+ * @param callBuilderBasedSuperConstructor
+ * If {@code true}, the constructor will explicitly call a super
+ * constructor with the builder as argument. Requires
+ * {@code builderClassAsParameter != null}.
+ */
+ private void generateBuilderBasedConstructor(JavacNode typeNode, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, JavacNode source, String builderClassName, boolean callBuilderBasedSuperConstructor) {
+ JavacTreeMaker maker = typeNode.getTreeMaker();
+
+ AccessLevel level = AccessLevel.PROTECTED;
+
+ ListBuffer<JCStatement> nullChecks = new ListBuffer<JCStatement>();
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ Name builderVariableName = typeNode.toName("b");
+ for (BuilderFieldData bfd : builderFields) {
+ List<JCAnnotation> nonNulls = findAnnotations(bfd.originalFieldNode, NON_NULL_PATTERN);
+ if (!nonNulls.isEmpty()) {
+ JCStatement nullCheck = generateNullCheck(maker, bfd.originalFieldNode, source);
+ if (nullCheck != null) nullChecks.append(nullCheck);
+ }
+
+ JCExpression rhs;
+ if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) {
+ bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, bfd.originalFieldNode, bfd.type, statements, bfd.name, "b");
+ rhs = maker.Ident(bfd.singularData.getPluralName());
+ } else {
+ rhs = maker.Select(maker.Ident(builderVariableName), bfd.rawName);
+ }
+ JCFieldAccess thisX = maker.Select(maker.Ident(typeNode.toName("this")), bfd.rawName);
+
+ JCStatement assign = maker.Exec(maker.Assign(thisX, rhs));
+
+ // In case of @Builder.Default, only set the value if it really was set in the builder.
+ if (bfd.nameOfSetFlag != null) {
+ JCFieldAccess setField = maker.Select(maker.Ident(builderVariableName), bfd.nameOfSetFlag);
+ JCIf ifSet = maker.If(setField, assign, null);
+ statements.append(ifSet);
+ } else {
+ statements.append(assign);
+ }
+ }
+
+ JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
+
+ // Create a constructor that has just the builder as parameter.
+ ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
+ long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
+ Name builderClassname = typeNode.toName(builderClassName);
+ // First add all generics that are present on the parent type.
+ ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(typeParams, maker);
+ // Now add the <?, ?>.
+ JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+ typeParamsForBuilderParameter.add(wildcard);
+ wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+ typeParamsForBuilderParameter.add(wildcard);
+ JCTypeApply paramType = maker.TypeApply(maker.Ident(builderClassname), typeParamsForBuilderParameter.toList());
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(flags), builderVariableName, paramType, null);
+ params.append(param);
+
+ if (callBuilderBasedSuperConstructor) {
+ // The first statement must be the call to the super constructor.
+ JCMethodInvocation callToSuperConstructor = maker.Apply(List.<JCExpression>nil(),
+ maker.Ident(typeNode.toName("super")),
+ List.<JCExpression>of(maker.Ident(builderVariableName)));
+ statements.prepend(maker.Exec(callToSuperConstructor));
+ }
+
+ JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"),
+ null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
+ maker.Block(0L, nullChecks.appendList(statements).toList()), null), source.get(), typeNode.getContext());
+
+ injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
+ }
+
+ private JCMethodDecl generateBuilderMethod(String builderMethodName, String builderClassName, String builderImplClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) {
+ JavacTreeMaker maker = type.getTreeMaker();
+
+ ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
+ for (JCTypeParameter typeParam : typeParams) typeArgs.append(maker.Ident(typeParam.name));
+
+ JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderImplClassName), typeParams), List.<JCExpression>nil(), null);
+ JCStatement statement = maker.Return(call);
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ int modifiers = Flags.PUBLIC;
+ modifiers |= Flags.STATIC;
+
+ // Add any type params of the annotated class to the return type.
+ ListBuffer<JCExpression> typeParameterNames = new ListBuffer<JCExpression>();
+ typeParameterNames.addAll(typeParameterNames(maker, typeParams));
+ // Now add the <?, ?>.
+ JCWildcard wildcard = maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null);
+ typeParameterNames.add(wildcard);
+ typeParameterNames.add(wildcard);
+ JCTypeApply returnType = maker.TypeApply(maker.Ident(type.toName(builderClassName)), typeParameterNames.toList());
+
+ return maker.MethodDef(maker.Modifiers(modifiers), type.toName(builderMethodName), returnType, copyTypeParams(source, typeParams), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
+ private JCMethodDecl generateAbstractSelfMethod(JavacNode type, boolean override, String builderGenericName) {
+ JavacTreeMaker maker = type.getTreeMaker();
+ List<JCAnnotation> annotations = List.nil();
+ if (override) {
+ JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil());
+ annotations = List.of(overrideAnnotation);
+ }
+ JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED | Flags.ABSTRACT, annotations);
+ Name name = type.toName(SELF_METHOD);
+ JCExpression returnType = maker.Ident(type.toName(builderGenericName));
+
+ return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
+ }
+
+ private JCMethodDecl generateSelfMethod(JavacNode builderImplType) {
+ JavacTreeMaker maker = builderImplType.getTreeMaker();
+
+ JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(builderImplType, "Override"), List.<JCExpression>nil());
+ JCModifiers modifiers = maker.Modifiers(Flags.PROTECTED, List.of(overrideAnnotation));
+ Name name = builderImplType.toName(SELF_METHOD);
+ JCExpression returnType = maker.Ident(builderImplType.toName(builderImplType.getName()));
+
+ JCStatement statement = maker.Return(maker.Ident(builderImplType.toName("this")));
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+
+ return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
+ private JCMethodDecl generateAbstractBuildMethod(JavacNode type, String methodName, boolean override, String classGenericName) {
+ JavacTreeMaker maker = type.getTreeMaker();
+ List<JCAnnotation> annotations = List.nil();
+ if (override) {
+ JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil());
+ annotations = List.of(overrideAnnotation);
+ }
+ JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC | Flags.ABSTRACT, annotations);
+ Name name = type.toName(methodName);
+ JCExpression returnType = maker.Ident(type.toName(classGenericName));
+
+ return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
+ }
+
+ private JCMethodDecl generateBuildMethod(String buildName, JCExpression returnType, JavacNode type, List<JCExpression> thrownExceptions) {
+ JavacTreeMaker maker = type.getTreeMaker();
+
+ JCExpression call;
+ ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
+
+ // Use a constructor that only has this builder as parameter.
+ List<JCExpression> builderArg = List.<JCExpression>of(maker.Ident(type.toName("this")));
+ call = maker.NewClass(null, List.<JCExpression>nil(), returnType, builderArg, null);
+ statements.append(maker.Return(call));
+
+ JCBlock body = maker.Block(0, statements.toList());
+
+ JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(type, "Override"), List.<JCExpression>nil());
+ JCModifiers modifiers = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation));
+
+ return maker.MethodDef(modifiers, type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null);
+ }
+
+ 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);
+ }
+
+ private 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);
+ }
+
+ for (int i = len - 1; i >= 0; i--) {
+ 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);
+ }
+ }
+ }
+
+ private void generateSetterMethodsForBuilder(final JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, final String builderGenericName) {
+ boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode);
+ final JavacTreeMaker maker = builderType.getTreeMaker();
+ ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() {
+ return maker.Ident(builderType.toName(builderGenericName));
+ }};
+
+ StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() {
+ return maker.Return(maker.Apply(List.<JCExpression>nil(), maker.Ident(builderType.toName(SELF_METHOD)), List.<JCExpression>nil()));
+ }};
+
+ if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) {
+ generateSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, true, true, returnTypeMaker.make(), returnStatementMaker.make());
+ } else {
+ fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), true, returnTypeMaker, returnStatementMaker);
+ }
+ }
+
+ private void generateSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain, JCExpression returnType, JCStatement returnStatement) {
+ Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
+
+ for (JavacNode child : builderType.down()) {
+ if (child.getKind() != Kind.METHOD) continue;
+ JCMethodDecl methodDecl = (JCMethodDecl) child.get();
+ Name existingName = methodDecl.name;
+ if (existingName.equals(fieldName) && !isTolerate(fieldNode, methodDecl)) return;
+ }
+
+ String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
+
+ JavacTreeMaker maker = fieldNode.getTreeMaker();
+
+ JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, returnType, returnStatement, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil());
+
+ injectMethod(builderType, newMethod);
+ }
+
+ 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;
+ }
+
+ private String generateNonclashingNameFor(String classGenericName, java.util.List<String> typeParamStrings) {
+ if (!typeParamStrings.contains(classGenericName)) return classGenericName;
+ int counter = 2;
+ while (typeParamStrings.contains(classGenericName + counter)) counter++;
+ return classGenericName + counter;
+ }
+
+ private JavacNode findInnerClass(JavacNode parent, String name) {
+ for (JavacNode child : parent.down()) {
+ if (child.getKind() != Kind.TYPE) continue;
+ JCClassDecl td = (JCClassDecl) child.get();
+ if (td.name.contentEquals(name)) return child;
+ }
+ return null;
+ }
+
+ private ListBuffer<JCExpression> getTypeParamExpressions(List<? extends JCTree> typeParams, JavacTreeMaker maker) {
+ ListBuffer<JCExpression> typeParamsForBuilderParameter = new ListBuffer<JCExpression>();
+ for (JCTree typeParam : typeParams) {
+ if (typeParam instanceof JCTypeParameter) {
+ typeParamsForBuilderParameter.add(maker.Ident(((JCTypeParameter)typeParam).getName()));
+ } else if (typeParam instanceof JCIdent) {
+ typeParamsForBuilderParameter.add(maker.Ident(((JCIdent)typeParam).getName()));
+ }
+ }
+ return typeParamsForBuilderParameter;
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java
index 743e7b26..28d18357 100644
--- a/src/core/lombok/javac/handlers/HandleToString.java
+++ b/src/core/lombok/javac/handlers/HandleToString.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
@@ -31,6 +31,8 @@ import lombok.ConfigurationKeys;
import lombok.ToString;
import lombok.core.AnnotationValues;
import lombok.core.AST.Kind;
+import lombok.core.handlers.InclusionExclusionUtils;
+import lombok.core.handlers.InclusionExclusionUtils.Included;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
@@ -52,57 +54,33 @@ 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;
/**
* Handles the {@code ToString} annotation for javac.
*/
@ProviderFor(JavacAnnotationHandler.class)
public class HandleToString extends JavacAnnotationHandler<ToString> {
- public void checkForBogusFieldNames(JavacNode type, AnnotationValues<ToString> annotation) {
- if (annotation.isExplicit("exclude")) {
- for (int i : createListOfNonExistentFields(List.from(annotation.getInstance().exclude()), type, true, false)) {
- annotation.setWarning("exclude", "This field does not exist, or would have been excluded anyway.", i);
- }
- }
- if (annotation.isExplicit("of")) {
- for (int i : createListOfNonExistentFields(List.from(annotation.getInstance().of()), type, false, false)) {
- annotation.setWarning("of", "This field does not exist.", i);
- }
- }
- }
-
@Override public void handle(AnnotationValues<ToString> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString");
deleteAnnotationIfNeccessary(annotationNode, ToString.class);
ToString ann = annotation.getInstance();
- List<String> excludes = List.from(ann.exclude());
- List<String> includes = List.from(ann.of());
- JavacNode typeNode = annotationNode.up();
-
- checkForBogusFieldNames(typeNode, annotation);
+ java.util.List<Included<JavacNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(annotationNode.up(), annotation, annotationNode);
+ if (members == null) return;
Boolean callSuper = ann.callSuper();
if (!annotation.isExplicit("callSuper")) callSuper = null;
- if (!annotation.isExplicit("exclude")) excludes = null;
- if (!annotation.isExplicit("of")) includes = null;
-
- if (excludes != null && includes != null) {
- excludes = null;
- annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored.");
- }
Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS);
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES);
- boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
+ boolean includeNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration;
- generateToString(typeNode, annotationNode, excludes, includes, includeFieldNames, callSuper, true, fieldAccess);
+ generateToString(annotationNode.up(), annotationNode, members, includeNames, callSuper, true, fieldAccess);
}
public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) {
@@ -111,26 +89,31 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
return;
}
-
boolean includeFieldNames = true;
try {
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;
+
+ java.util.List<Included<JavacNode, ToString.Include>> members = InclusionExclusionUtils.handleToStringMarking(typeNode, null, null);
+ generateToString(typeNode, errorNode, members, includeFieldNames, null, false, access);
}
- public void generateToString(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes,
- boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
+ public void generateToString(JavacNode typeNode, JavacNode source, java.util.List<Included<JavacNode, ToString.Include>> members,
+ boolean includeFieldNames, Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess) {
+
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)) != 0;
}
if (callSuper == null) {
try {
- callSuper = ((Boolean)ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ callSuper = ((Boolean) ToString.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {}
}
@@ -139,30 +122,9 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
return;
}
- ListBuffer<JavacNode> nodesForToString = new ListBuffer<JavacNode>();
- if (includes != null) {
- for (JavacNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
- if (includes.contains(fieldDecl.name.toString())) nodesForToString.append(child);
- }
- } else {
- for (JavacNode child : typeNode.down()) {
- if (child.getKind() != Kind.FIELD) continue;
- JCVariableDecl fieldDecl = (JCVariableDecl) child.get();
- //Skip static fields.
- if ((fieldDecl.mods.flags & Flags.STATIC) != 0) continue;
- //Skip excluded fields.
- if (excludes != null && excludes.contains(fieldDecl.name.toString())) continue;
- //Skip fields that start with $.
- if (fieldDecl.name.toString().startsWith("$")) continue;
- nodesForToString.append(child);
- }
- }
-
switch (methodExists("toString", typeNode, 0)) {
case NOT_EXISTS:
- JCMethodDecl method = createToString(typeNode, nodesForToString.toList(), includeFieldNames, callSuper, fieldAccess, source.get());
+ JCMethodDecl method = createToString(typeNode, members, includeFieldNames, callSuper, fieldAccess, source.get());
injectMethod(typeNode, method);
break;
case EXISTS_BY_LOMBOK:
@@ -176,7 +138,9 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
}
}
- static JCMethodDecl createToString(JavacNode typeNode, Collection<JavacNode> fields, boolean includeFieldNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+ static JCMethodDecl createToString(JavacNode typeNode, Collection<Included<JavacNode, ToString.Include>> members,
+ boolean includeNames, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+
JavacTreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
@@ -191,10 +155,13 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
String prefix;
if (callSuper) {
prefix = typeName + "(super=";
- } else if (fields.isEmpty()) {
+ } else if (members.isEmpty()) {
prefix = typeName + "()";
- } else if (includeFieldNames) {
- prefix = typeName + "(" + ((JCVariableDecl)fields.iterator().next().get()).name.toString() + "=";
+ } else if (includeNames) {
+ Included<JavacNode, ToString.Include> firstMember = members.iterator().next();
+ String name = firstMember.getInc() == null ? "" : firstMember.getInc().name();
+ if (name.isEmpty()) name = firstMember.getNode().getName();
+ prefix = typeName + "(" + name + "=";
} else {
prefix = typeName + "(";
}
@@ -203,30 +170,35 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
if (callSuper) {
JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("toString")),
- List.<JCExpression>nil());
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("toString")),
+ List.<JCExpression>nil());
current = maker.Binary(CTC_PLUS, current, callToSuper);
first = false;
}
- for (JavacNode fieldNode : fields) {
+ for (Included<JavacNode, ToString.Include> member : members) {
JCExpression expr;
- JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
+ JCExpression memberAccessor;
+ JavacNode memberNode = member.getNode();
+ if (memberNode.getKind() == Kind.METHOD) {
+ memberAccessor = createMethodAccessor(maker, memberNode);
+ } else {
+ memberAccessor = createFieldAccessor(maker, memberNode, fieldAccess);
+ }
- JCExpression fieldType = getFieldType(fieldNode, fieldAccess);
+ JCExpression memberType = getFieldType(memberNode, fieldAccess);
// The distinction between primitive and object will be useful if we ever add a 'hideNulls' option.
- boolean fieldIsPrimitive = fieldType instanceof JCPrimitiveTypeTree;
- boolean fieldIsPrimitiveArray = fieldType instanceof JCArrayTypeTree && ((JCArrayTypeTree) fieldType).elemtype instanceof JCPrimitiveTypeTree;
- boolean fieldIsObjectArray = !fieldIsPrimitiveArray && fieldType instanceof JCArrayTypeTree;
@SuppressWarnings("unused")
- boolean fieldIsObject = !fieldIsPrimitive && !fieldIsPrimitiveArray && !fieldIsObjectArray;
+ boolean fieldIsPrimitive = memberType instanceof JCPrimitiveTypeTree;
+ boolean fieldIsPrimitiveArray = memberType instanceof JCArrayTypeTree && ((JCArrayTypeTree) memberType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean fieldIsObjectArray = !fieldIsPrimitiveArray && memberType instanceof JCArrayTypeTree;
if (fieldIsPrimitiveArray || fieldIsObjectArray) {
JCExpression tsMethod = chainDots(typeNode, "java", "util", "Arrays", fieldIsObjectArray ? "deepToString" : "toString");
- expr = maker.Apply(List.<JCExpression>nil(), tsMethod, List.<JCExpression>of(fieldAccessor));
- } else expr = fieldAccessor;
+ expr = maker.Apply(List.<JCExpression>nil(), tsMethod, List.<JCExpression>of(memberAccessor));
+ } else expr = memberAccessor;
if (first) {
current = maker.Binary(CTC_PLUS, current, expr);
@@ -234,8 +206,10 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
continue;
}
- if (includeFieldNames) {
- current = maker.Binary(CTC_PLUS, current, maker.Literal(infix + fieldNode.getName() + "="));
+ if (includeNames) {
+ String n = member.getInc() == null ? "" : member.getInc().name();
+ if (n.isEmpty()) n = memberNode.getName();
+ current = maker.Binary(CTC_PLUS, current, maker.Literal(infix + n + "="));
} else {
current = maker.Binary(CTC_PLUS, current, maker.Literal(infix));
}
@@ -250,7 +224,7 @@ public class HandleToString extends JavacAnnotationHandler<ToString> {
JCBlock body = maker.Block(0, List.of(returnStatement));
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("toString"), 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 static String getTypeName(JavacNode typeNode) {
diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java
index 010c05a5..ee8081d6 100644
--- a/src/core/lombok/javac/handlers/HandleUtilityClass.java
+++ b/src/core/lombok/javac/handlers/HandleUtilityClass.java
@@ -21,19 +21,14 @@
*/
package lombok.javac.handlers;
-import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage;
+import static lombok.javac.Javac.CTC_VOID;
import static lombok.javac.handlers.JavacHandlerUtil.*;
-import lombok.ConfigurationKeys;
-import lombok.core.AST.Kind;
-import lombok.core.AnnotationValues;
-import lombok.experimental.UtilityClass;
-import lombok.javac.JavacAnnotationHandler;
-import lombok.javac.JavacNode;
-import lombok.javac.JavacTreeMaker;
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;
@@ -46,9 +41,20 @@ 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) {
@@ -141,7 +147,7 @@ public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> {
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);
+ JavacHandlerUtil.injectMethod(typeNode, constructor, List.<Type>nil(), Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID));
}
private List<JCStatement> createThrowStatement(JavacNode typeNode, JavacTreeMaker maker) {
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
index 337ab2d7..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-2015 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;
@@ -42,6 +43,7 @@ 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;
@@ -50,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) {
+
+ 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 (!typeTreeToString.equals("val") && !typeTreeToString.equals("lombok.val")) return;
- if (!typeMatches(val.class, localNode, typeTree)) return;
- handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val");
+ 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;
+
+ 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) {
@@ -73,19 +90,24 @@ 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()), typeTree, localNode.getContext());
@@ -102,11 +124,14 @@ 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 {
@@ -117,7 +142,7 @@ public class HandleVal extends JavacASTAdapter {
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);
+ System.err.println("Exception while resolving: " + localNode + "(" + localNode.getFileName() + ")");
throw e;
}
}
@@ -149,7 +174,7 @@ 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) {
diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java
index 90f6a98d..abc5a5ca 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;
@@ -42,6 +40,7 @@ import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.util.List;
/**
* Handles the {@code lombok.Value} annotation for javac.
@@ -49,13 +48,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 +75,11 @@ 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);
+ handleConstructor.generateExtraNoArgsConstructor(typeNode, annotationNode);
+ handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true, List.<JCAnnotation>nil());
+ 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 f8b6152a..87f3c16a 100644
--- a/src/core/lombok/javac/handlers/HandleWither.java
+++ b/src/core/lombok/javac/handlers/HandleWither.java
@@ -36,11 +36,12 @@ import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.JavacHandlerUtil.CopyJavadoc;
-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 +130,8 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
if (level == AccessLevel.NONE || node == null) return;
- List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Wither(onMethod=", annotationNode);
- List<JCAnnotation> onParam = unboxAndRemoveAnnotationParameter(ast, "onParam", "@Wither(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:
@@ -209,7 +210,10 @@ public class HandleWither extends JavacAnnotationHandler<Wither> {
long access = toJavacModifier(level);
JCMethodDecl createdWither = createWither(access, fieldNode, fieldNode.getTreeMaker(), source, onMethod, onParam, makeAbstract);
- injectMethod(typeNode, createdWither);
+ 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, boolean makeAbstract) {
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index fdc8a262..f335cf94 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2015 The Project Lombok Authors.
+ * Copyright (C) 2009-2018 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -37,6 +37,8 @@ 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;
@@ -59,7 +61,12 @@ 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;
@@ -85,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;
@@ -154,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);
}
@@ -183,6 +195,52 @@ 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;
+ }
+ }
+
+ public static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node) {
+ return findAnnotation(type, node, false);
+ }
+
+ public 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.
*
@@ -195,6 +253,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.
@@ -202,10 +271,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);
}
/**
@@ -214,6 +294,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;
@@ -244,8 +325,19 @@ public class JavacHandlerUtil {
* @param node A Lombok AST node representing an annotation in source code.
*/
public static <A extends Annotation> AnnotationValues<A> createAnnotation(Class<A> type, final JavacNode node) {
+ return createAnnotation(type, (JCAnnotation) node.get(), node);
+ }
+
+ /**
+ * Creates an instance of {@code AnnotationValues} for the provided AST Node
+ * and Annotation expression.
+ *
+ * @param type An annotation class type, such as {@code lombok.Getter.class}.
+ * @param anno the annotation expression
+ * @param node A Lombok AST node representing an annotation in source code.
+ */
+ public static <A extends Annotation> AnnotationValues<A> createAnnotation(Class<A> type, JCAnnotation anno, final JavacNode node) {
Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>();
- JCAnnotation anno = (JCAnnotation) node.get();
List<JCExpression> arguments = anno.getArguments();
for (JCExpression arg : arguments) {
@@ -266,17 +358,39 @@ public class JavacHandlerUtil {
}
if (rhs instanceof JCNewArray) {
- List<JCExpression> elems = ((JCNewArray)rhs).elems;
+ List<JCExpression> elems = ((JCNewArray) rhs).elems;
for (JCExpression inner : elems) {
raws.add(inner.toString());
expressions.add(inner);
- guesses.add(calculateGuess(inner));
+ if (inner instanceof JCAnnotation) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<A> innerClass = (Class<A>) Class.forName(inner.type.toString());
+
+ guesses.add(createAnnotation(innerClass, (JCAnnotation) inner, node));
+ } catch (ClassNotFoundException ex) {
+ guesses.add(calculateGuess(inner));
+ }
+ } else {
+ guesses.add(calculateGuess(inner));
+ }
positions.add(inner.pos());
}
} else {
raws.add(rhs.toString());
expressions.add(rhs);
- guesses.add(calculateGuess(rhs));
+ if (rhs instanceof JCAnnotation) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<A> innerClass = (Class<A>) Class.forName(rhs.type.toString());
+
+ guesses.add(createAnnotation(innerClass, (JCAnnotation) rhs, node));
+ } catch (ClassNotFoundException ex) {
+ guesses.add(calculateGuess(rhs));
+ }
+ } else {
+ guesses.add(calculateGuess(rhs));
+ }
positions.add(rhs.pos());
}
@@ -316,8 +430,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);
}
@@ -326,12 +439,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());
+ }
+
+ /**
+ * 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, Class<? extends Annotation>... annotationTypes) {
+ private static void deleteAnnotationIfNeccessary0(JavacNode annotation, String... annotationTypes) {
if (inNetbeansEditor(annotation)) return;
if (!annotation.shouldDeleteLombokAnnotations()) return;
JavacNode parentNode = annotation.directUp();
@@ -360,8 +490,8 @@ public class JavacHandlerUtil {
}
parentNode.getAst().setChanged();
- for (Class<?> annotationType : annotationTypes) {
- deleteImportFromCompilationUnit(annotation, annotationType.getName());
+ for (String annotationType : annotationTypes) {
+ deleteImportFromCompilationUnit(annotation, annotationType);
}
}
@@ -605,10 +735,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;
}
@@ -619,6 +746,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.
@@ -629,15 +764,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;
}
}
@@ -723,7 +855,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();
@@ -746,10 +878,6 @@ public class JavacHandlerUtil {
return null;
}
- public enum FieldAccess {
- GETTER, PREFER_FIELD, ALWAYS_FIELD;
- }
-
static boolean lookForGetter(JavacNode field, FieldAccess fieldAccess) {
if (fieldAccess == FieldAccess.GETTER) return true;
if (fieldAccess == FieldAccess.ALWAYS_FIELD) return false;
@@ -771,12 +899,14 @@ public class JavacHandlerUtil {
* @see #createFieldAccessor(TreeMaker, JavacNode, FieldAccess)
*/
static JCExpression getFieldType(JavacNode field, FieldAccess fieldAccess) {
+ if (field.getKind() == Kind.METHOD) return ((JCMethodDecl) field.get()).restype;
+
boolean lookForGetter = lookForGetter(field, fieldAccess);
GetterMethod getter = lookForGetter ? findGetter(field) : null;
if (getter == null) {
- return ((JCVariableDecl)field.get()).vartype;
+ return ((JCVariableDecl) field.get()).vartype;
}
return getter.type;
@@ -813,10 +943,38 @@ public class JavacHandlerUtil {
if (receiver == null) receiver = maker.Ident(field.toName("this"));
JCMethodInvocation call = maker.Apply(List.<JCExpression>nil(),
- maker.Select(receiver, getter.name), List.<JCExpression>nil());
+ maker.Select(receiver, getter.name), List.<JCExpression>nil());
return call;
}
+ static JCExpression createMethodAccessor(JavacTreeMaker maker, JavacNode method) {
+ return createMethodAccessor(maker, method, null);
+ }
+
+ static JCExpression createMethodAccessor(JavacTreeMaker maker, JavacNode method, JCExpression receiver) {
+ JCMethodDecl methodDecl = (JCMethodDecl) method.get();
+
+ if (receiver == null && (methodDecl.mods.flags & Flags.STATIC) == 0) {
+ receiver = maker.Ident(method.toName("this"));
+ } else if (receiver == null) {
+ JavacNode containerNode = method.up();
+ if (containerNode != null && containerNode.get() instanceof JCClassDecl) {
+ JCClassDecl container = (JCClassDecl) method.up().get();
+ receiver = maker.Ident(container.name);
+ }
+ }
+
+ JCMethodInvocation call = maker.Apply(List.<JCExpression>nil(),
+ receiver == null ? maker.Ident(methodDecl.name) : maker.Select(receiver, methodDecl.name), List.<JCExpression>nil());
+ 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.
@@ -879,16 +1037,20 @@ public class JavacHandlerUtil {
static class ClassSymbolMembersField {
private static final Field membersField;
private static final Method removeMethod;
+ private static final Method enterMethod;
static {
Field f = null;
- Method m = null;
+ Method r = null;
+ Method e = null;
try {
f = ClassSymbol.class.getField("members_field");
- m = f.getType().getMethod("remove", Symbol.class);
- } catch (Exception e) {}
+ r = f.getType().getMethod("remove", Symbol.class);
+ e = f.getType().getMethod("enter", Symbol.class);
+ } catch (Exception ex) {}
membersField = f;
- removeMethod = m;
+ removeMethod = r;
+ enterMethod = e;
}
static void remove(ClassSymbol from, Symbol toRemove) {
@@ -899,6 +1061,19 @@ public class JavacHandlerUtil {
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);
}
/**
@@ -907,7 +1082,7 @@ public class JavacHandlerUtil {
*
* 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>")) {
@@ -915,7 +1090,7 @@ 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);
@@ -931,9 +1106,18 @@ public class JavacHandlerUtil {
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.
*
@@ -997,9 +1181,12 @@ public class JavacHandlerUtil {
public static void addGenerated(JCModifiers mods, JavacNode node, int pos, JCTree source, Context context) {
if (!LombokOptionsFactory.getDelombokOptions(context).getFormatPreferences().generateGenerated()) return;
- if (!Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_GENERATED_ANNOTATIONS))) {
+ 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) {
@@ -1013,12 +1200,16 @@ public class JavacHandlerUtil {
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(simpleName)) return;
+ if (annType instanceof JCFieldAccess) {
+ if (annType.toString().equals(annotationTypeFqn)) return;
+ }
}
+
JavacTreeMaker maker = node.getTreeMaker();
JCExpression annType = isJavaLangBased ? genJavaLangTypeRef(node, simpleName) : chainDotsString(node, annotationTypeFqn);
annType.pos = pos;
@@ -1140,16 +1331,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());
@@ -1191,19 +1391,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) {
@@ -1215,6 +1405,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;
@@ -1227,75 +1426,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));
}
}
}
@@ -1580,4 +1813,12 @@ public class JavacHandlerUtil {
docComments.put(from.get(), filtered[1]);
}
}
+
+ public static boolean isDirectDescendantOfObject(JavacNode typeNode) {
+ if (!(typeNode.get() instanceof JCClassDecl)) throw new IllegalArgumentException("not a type node");
+ JCTree extending = Javac.getExtendsClause((JCClassDecl)typeNode.get());
+ if (extending == null) return true;
+ String p = extending.toString();
+ return p.equals("Object") || p.equals("java.lang.Object");
+ }
}
diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
index c6d601bd..ab8c3a42 100644
--- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
+++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -38,9 +38,12 @@ 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;
@@ -50,6 +53,14 @@ import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
public class JavacSingularsRecipes {
+ public interface ExpressionMaker {
+ JCExpression make();
+ }
+
+ public interface StatementMaker {
+ JCStatement make();
+ }
+
private static final JavacSingularsRecipes INSTANCE = new JavacSingularsRecipes();
private final Map<String, JavacSingularizer> singularizers = new HashMap<String, JavacSingularizer>();
private final TypeLibrary singularizableTypes = new TypeLibrary();
@@ -141,6 +152,11 @@ public class JavacSingularsRecipes {
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()) {
@@ -186,8 +202,36 @@ public class JavacSingularsRecipes {
}
public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source);
- public abstract void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain);
- public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName);
+
+ /**
+ * Generates the singular, plural, and clear methods for the given {@link SingularData}.
+ * Uses the given {@code builderType} as return type if {@code chain == true}, {@code void} otherwise.
+ * If you need more control over the return type and value, use
+ * {@link #generateMethods(SingularData, boolean, JavacNode, JCTree, boolean, ExpressionMaker, StatementMaker)}.
+ */
+ public void generateMethods(SingularData data, boolean deprecate, final JavacNode builderType, JCTree source, boolean fluent, final boolean chain) {
+ final JavacTreeMaker maker = builderType.getTreeMaker();
+
+ ExpressionMaker returnTypeMaker = new ExpressionMaker() { @Override public JCExpression make() {
+ return chain ?
+ cloneSelfType(builderType) :
+ maker.Type(createVoidType(builderType.getSymbolTable(), CTC_VOID));
+ }};
+
+ StatementMaker returnStatementMaker = new StatementMaker() { @Override public JCStatement make() {
+ return chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
+ }};
+
+ generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker);
+ }
+
+ /**
+ * Generates the singular, plural, and clear methods for the given {@link SingularData}.
+ * Uses the given {@code returnTypeMaker} and {@code returnStatementMaker} for the generated methods.
+ */
+ public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker);
+
+ public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable);
public boolean requiresCleaning() {
try {
@@ -266,9 +310,9 @@ public class JavacSingularsRecipes {
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");
+ /** Generates '<em>builderVariable</em>.<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, String builderVariable) {
+ Name thisName = builderType.toName(builderVariable);
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) {
diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
index 4de39d98..ffaf6674 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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,8 +32,10 @@ import lombok.core.handlers.HandlerUtil;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker;
import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
@@ -69,23 +71,15 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer {
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
- @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) {
JavacTreeMaker maker = builderType.getTreeMaker();
- JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
- generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
- generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
- generateClearMethod(maker, returnType, returnStatement, data, builderType, source);
+ generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent);
+ generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent);
+ generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source);
}
- private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) {
- JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ 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();
@@ -100,7 +94,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer {
injectMethod(builderType, method);
}
- void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ 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();
@@ -112,7 +106,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer {
names[i] = s.isEmpty() ? n : builderType.toName(s);
}
- JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ 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());
@@ -139,11 +133,10 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer {
injectMethod(builderType, method);
}
- protected void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ 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 = maker.Modifiers(Flags.PUBLIC);
+ 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");
@@ -167,7 +160,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer {
injectMethod(builderType, method);
}
- @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) {
JavacTreeMaker maker = builderType.getTreeMaker();
List<JCExpression> jceBlank = List.nil();
@@ -184,12 +177,12 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer {
JCExpression invokeBuild; {
//this.pluralName.build();
- invokeBuild = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName().toString(), "build"), jceBlank);
+ invokeBuild = maker.Apply(jceBlank, chainDots(builderType, builderVariable, 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));
+ isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(builderType.toName(builderVariable)), data.getPluralName()), maker.Literal(CTC_BOT, null));
}
JCExpression init = maker.Conditional(isNull, empty, invokeBuild); // this.pluralName == null ? ImmutableX.of() : this.pluralName.build()
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
index e167c7e2..39e53ebb 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-2018 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,9 @@ import lombok.core.handlers.HandlerUtil;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker;
import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
@@ -75,30 +77,20 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize
return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField));
}
- @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.generateMethods(data, builderType, source, fluent, chain);
+ guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker);
return;
}
JavacTreeMaker maker = builderType.getTreeMaker();
- Name thisName = builderType.toName("this");
-
- JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- JCStatement returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
- generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
- generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null;
- generateClearMethod(maker, returnType, returnStatement, data, builderType, source);
+ generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent);
+ generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent);
+ generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source);
}
- private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) {
- JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ 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();
@@ -117,11 +109,10 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize
injectMethod(builderType, method);
}
- void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ 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 = maker.Modifiers(Flags.PUBLIC);
+ 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");
@@ -138,11 +129,10 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize
injectMethod(builderType, method);
}
- void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ 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 = maker.Modifiers(Flags.PUBLIC);
+ 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");
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
index 3002a98f..b1375151 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
@@ -46,9 +46,9 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari
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) {
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable);
return;
}
@@ -71,7 +71,7 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari
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));
+ JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, builderVariable, 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));
@@ -82,12 +82,12 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari
}
/* default: Create with right size, then addAll */ {
- List<JCStatement> defStats = createListCopy(maker, data, builderType, source);
+ List<JCStatement> defStats = createListCopy(maker, data, builderType, source, builderVariable);
JCCase defaultCase = maker.Case(null, defStats);
cases.append(defaultCase);
}
- JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false), cases.toList());
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false, builderVariable), 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);
@@ -95,9 +95,9 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari
statements.append(switchStat);
}
- private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source) {
+ private List<JCStatement> createListCopy(JavacTreeMaker maker, SingularData data, JavacNode builderType, JCTree source, String builderVariable) {
List<JCExpression> jceBlank = List.nil();
- Name thisName = builderType.toName("this");
+ Name thisName = builderType.toName(builderVariable);
JCExpression argToUnmodifiable; {
// new java.util.ArrayList<Generics>(this.pluralName);
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
index 1acae7e3..34350f40 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Project Lombok Authors.
+ * Copyright (C) 2015-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
@@ -31,8 +31,10 @@ import lombok.core.handlers.HandlerUtil;
import lombok.javac.JavacNode;
import lombok.javac.JavacTreeMaker;
import lombok.javac.handlers.JavacHandlerUtil;
+import lombok.javac.handlers.JavacSingularsRecipes.ExpressionMaker;
import lombok.javac.handlers.JavacSingularsRecipes.JavacSingularizer;
import lombok.javac.handlers.JavacSingularsRecipes.SingularData;
+import lombok.javac.handlers.JavacSingularsRecipes.StatementMaker;
import org.mangosdk.spi.ProviderFor;
@@ -100,29 +102,21 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer {
return Arrays.asList(keyFieldNode, valueFieldNode);
}
- @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) {
+ @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, ExpressionMaker returnTypeMaker, StatementMaker returnStatementMaker) {
if (useGuavaInstead(builderType)) {
- guavaMapSingularizer.generateMethods(data, builderType, source, fluent, chain);
+ guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, returnTypeMaker, returnStatementMaker);
return;
}
JavacTreeMaker maker = builderType.getTreeMaker();
-
- JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
- generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
- generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent);
-
- returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(maker, CTC_VOID));
- returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null;
- generateClearMethod(maker, returnType, returnStatement, data, builderType, source);
+ generateSingularMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent);
+ generatePluralMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source, fluent);
+ generateClearMethod(deprecate, maker, returnTypeMaker.make(), returnStatementMaker.make(), data, builderType, source);
}
- private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) {
- JCModifiers mods = maker.Modifiers(Flags.PUBLIC);
+ 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();
@@ -144,10 +138,11 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer {
injectMethod(builderType, method);
}
- private void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ 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 = maker.Modifiers(Flags.PUBLIC);
+ 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");
@@ -176,10 +171,10 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer {
injectMethod(builderType, method);
}
- private void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) {
+ 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 = maker.Modifiers(Flags.PUBLIC);
+ 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());
@@ -208,18 +203,18 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer {
injectMethod(builderType, method);
}
- @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName) {
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ guavaMapSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable);
return;
}
JavacTreeMaker maker = builderType.getTreeMaker();
if (data.getTargetFqn().equals("java.util.Map")) {
- statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source));
+ statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, true, "emptyMap", "singletonMap", "LinkedHashMap", source, builderVariable));
} else {
- statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source));
+ statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, true, true, false, true, "TreeMap", source, builderVariable));
}
}
}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
index 317233cb..71a36c0e 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSetSingularizer.java
@@ -40,18 +40,18 @@ public class JavacJavaUtilSetSingularizer extends JavacJavaUtilListSetSingulariz
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) {
+ @Override public void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable) {
if (useGuavaInstead(builderType)) {
- guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName);
+ guavaListSetSingularizer.appendBuildCode(data, builderType, source, statements, targetVariableName, builderVariable);
return;
}
JavacTreeMaker maker = builderType.getTreeMaker();
if (data.getTargetFqn().equals("java.util.Set")) {
- statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source));
+ statements.appendList(createJavaUtilSetMapInitialCapacitySwitchStatements(maker, data, builderType, false, "emptySet", "singleton", "LinkedHashSet", source, builderVariable));
} else {
- statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source));
+ statements.appendList(createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, false, true, false, true, "TreeSet", source, builderVariable));
}
}
}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
index 25669721..df521fd8 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
@@ -46,7 +46,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
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) {
+ protected List<JCStatement> createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source, String builderVariable) {
List<JCExpression> jceBlank = List.nil();
ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
@@ -66,11 +66,11 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
// !mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName.get(0));
// mapMode: pluralName = java.util.Collections.singletonCollectionMethod(this.pluralName$key.get(0), this.pluralName$value.get(0));
JCExpression zeroLiteral = maker.Literal(CTC_INT, 0);
- JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + (mapMode ? "$key" : ""), "get"), List.of(zeroLiteral));
+ JCExpression arg = maker.Apply(jceBlank, chainDots(builderType, builderVariable, 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));
+ JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + (mapMode ? "$value" : ""), "get"), List.of(zeroLiteralClone));
args = List.of(arg, arg2);
} else {
args = List.of(arg);
@@ -84,12 +84,12 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
}
{ // default:
- List<JCStatement> statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source);
+ List<JCStatement> statements = createJavaUtilSimpleCreationAndFillStatements(maker, data, builderType, mapMode, false, true, emptyCollectionMethod == null, targetType, source, builderVariable);
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());
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false, builderVariable), 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);
@@ -125,9 +125,9 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
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) {
+ protected List<JCStatement> createJavaUtilSimpleCreationAndFillStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, boolean defineVar, boolean addInitialCapacityArg, boolean nullGuard, String targetType, JCTree source, String builderVariable) {
List<JCExpression> jceBlank = List.nil();
- Name thisName = builderType.toName("this");
+ Name thisName = builderType.toName(builderVariable);
JCStatement createStat; {
// pluralName = new java.util.TargetType(initialCap);
@@ -136,10 +136,10 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
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 lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard, true, builderVariable), 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 sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard, true, builderVariable));
+ JCExpression sizeFormulaRightLeft = maker.Parens(maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard, true, builderVariable), 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));
@@ -163,11 +163,14 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
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)));
+ JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$key", "get"), List.<JCExpression>of(maker.Ident(ivar)));
+ JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, 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 checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true, builderVariable));
JCExpression incrementExpr = maker.Unary(CTC_POSTINC, maker.Ident(ivar));
fillStat = maker.ForLoop(List.of(forInit), checkExpr, List.of(maker.Exec(incrementExpr)), putStatement);
} else {
diff --git a/src/core/lombok/package-info.java b/src/core/lombok/package-info.java
index 5e34c914..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="https://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 a398fd34..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="https://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 {
+}