aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/AllArgsConstructor.java2
-rw-r--r--src/core/lombok/Builder.java35
-rw-r--r--src/core/lombok/Cleanup.java2
-rw-r--r--src/core/lombok/ConfigurationKeys.java33
-rw-r--r--src/core/lombok/Data.java2
-rw-r--r--src/core/lombok/EqualsAndHashCode.java2
-rw-r--r--src/core/lombok/Getter.java2
-rw-r--r--src/core/lombok/NoArgsConstructor.java10
-rw-r--r--src/core/lombok/RequiredArgsConstructor.java2
-rw-r--r--src/core/lombok/Setter.java2
-rw-r--r--src/core/lombok/SneakyThrows.java2
-rw-r--r--src/core/lombok/Synchronized.java2
-rw-r--r--src/core/lombok/ToString.java2
-rw-r--r--src/core/lombok/Value.java6
-rw-r--r--src/core/lombok/core/Main.java2
-rw-r--r--src/core/lombok/core/TypeLibrary.java38
-rw-r--r--src/core/lombok/core/TypeResolver.java16
-rw-r--r--src/core/lombok/core/Version.java2
-rw-r--r--src/core/lombok/core/configuration/BubblingConfigurationResolver.java21
-rw-r--r--src/core/lombok/core/configuration/FileSystemSourceCache.java77
-rw-r--r--src/core/lombok/core/handlers/HandlerUtil.java4
-rw-r--r--src/core/lombok/eclipse/EclipseAST.java2
-rw-r--r--src/core/lombok/eclipse/EclipseASTVisitor.java2
-rw-r--r--src/core/lombok/eclipse/EclipseNode.java48
-rw-r--r--src/core/lombok/eclipse/TransformEclipseAST.java2
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleBuilder.java284
-rw-r--r--src/core/lombok/eclipse/handlers/HandleConstructor.java134
-rw-r--r--src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java6
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldDefaults.java85
-rw-r--r--src/core/lombok/eclipse/handlers/HandleHelper.java137
-rw-r--r--src/core/lombok/eclipse/handlers/HandleNonNull.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleUtilityClass.java13
-rw-r--r--src/core/lombok/experimental/Accessors.java2
-rw-r--r--src/core/lombok/experimental/Builder.java2
-rw-r--r--src/core/lombok/experimental/Delegate.java2
-rw-r--r--src/core/lombok/experimental/ExtensionMethod.java2
-rw-r--r--src/core/lombok/experimental/FieldDefaults.java2
-rw-r--r--src/core/lombok/experimental/Helper.java35
-rw-r--r--src/core/lombok/experimental/Value.java2
-rw-r--r--src/core/lombok/experimental/Wither.java2
-rw-r--r--src/core/lombok/experimental/package-info.java2
-rw-r--r--src/core/lombok/extern/apachecommons/CommonsLog.java2
-rw-r--r--src/core/lombok/extern/java/Log.java2
-rw-r--r--src/core/lombok/extern/log4j/Log4j.java2
-rw-r--r--src/core/lombok/extern/log4j/Log4j2.java4
-rw-r--r--src/core/lombok/extern/slf4j/Slf4j.java2
-rw-r--r--src/core/lombok/extern/slf4j/XSlf4j.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleBuilder.java272
-rw-r--r--src/core/lombok/javac/handlers/HandleConstructor.java91
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java94
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldDefaults.java81
-rw-r--r--src/core/lombok/javac/handlers/HandleHelper.java138
-rw-r--r--src/core/lombok/javac/handlers/HandleNonNull.java3
-rw-r--r--src/core/lombok/javac/handlers/HandleUtilityClass.java2
-rw-r--r--src/core/lombok/javac/handlers/HandleVal.java14
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java35
-rw-r--r--src/core/lombok/javac/handlers/JavacSingularsRecipes.java6
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java2
-rw-r--r--src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java10
-rw-r--r--src/core/lombok/package-info.java2
-rw-r--r--src/core/lombok/val.java2
-rw-r--r--src/delombok/lombok/delombok/DelombokResult.java3
-rw-r--r--src/delombok/lombok/delombok/PrettyCommentsPrinter.java1715
-rw-r--r--src/delombok/lombok/delombok/PrettyPrinter.java1488
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java10
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java85
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java58
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java46
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java4
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java3
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java2
-rw-r--r--src/eclipseAgent/lombok/launch/PatchFixesHider.java29
-rw-r--r--src/installer/lombok/installer/Installer.java2
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseLocation.java19
-rw-r--r--src/installer/lombok/installer/eclipse/EclipseLocationProvider.java7
-rw-r--r--src/launch/lombok/launch/Main.java5
-rw-r--r--src/launch/lombok/launch/ShadowClassLoader.java183
-rw-r--r--src/utils/lombok/eclipse/Eclipse.java19
79 files changed, 3210 insertions, 2266 deletions
diff --git a/src/core/lombok/AllArgsConstructor.java b/src/core/lombok/AllArgsConstructor.java
index cc494967..3037c9db 100644
--- a/src/core/lombok/AllArgsConstructor.java
+++ b/src/core/lombok/AllArgsConstructor.java
@@ -30,7 +30,7 @@ import java.lang.annotation.Target;
* Generates an all-args constructor.
* An all-args constructor requires one argument for every field in the class.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Constructor.html">the project lombok features page for &#64;Constructor</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor.html">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.
*
diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java
index c56c4004..6a92028c 100644
--- a/src/core/lombok/Builder.java
+++ b/src/core/lombok/Builder.java
@@ -48,7 +48,7 @@ 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="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for &#64;Builder</a>.
+ * 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:
@@ -113,9 +113,40 @@ public @interface 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.
+ /**
+ * Name of the builder class.
+ *
* Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}.
+ * <p>
* Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}.
*/
String builderClassName() default "";
+
+ /**
+ * 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.
+ */
+ boolean toBuilder() default false;
+
+ /**
+ * Put on a field (in case of {@code @Builder} on a type) or a parameter (for {@code @Builder} on a constructor or static method) to
+ * indicate how lombok should obtain a value for this field or parameter given an instance; this is only relevant if {@code toBuilder} is {@code true}.
+ *
+ * You do not need to supply an {@code @ObtainVia} annotation unless you wish to change the default behaviour: Use a field with the same name.
+ * <p>
+ * Note that one of {@code field} or {@code method} should be set, or an error is generated.
+ * <p>
+ * The default behaviour is to obtain a value by referencing the name of the parameter as a field on 'this'.
+ */
+ public @interface ObtainVia {
+ /** 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()}. */
+ String method() default "";
+
+ /** Tells lombok to obtain a value with the expression {@code SelfType.method(this)}; requires {@code method} to be set. */
+ boolean isStatic() default false;
+ }
}
diff --git a/src/core/lombok/Cleanup.java b/src/core/lombok/Cleanup.java
index 4b5c6fc2..c95c03c5 100644
--- a/src/core/lombok/Cleanup.java
+++ b/src/core/lombok/Cleanup.java
@@ -31,7 +31,7 @@ import java.lang.annotation.Target;
* of what happens. Implemented by wrapping all statements following the local variable declaration to the
* end of your scope into a try block that, as a finally action, closes the resource.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Cleanup.html">the project lombok features page for &#64;Cleanup</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Cleanup.html">the project lombok features page for &#64;Cleanup</a>.
* <p>
* Example:
* <pre>
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java
index 6c595504..dd6732ed 100644
--- a/src/core/lombok/ConfigurationKeys.java
+++ b/src/core/lombok/ConfigurationKeys.java
@@ -396,20 +396,34 @@ public class ConfigurationKeys {
// ----- FieldDefaults -----
/**
+ * lombok configuration: {@code lombok.fieldDefaults.defaultPrivate} = {@code true} | {@code false}.
+ *
+ * If set to <code>true</code> <em>any</em> field without an access modifier or {@code @PackagePrivate} is marked as {@code private} by lombok, in all source files compiled.
+ */
+ public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_PRIVATE_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultPrivate", "If true, fields without any access modifier, in any file (lombok annotated or not) are marked as private. Use @PackagePrivate or an explicit modifier to override this.") {};
+
+ /**
+ * lombok configuration: {@code lombok.fieldDefaults.defaultFinal} = {@code true} | {@code false}.
+ *
+ * If set to <code>true</code> <em>any</em> field without {@code @NonFinal} is marked as {@code final} by lombok, in all source files compiled.
+ */
+ public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_FINAL_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultFinal", "If true, fields, in any file (lombok annotated or not) are marked as final. Use @NonFinal to override this.") {};
+
+ /**
* lombok configuration: {@code lombok.fieldDefaults.flagUsage} = {@code WARNING} | {@code ERROR}.
*
* If set, <em>any</em> usage of {@code @FieldDefaults} results in a warning / error.
*/
public static final ConfigurationKey<FlagUsageType> FIELD_DEFAULTS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.fieldDefaults.flagUsage", "Emit a warning or error if @FieldDefaults is used.") {};
- // ----- Wither -----
+ // ----- Helper -----
/**
- * lombok configuration: {@code lombok.wither.flagUsage} = {@code WARNING} | {@code ERROR}.
+ * lombok configuration: {@code lombok.helper.flagUsage} = {@code WARNING} | {@code ERROR}.
*
- * If set, <em>any</em> usage of {@code @Wither} results in a warning / error.
+ * If set, <em>any</em> usage of {@code @Helper} results in a warning / error.
*/
- public static final ConfigurationKey<FlagUsageType> WITHER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.wither.flagUsage", "Emit a warning or error if @Wither is used.") {};
+ public static final ConfigurationKey<FlagUsageType> HELPER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.helper.flagUsage", "Emit a warning or error if @Helper is used.") {};
// ----- UtilityClass -----
@@ -418,7 +432,16 @@ public class ConfigurationKeys {
*
* If set, <em>any</em> usage of {@code @UtilityClass} results in a warning / error.
*/
- public static final ConfigurationKey<FlagUsageType> UTLITY_CLASS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.utilityClass.flagUsage", "Emit a warning or error if @UtilityClass is used.") {};
+ public static final ConfigurationKey<FlagUsageType> UTILITY_CLASS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.utilityClass.flagUsage", "Emit a warning or error if @UtilityClass is used.") {};
+
+ // ----- Wither -----
+
+ /**
+ * lombok configuration: {@code lombok.wither.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @Wither} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> WITHER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.wither.flagUsage", "Emit a warning or error if @Wither is used.") {};
// ----- Configuration System -----
diff --git a/src/core/lombok/Data.java b/src/core/lombok/Data.java
index bbc8d920..4c6c2e5d 100644
--- a/src/core/lombok/Data.java
+++ b/src/core/lombok/Data.java
@@ -32,7 +32,7 @@ import java.lang.annotation.Target;
* <p>
* Equivalent to {@code @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Data.html">the project lombok features page for &#64;Data</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Data.html">the project lombok features page for &#64;Data</a>.
*
* @see Getter
* @see Setter
diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java
index dbce23b8..605815ed 100644
--- a/src/core/lombok/EqualsAndHashCode.java
+++ b/src/core/lombok/EqualsAndHashCode.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Generates implementations for the {@code equals} and {@code hashCode} methods inherited by all objects, based on relevant fields.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/EqualsAndHashCode.html">the project lombok features page for &#64;EqualsAndHashCode</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/EqualsAndHashCode.html">the project lombok features page for &#64;EqualsAndHashCode</a>.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
diff --git a/src/core/lombok/Getter.java b/src/core/lombok/Getter.java
index 428f53ef..906ae60f 100644
--- a/src/core/lombok/Getter.java
+++ b/src/core/lombok/Getter.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Put on any field to make lombok build a standard getter.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/GetterSetter.html">the project lombok features page for &#64;Getter and &#64;Setter</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/GetterSetter.html">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>
diff --git a/src/core/lombok/NoArgsConstructor.java b/src/core/lombok/NoArgsConstructor.java
index cd3e5568..ff437bba 100644
--- a/src/core/lombok/NoArgsConstructor.java
+++ b/src/core/lombok/NoArgsConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2013 The Project Lombok Authors.
+ * Copyright (C) 2010-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,7 @@ import java.lang.annotation.Target;
* Generates a no-args constructor.
* Will generate an error message if such a constructor cannot be written due to the existence of final fields.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Constructor.html">the project lombok features page for &#64;Constructor</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor.html">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>
@@ -61,6 +61,12 @@ public @interface NoArgsConstructor {
AccessLevel access() default lombok.AccessLevel.PUBLIC;
/**
+ * If {@code true}, initializes all final fields to 0 / null / false.
+ * Otherwise, a compile time error occurs.
+ */
+ boolean force() default false;
+
+ /**
* Placeholder annotation to enable the placement of annotations on the generated code.
* @deprecated Don't use this annotation, ever - Read the documentation.
*/
diff --git a/src/core/lombok/RequiredArgsConstructor.java b/src/core/lombok/RequiredArgsConstructor.java
index 8bb57c1a..7a9da3f9 100644
--- a/src/core/lombok/RequiredArgsConstructor.java
+++ b/src/core/lombok/RequiredArgsConstructor.java
@@ -30,7 +30,7 @@ import java.lang.annotation.Target;
* Generates a constructor with required arguments.
* Required arguments are final fields and fields with constraints such as {@code @NonNull}.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Constructor.html">the project lombok features page for &#64;Constructor</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor.html">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.
*
diff --git a/src/core/lombok/Setter.java b/src/core/lombok/Setter.java
index 5e07b802..ba9e9759 100644
--- a/src/core/lombok/Setter.java
+++ b/src/core/lombok/Setter.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Put on any field to make lombok build a standard setter.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/GetterSetter.html">the project lombok features page for &#64;Getter and &#64;Setter</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/GetterSetter.html">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>
diff --git a/src/core/lombok/SneakyThrows.java b/src/core/lombok/SneakyThrows.java
index 929b4578..489e13e4 100644
--- a/src/core/lombok/SneakyThrows.java
+++ b/src/core/lombok/SneakyThrows.java
@@ -34,7 +34,7 @@ import java.lang.annotation.Target;
* checked exception types. The JVM does not check for the consistency of the checked exception system; javac does,
* and this annotation lets you opt out of its mechanism.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/SneakyThrows.html">the project lombok features page for &#64;SneakyThrows</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/SneakyThrows.html">the project lombok features page for &#64;SneakyThrows</a>.
* <p>
* Example:
* <pre>
diff --git a/src/core/lombok/Synchronized.java b/src/core/lombok/Synchronized.java
index c5601a0c..9a39c212 100644
--- a/src/core/lombok/Synchronized.java
+++ b/src/core/lombok/Synchronized.java
@@ -35,7 +35,7 @@ import java.lang.annotation.Target;
* {@code $LOCK} is used. These will be generated if needed and if they aren't already present. The contents
* of the fields will be serializable.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Synchronized.html">the project lombok features page for &#64;Synchronized</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Synchronized.html">the project lombok features page for &#64;Synchronized</a>.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
diff --git a/src/core/lombok/ToString.java b/src/core/lombok/ToString.java
index e87c71e7..b9926148 100644
--- a/src/core/lombok/ToString.java
+++ b/src/core/lombok/ToString.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Generates an implementation for the {@code toString} method inherited by all objects, consisting of printing the values of relevant fields.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/ToString.html">the project lombok features page for &#64;ToString</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/ToString.html">the project lombok features page for &#64;ToString</a>.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
diff --git a/src/core/lombok/Value.java b/src/core/lombok/Value.java
index 2cffe15b..2cecea63 100644
--- a/src/core/lombok/Value.java
+++ b/src/core/lombok/Value.java
@@ -29,13 +29,13 @@ import java.lang.annotation.Target;
/**
* Generates a lot of code which fits with a class that is a representation of an immutable entity.
* <p>
- * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
+ * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @AllArgsConstructor @ToString @EqualsAndHashCode}.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
*
* @see lombok.Getter
* @see lombok.experimental.FieldDefaults
- * @see lombok.RequiredArgsConstructor
+ * @see lombok.AllArgsConstructor
* @see lombok.ToString
* @see lombok.EqualsAndHashCode
* @see lombok.Data
diff --git a/src/core/lombok/core/Main.java b/src/core/lombok/core/Main.java
index dc613b0d..6952ab78 100644
--- a/src/core/lombok/core/Main.java
+++ b/src/core/lombok/core/Main.java
@@ -95,7 +95,7 @@ public class Main {
in.close();
}
} catch (Exception e) {
- System.err.println("License file not found. Check http://projectlombok.org/LICENSE");
+ System.err.println("License file not found. Check https://projectlombok.org/LICENSE");
return 1;
}
}
diff --git a/src/core/lombok/core/TypeLibrary.java b/src/core/lombok/core/TypeLibrary.java
index dc557c47..cdaf7a70 100644
--- a/src/core/lombok/core/TypeLibrary.java
+++ b/src/core/lombok/core/TypeLibrary.java
@@ -50,13 +50,20 @@ public class TypeLibrary {
}
private TypeLibrary(String fqnSingleton) {
- unqualifiedToQualifiedMap = null;
- qualified = fqnSingleton;
- int idx = fqnSingleton.lastIndexOf('.');
- if (idx == -1) {
- unqualified = fqnSingleton;
+ if (fqnSingleton.indexOf("$") != -1) {
+ unqualifiedToQualifiedMap = new HashMap<String, String>();
+ unqualified = null;
+ qualified = null;
+ addType(fqnSingleton);
} else {
- unqualified = fqnSingleton.substring(idx + 1);
+ unqualifiedToQualifiedMap = null;
+ qualified = fqnSingleton;
+ int idx = fqnSingleton.lastIndexOf('.');
+ if (idx == -1) {
+ unqualified = fqnSingleton;
+ } else {
+ unqualified = fqnSingleton.substring(idx + 1);
+ }
}
locked = true;
}
@@ -71,18 +78,29 @@ public class TypeLibrary {
* @param fullyQualifiedTypeName the FQN type name, such as 'java.lang.String'.
*/
public void addType(String fullyQualifiedTypeName) {
+ String dotBased = fullyQualifiedTypeName.replace("$", ".");
+
if (locked) throw new IllegalStateException("locked");
- fullyQualifiedTypeName = fullyQualifiedTypeName.replace("$", ".");
int idx = fullyQualifiedTypeName.lastIndexOf('.');
if (idx == -1) throw new IllegalArgumentException(
"Only fully qualified types are allowed (and stuff in the default package is not palatable to us either!)");
String unqualified = fullyQualifiedTypeName.substring(idx + 1);
if (unqualifiedToQualifiedMap == null) throw new IllegalStateException("SingleType library");
- unqualifiedToQualifiedMap.put(unqualified, fullyQualifiedTypeName);
- unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, fullyQualifiedTypeName);
+ unqualifiedToQualifiedMap.put(unqualified.replace("$", "."), dotBased);
+ unqualifiedToQualifiedMap.put(unqualified, dotBased);
+ unqualifiedToQualifiedMap.put(fullyQualifiedTypeName, dotBased);
+ unqualifiedToQualifiedMap.put(dotBased, dotBased);
for (Map.Entry<String, String> e : LombokInternalAliasing.ALIASES.entrySet()) {
- if (fullyQualifiedTypeName.equals(e.getValue())) unqualifiedToQualifiedMap.put(e.getKey(), fullyQualifiedTypeName);
+ if (fullyQualifiedTypeName.equals(e.getValue())) unqualifiedToQualifiedMap.put(e.getKey(), dotBased);
+ }
+
+ int idx2 = fullyQualifiedTypeName.indexOf('$', idx + 1);
+ while (idx2 != -1) {
+ String unq = fullyQualifiedTypeName.substring(idx2 + 1);
+ unqualifiedToQualifiedMap.put(unq.replace("$", "."), dotBased);
+ unqualifiedToQualifiedMap.put(unq, dotBased);
+ idx2 = fullyQualifiedTypeName.indexOf('$', idx2 + 1);
}
}
diff --git a/src/core/lombok/core/TypeResolver.java b/src/core/lombok/core/TypeResolver.java
index 287a085f..60ac6b6a 100644
--- a/src/core/lombok/core/TypeResolver.java
+++ b/src/core/lombok/core/TypeResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2013 The Project Lombok Authors.
+ * Copyright (C) 2009-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -53,10 +53,13 @@ public class TypeResolver {
if (typeRef.equals(qualified)) return typeRef;
// When asking if 'Getter' could possibly be referring to 'lombok.Getter' if 'import lombok.Getter;' is in the source file, the answer is yes.
- String fromExplicitImport = imports.getFullyQualifiedNameForSimpleName(typeRef);
+ int firstDot = typeRef.indexOf('.');
+ if (firstDot == -1) firstDot = typeRef.length();
+ String firstTypeRef = typeRef.substring(0, firstDot);
+ String fromExplicitImport = imports.getFullyQualifiedNameForSimpleName(firstTypeRef);
if (fromExplicitImport != null) {
// ... and if 'import foobar.Getter;' is in the source file, the answer is no.
- return fromExplicitImport.equals(qualified) ? qualified : null;
+ return (fromExplicitImport + typeRef.substring(firstDot)).equals(qualified) ? qualified : null;
}
// When asking if 'Getter' could possibly be referring to 'lombok.Getter' and 'import lombok.*; / package lombok;' isn't in the source file. the answer is no.
@@ -68,7 +71,7 @@ public class TypeResolver {
mainLoop:
while (n != null) {
- if (n.getKind() == Kind.TYPE && typeRef.equals(n.getName())) {
+ if (n.getKind() == Kind.TYPE && firstTypeRef.equals(n.getName())) {
// Our own class or one of our outer classes is named 'typeRef' so that's what 'typeRef' is referring to, not one of our type library classes.
return null;
}
@@ -81,7 +84,7 @@ public class TypeResolver {
for (LombokNode<?, ?, ?> child : newN.down()) {
// We found a method local with the same name above our code. That's the one 'typeRef' is referring to, not
// anything in the type library we're trying to find, so, no matches.
- if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null;
+ if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null;
if (child == n) break;
}
}
@@ -92,14 +95,13 @@ public class TypeResolver {
if (n.getKind() == Kind.TYPE || n.getKind() == Kind.COMPILATION_UNIT) {
for (LombokNode<?, ?, ?> child : n.down()) {
// Inner class that's visible to us has 'typeRef' as name, so that's the one being referred to, not one of our type library classes.
- if (child.getKind() == Kind.TYPE && typeRef.equals(child.getName())) return null;
+ if (child.getKind() == Kind.TYPE && firstTypeRef.equals(child.getName())) return null;
}
}
n = n.directUp();
}
-
return qualified;
}
}
diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java
index f072a632..a0f98ad1 100644
--- a/src/core/lombok/core/Version.java
+++ b/src/core/lombok/core/Version.java
@@ -30,7 +30,7 @@ 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.3";
+ private static final String VERSION = "1.16.7";
private static final String RELEASE_NAME = "Edgy Guinea Pig";
// private static final String RELEASE_NAME = "Candid Duck";
diff --git a/src/core/lombok/core/configuration/BubblingConfigurationResolver.java b/src/core/lombok/core/configuration/BubblingConfigurationResolver.java
index f96b4468..0849eee2 100644
--- a/src/core/lombok/core/configuration/BubblingConfigurationResolver.java
+++ b/src/core/lombok/core/configuration/BubblingConfigurationResolver.java
@@ -45,32 +45,23 @@ public class BubblingConfigurationResolver implements ConfigurationResolver {
Result result = source.resolve(key);
if (result == null) continue;
if (isList) {
- if (listModificationsList == null) {
- listModificationsList = new ArrayList<List<ListModification>>();
- }
+ if (listModificationsList == null) listModificationsList = new ArrayList<List<ListModification>>();
listModificationsList.add((List<ListModification>)result.getValue());
}
if (result.isAuthoritative()) {
- if (isList) {
- break;
- }
+ if (isList) break;
return (T) result.getValue();
}
}
- if (!isList) {
- return null;
- }
- if (listModificationsList == null) {
- return (T) Collections.emptyList();
- }
+ if (!isList) return null;
+ if (listModificationsList == null) return (T) Collections.emptyList();
+
List<Object> listValues = new ArrayList<Object>();
Collections.reverse(listModificationsList);
for (List<ListModification> listModifications : listModificationsList) {
if (listModifications != null) for (ListModification modification : listModifications) {
listValues.remove(modification.getValue());
- if (modification.isAdded()) {
- listValues.add(modification.getValue());
- }
+ if (modification.isAdded()) listValues.add(modification.getValue());
}
}
return (T) listValues;
diff --git a/src/core/lombok/core/configuration/FileSystemSourceCache.java b/src/core/lombok/core/configuration/FileSystemSourceCache.java
index dc390eb6..8bc5050b 100644
--- a/src/core/lombok/core/configuration/FileSystemSourceCache.java
+++ b/src/core/lombok/core/configuration/FileSystemSourceCache.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Project Lombok Authors.
+ * Copyright (C) 2014-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -38,41 +38,68 @@ import lombok.core.debug.ProblemReporter;
public class FileSystemSourceCache {
private static final String LOMBOK_CONFIG_FILENAME = "lombok.config";
+ private static final long FULL_CACHE_CLEAR_INTERVAL = TimeUnit.MINUTES.toMillis(30);
private static final long RECHECK_FILESYSTEM = TimeUnit.SECONDS.toMillis(2);
private static final long NEVER_CHECKED = -1;
private static final long MISSING = -88; // Magic value; any lombok.config with this exact epochmillis last modified will never be read, so, let's ensure nobody accidentally has one with that exact last modified stamp.
- private final ConcurrentMap<File, Content> cache = new ConcurrentHashMap<File, Content>();
+ private final ConcurrentMap<File, Content> dirCache = new ConcurrentHashMap<File, Content>(); // caches files (representing dirs) to the content object that tracks content.
+ private final ConcurrentMap<URI, File> uriCache = new ConcurrentHashMap<URI, File>(); // caches URIs of java source files to the dir that contains it.
+ private volatile long lastCacheClear = System.currentTimeMillis();
+
+ private void cacheClear() {
+ // We never clear the caches, generally because it'd be weird if a compile run would continually create an endless stream of new java files.
+ // Still, eventually that's going to cause a bit of a memory leak, so lets just completely clear them out every many minutes.
+ long now = System.currentTimeMillis();
+ long delta = now - lastCacheClear;
+ if (delta > FULL_CACHE_CLEAR_INTERVAL) {
+ lastCacheClear = now;
+ dirCache.clear();
+ uriCache.clear();
+ }
+ }
public Iterable<ConfigurationSource> sourcesForJavaFile(URI javaFile, ConfigurationProblemReporter reporter) {
if (javaFile == null) return Collections.emptyList();
- URI uri = javaFile.normalize();
- if (!uri.isAbsolute()) uri = URI.create("file:" + uri.toString());
-
- File file;
- try {
- file = new File(uri);
- if (!file.exists()) throw new IllegalArgumentException("File does not exist: " + uri);
- return sourcesForDirectory(file.getParentFile(), reporter);
- } catch (IllegalArgumentException e) {
- // This means that the file as passed is not actually a file at all, and some exotic path system is involved.
- // examples: sourcecontrol://jazz stuff, or an actual relative path (uri.isAbsolute() is completely different, that checks presence of schema!),
- // or it's eclipse trying to parse a snippet, which has "/Foo.java" as uri.
- // At some point it might be worth investigating abstracting away the notion of "I can read lombok.config if present in
- // current context, and I can give you may parent context", using ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(javaFile) as basis.
+ cacheClear();
+ File dir = uriCache.get(javaFile);
+ if (dir == null) {
+ URI uri = javaFile.normalize();
+ if (!uri.isAbsolute()) uri = URI.create("file:" + uri.toString());
- // For now, we just carry on as if there is no lombok.config. (intentional fallthrough)
- } catch (Exception e) {
- // Especially for eclipse's sake, exceptions here make eclipse borderline unusable, so let's play nice.
- ProblemReporter.error("Can't find absolute path of file being compiled: " + javaFile, e);
+ try {
+ File file = new File(uri);
+ if (!file.exists()) throw new IllegalArgumentException("File does not exist: " + uri);
+ dir = file.isDirectory() ? file : file.getParentFile();
+ if (dir != null) uriCache.put(javaFile, dir);
+ } catch (IllegalArgumentException e) {
+ // This means that the file as passed is not actually a file at all, and some exotic path system is involved.
+ // examples: sourcecontrol://jazz stuff, or an actual relative path (uri.isAbsolute() is completely different, that checks presence of schema!),
+ // or it's eclipse trying to parse a snippet, which has "/Foo.java" as uri.
+ // At some point it might be worth investigating abstracting away the notion of "I can read lombok.config if present in
+ // current context, and I can give you may parent context", using ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(javaFile) as basis.
+
+ // For now, we just carry on as if there is no lombok.config. (intentional fallthrough)
+ } catch (Exception e) {
+ // Especially for eclipse's sake, exceptions here make eclipse borderline unusable, so let's play nice.
+ ProblemReporter.error("Can't find absolute path of file being compiled: " + javaFile, e);
+ }
+ }
+
+ if (dir != null) {
+ try {
+ return sourcesForDirectory(dir, reporter);
+ } catch (Exception e) {
+ // Especially for eclipse's sake, exceptions here make eclipse borderline unusable, so let's play nice.
+ ProblemReporter.error("Can't resolve config stack for dir: " + dir.getAbsolutePath(), e);
+ }
}
return Collections.emptyList();
}
public Iterable<ConfigurationSource> sourcesForDirectory(URI directory, ConfigurationProblemReporter reporter) {
- if (directory == null) return Collections.emptyList();
- return sourcesForDirectory(new File(directory.normalize()), reporter);
+ return sourcesForJavaFile(directory, reporter);
}
private Iterable<ConfigurationSource> sourcesForDirectory(final File directory, final ConfigurationProblemReporter reporter) {
@@ -139,12 +166,12 @@ public class FileSystemSourceCache {
}
private Content ensureContent(File directory) {
- Content content = cache.get(directory);
+ Content content = dirCache.get(directory);
if (content != null) {
return content;
}
- cache.putIfAbsent(directory, Content.empty());
- return cache.get(directory);
+ dirCache.putIfAbsent(directory, Content.empty());
+ return dirCache.get(directory);
}
private ConfigurationSource parse(File configFile, ConfigurationProblemReporter reporter) {
diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java
index 87462921..a9a4be78 100644
--- a/src/core/lombok/core/handlers/HandlerUtil.java
+++ b/src/core/lombok/core/handlers/HandlerUtil.java
@@ -68,6 +68,10 @@ public class HandlerUtil {
return 97;
}
+ public static int primeForNull() {
+ return 43;
+ }
+
/** Checks if the given name is a valid identifier.
*
* If it is, this returns {@code true} and does nothing else.
diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java
index 6df5c4d7..6741b33a 100644
--- a/src/core/lombok/eclipse/EclipseAST.java
+++ b/src/core/lombok/eclipse/EclipseAST.java
@@ -70,7 +70,7 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
}
private static volatile boolean skipEclipseWorkspaceBasedFileResolver = false;
- private static final URI NOT_CALCULATED_MARKER = URI.create("http://projectlombok.org/not/calculated");
+ private static final URI NOT_CALCULATED_MARKER = URI.create("https://projectlombok.org/not/calculated");
private URI memoizedAbsoluteFileLocation = NOT_CALCULATED_MARKER;
public URI getAbsoluteFileLocation() {
diff --git a/src/core/lombok/eclipse/EclipseASTVisitor.java b/src/core/lombok/eclipse/EclipseASTVisitor.java
index aa19adc6..f5b49cbb 100644
--- a/src/core/lombok/eclipse/EclipseASTVisitor.java
+++ b/src/core/lombok/eclipse/EclipseASTVisitor.java
@@ -40,7 +40,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
/**
- * Implement so you can ask any JavacAST.Node to traverse depth-first through all children,
+ * Implement so you can ask any EclipseAST.Node to traverse depth-first through all children,
* calling the appropriate visit and endVisit methods.
*/
public interface EclipseASTVisitor {
diff --git a/src/core/lombok/eclipse/EclipseNode.java b/src/core/lombok/eclipse/EclipseNode.java
index 2c970db2..49867e62 100644
--- a/src/core/lombok/eclipse/EclipseNode.java
+++ b/src/core/lombok/eclipse/EclipseNode.java
@@ -54,70 +54,70 @@ public class EclipseNode extends lombok.core.LombokNode<EclipseAST, EclipseNode,
switch (getKind()) {
case COMPILATION_UNIT:
- visitor.visitCompilationUnit(this, (CompilationUnitDeclaration)get());
+ visitor.visitCompilationUnit(this, (CompilationUnitDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitCompilationUnit(this, (CompilationUnitDeclaration)get());
+ visitor.endVisitCompilationUnit(this, (CompilationUnitDeclaration) get());
break;
case TYPE:
- visitor.visitType(this, (TypeDeclaration)get());
+ visitor.visitType(this, (TypeDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitType(this, (TypeDeclaration)get());
+ visitor.endVisitType(this, (TypeDeclaration) get());
break;
case FIELD:
- visitor.visitField(this, (FieldDeclaration)get());
+ visitor.visitField(this, (FieldDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitField(this, (FieldDeclaration)get());
+ visitor.endVisitField(this, (FieldDeclaration) get());
break;
case INITIALIZER:
- visitor.visitInitializer(this, (Initializer)get());
+ visitor.visitInitializer(this, (Initializer) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitInitializer(this, (Initializer)get());
+ visitor.endVisitInitializer(this, (Initializer) get());
break;
case METHOD:
if (get() instanceof Clinit) return;
- visitor.visitMethod(this, (AbstractMethodDeclaration)get());
+ visitor.visitMethod(this, (AbstractMethodDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitMethod(this, (AbstractMethodDeclaration)get());
+ visitor.endVisitMethod(this, (AbstractMethodDeclaration) get());
break;
case ARGUMENT:
- AbstractMethodDeclaration method = (AbstractMethodDeclaration)up().get();
- visitor.visitMethodArgument(this, (Argument)get(), method);
+ AbstractMethodDeclaration method = (AbstractMethodDeclaration) up().get();
+ visitor.visitMethodArgument(this, (Argument) get(), method);
ast.traverseChildren(visitor, this);
- visitor.endVisitMethodArgument(this, (Argument)get(), method);
+ visitor.endVisitMethodArgument(this, (Argument) get(), method);
break;
case LOCAL:
- visitor.visitLocal(this, (LocalDeclaration)get());
+ visitor.visitLocal(this, (LocalDeclaration) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitLocal(this, (LocalDeclaration)get());
+ visitor.endVisitLocal(this, (LocalDeclaration) get());
break;
case ANNOTATION:
switch (up().getKind()) {
case TYPE:
- visitor.visitAnnotationOnType((TypeDeclaration)up().get(), this, (Annotation)get());
+ visitor.visitAnnotationOnType((TypeDeclaration) up().get(), this, (Annotation) get());
break;
case FIELD:
- visitor.visitAnnotationOnField((FieldDeclaration)up().get(), this, (Annotation)get());
+ visitor.visitAnnotationOnField((FieldDeclaration) up().get(), this, (Annotation) get());
break;
case METHOD:
- visitor.visitAnnotationOnMethod((AbstractMethodDeclaration)up().get(), this, (Annotation)get());
+ visitor.visitAnnotationOnMethod((AbstractMethodDeclaration) up().get(), this, (Annotation) get());
break;
case ARGUMENT:
visitor.visitAnnotationOnMethodArgument(
- (Argument)parent.get(),
- (AbstractMethodDeclaration)parent.directUp().get(),
- this, (Annotation)get());
+ (Argument) parent.get(),
+ (AbstractMethodDeclaration) parent.directUp().get(),
+ this, (Annotation) get());
break;
case LOCAL:
- visitor.visitAnnotationOnLocal((LocalDeclaration)parent.get(), this, (Annotation)get());
+ visitor.visitAnnotationOnLocal((LocalDeclaration) parent.get(), this, (Annotation) get());
break;
default:
throw new AssertionError("Annotation not expected as child of a " + up().getKind());
}
break;
case STATEMENT:
- visitor.visitStatement(this, (Statement)get());
+ visitor.visitStatement(this, (Statement) get());
ast.traverseChildren(visitor, this);
- visitor.endVisitStatement(this, (Statement)get());
+ visitor.endVisitStatement(this, (Statement) get());
break;
default:
throw new AssertionError("Unexpected kind during node traversal: " + getKind());
diff --git a/src/core/lombok/eclipse/TransformEclipseAST.java b/src/core/lombok/eclipse/TransformEclipseAST.java
index e6528178..683465c9 100644
--- a/src/core/lombok/eclipse/TransformEclipseAST.java
+++ b/src/core/lombok/eclipse/TransformEclipseAST.java
@@ -106,7 +106,7 @@ public class TransformEclipseAST {
EclipseAST existing = null;
if (astCacheField != null) {
try {
- existing = (EclipseAST)astCacheField.get(ast);
+ existing = (EclipseAST) astCacheField.get(ast);
} catch (Exception e) {
// existing remains null
}
diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
index 836a51c6..0ff5a7f6 100644
--- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
+++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
@@ -450,7 +450,7 @@ public class EclipseHandlerUtil {
*/
public static boolean annotationTypeMatches(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) {
if (node.getKind() != Kind.ANNOTATION) return false;
- return typeMatches(type, node, ((Annotation)node.get()).type);
+ return typeMatches(type, node, ((Annotation) node.get()).type);
}
public static TypeReference cloneSelfType(EclipseNode context) {
diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java
index 2d4db64f..be14653a 100644
--- a/src/core/lombok/eclipse/handlers/HandleBuilder.java
+++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java
@@ -70,6 +70,7 @@ import org.mangosdk.spi.ProviderFor;
import lombok.AccessLevel;
import lombok.Builder;
+import lombok.Builder.ObtainVia;
import lombok.ConfigurationKeys;
import lombok.Singular;
import lombok.core.AST.Kind;
@@ -99,12 +100,33 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
private static class BuilderFieldData {
TypeReference type;
+ char[] rawName;
char[] name;
SingularData singularData;
+ ObtainVia obtainVia;
+ EclipseNode obtainViaNode;
List<EclipseNode> createdFields = new ArrayList<EclipseNode>();
}
+ private static boolean equals(String a, char[] b) {
+ if (a.length() != b.length) return false;
+ for (int i = 0; i < b.length; i++) {
+ if (a.charAt(i) != b[i]) return false;
+ }
+ return true;
+ }
+
+ private static boolean equals(String a, char[][] b) {
+ if (a == null || a.isEmpty()) return b.length == 0;
+ String[] aParts = a.split("\\.");
+ if (aParts.length != b.length) return false;
+ for (int i = 0; i < b.length; i++) {
+ if (!equals(aParts[i], b[i])) return false;
+ }
+ return true;
+ }
+
@Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) {
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
@@ -117,6 +139,9 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
+ String toBuilderMethodName = "toBuilder";
+ boolean toBuilder = builderInstance.toBuilder();
+ List<char[]> typeArgsForToBuilder = null;
if (builderMethodName == null) builderMethodName = "builder";
if (buildMethodName == null) builderMethodName = "build";
@@ -155,15 +180,17 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
// 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;
BuilderFieldData bfd = new BuilderFieldData();
+ bfd.rawName = fieldNode.getName().toCharArray();
bfd.name = removePrefixFromField(fieldNode);
bfd.type = fd.type;
bfd.singularData = getSingularData(fieldNode, ast);
+ addObtainVia(bfd, fieldNode);
builderFields.add(bfd);
allFields.add(fieldNode);
}
- new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, null, SkipIfConstructorExists.I_AM_BUILDER, null,
- Collections.<Annotation>emptyList(), annotationNode);
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, null,
+ Collections.<Annotation>emptyList(), annotationNode);
returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p);
typeParams = td.typeParameters;
@@ -188,6 +215,78 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
MethodDeclaration md = (MethodDeclaration) parent.get();
tdParent = parent.up();
isStatic = md.isStatic();
+
+ if (toBuilder) {
+ final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
+ char[] token;
+ char[][] pkg = null;
+ if (md.returnType.dimensions() > 0) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (md.returnType instanceof SingleTypeReference) {
+ token = ((SingleTypeReference) md.returnType).token;
+ } else if (md.returnType instanceof QualifiedTypeReference) {
+ pkg = ((QualifiedTypeReference) md.returnType).tokens;
+ token = pkg[pkg.length];
+ char[][] pkg_ = new char[pkg.length - 1][];
+ System.arraycopy(pkg, 0, pkg_, 0, pkg_.length);
+ pkg = pkg_;
+ } else {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (pkg != null && !equals(parent.getPackageDeclaration(), pkg)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (tdParent == null || !equals(tdParent.getName(), token)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ TypeParameter[] tpOnType = ((TypeDeclaration) tdParent.get()).typeParameters;
+ TypeParameter[] tpOnMethod = md.typeParameters;
+ TypeReference[][] tpOnRet_ = null;
+ if (md.returnType instanceof ParameterizedSingleTypeReference) {
+ tpOnRet_ = new TypeReference[1][];
+ tpOnRet_[0] = ((ParameterizedSingleTypeReference) md.returnType).typeArguments;
+ } else if (md.returnType instanceof ParameterizedQualifiedTypeReference) {
+ tpOnRet_ = ((ParameterizedQualifiedTypeReference) md.returnType).typeArguments;
+ }
+
+ if (tpOnRet_ != null) for (int i = 0; i < tpOnRet_.length - 1; i++) {
+ if (tpOnRet_[i] != null && tpOnRet_[i].length > 0) {
+ annotationNode.addError("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate.");
+ return;
+ }
+ }
+ TypeReference[] tpOnRet = tpOnRet_ == null ? null : tpOnRet_[tpOnRet_.length - 1];
+ typeArgsForToBuilder = new ArrayList<char[]>();
+
+ // Every typearg on this method needs to be found in the return type, but the reverse is not true.
+ // We also need to 'map' them.
+
+
+ if (tpOnMethod != null) for (TypeParameter onMethod : tpOnMethod) {
+ int pos = -1;
+ if (tpOnRet != null) for (int i = 0; i < tpOnRet.length; i++) {
+ if (tpOnRet[i].getClass() != SingleTypeReference.class) continue;
+ if (!Arrays.equals(((SingleTypeReference) tpOnRet[i]).token, onMethod.name)) continue;
+ pos = i;
+ }
+ if (pos == -1 || tpOnType == null || tpOnType.length <= pos) {
+ annotationNode.addError("@Builder(toBuilder=true) requires that each type parameter on the static method is part of the typeargs of the return value. Type parameter " + new String(onMethod.name) + " is not part of the return type.");
+ return;
+ }
+
+ typeArgsForToBuilder.add(tpOnType[pos].name);
+ }
+ }
+
returnType = copyType(md.returnType, ast);
typeParams = md.typeParameters;
thrownExceptions = md.thrownExceptions;
@@ -231,9 +330,11 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
if (param.getKind() != Kind.ARGUMENT) continue;
BuilderFieldData bfd = new BuilderFieldData();
Argument arg = (Argument) param.get();
+ bfd.rawName = arg.name;
bfd.name = arg.name;
bfd.type = arg.type;
bfd.singularData = getSingularData(param, ast);
+ addObtainVia(bfd, param);
builderFields.add(bfd);
}
}
@@ -271,6 +372,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
break;
}
}
+ if (bfd.obtainVia != null) {
+ if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) {
+ bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\").");
+ return;
+ }
+ if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) {
+ bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set.");
+ return;
+ }
+ }
}
generateBuilderFields(builderType, builderFields, ast);
@@ -279,14 +390,13 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
cleanDecl.declarationSourceEnd = -1;
cleanDecl.modifiers = ClassFileConstants.AccPrivate;
cleanDecl.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0);
- System.out.println("INJECTING: cleaning");
injectFieldAndMarkGenerated(builderType, cleanDecl);
}
if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) {
ConstructorDeclaration cd = HandleConstructor.createConstructor(
- AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), null,
- annotationNode, Collections.<Annotation>emptyList());
+ AccessLevel.PACKAGE, builderType, Collections.<EclipseNode>emptyList(), false, null,
+ annotationNode, Collections.<Annotation>emptyList());
if (cd != null) injectMethod(builderType, cd);
}
@@ -317,6 +427,69 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
MethodDeclaration md = generateBuilderMethod(isStatic, builderMethodName, builderClassName, tdParent, typeParams, ast);
if (md != null) injectMethod(tdParent, md);
}
+
+ if (toBuilder) switch (methodExists(toBuilderMethodName, tdParent, 0)) {
+ case EXISTS_BY_USER:
+ annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+ break;
+ case NOT_EXISTS:
+ TypeParameter[] tps = typeParams;
+ if (typeArgsForToBuilder != null) {
+ tps = new TypeParameter[typeArgsForToBuilder.size()];
+ for (int i = 0; i < tps.length; i++) {
+ tps[i] = new TypeParameter();
+ tps[i].name = typeArgsForToBuilder.get(i);
+ }
+ }
+ MethodDeclaration md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast);
+
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
+ private MethodDeclaration generateToBuilderMethod(String methodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, List<BuilderFieldData> builderFields, boolean fluent, ASTNode source) {
+ // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b);
+
+ int pS = source.sourceStart, pE = source.sourceEnd;
+ long p = (long) pS << 32 | pE;
+
+ MethodDeclaration out = new MethodDeclaration(
+ ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ out.selector = methodName.toCharArray();
+ out.modifiers = ClassFileConstants.AccPublic;
+ out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ out.returnType = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+ AllocationExpression invoke = new AllocationExpression();
+ invoke.type = namePlusTypeParamsToTypeReference(builderClassName.toCharArray(), typeParams, p);
+
+ Expression receiver = invoke;
+ for (BuilderFieldData bfd : builderFields) {
+ char[] setterName = fluent ? bfd.name : HandlerUtil.buildAccessorName("set", new String(bfd.name)).toCharArray();
+ MessageSend ms = new MessageSend();
+ if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+ char[] fieldName = bfd.obtainVia == null ? bfd.rawName : bfd.obtainVia.field().toCharArray();
+ FieldReference fr = new FieldReference(fieldName, 0);
+ fr.receiver = new ThisReference(0, 0);
+ ms.arguments = new Expression[] {fr};
+ } else {
+ String obtainName = bfd.obtainVia.method();
+ boolean obtainIsStatic = bfd.obtainVia.isStatic();
+ MessageSend obtainExpr = new MessageSend();
+ obtainExpr.receiver = obtainIsStatic ? new SingleNameReference(type.getName().toCharArray(), 0) : new ThisReference(0, 0);
+ obtainExpr.selector = obtainName.toCharArray();
+ if (obtainIsStatic) obtainExpr.arguments = new Expression[] {new ThisReference(0, 0)};
+ ms.arguments = new Expression[] {obtainExpr};
+ }
+ ms.receiver = receiver;
+ ms.selector = setterName;
+ receiver = ms;
+ }
+
+ out.statements = new Statement[] {new ReturnStatement(receiver, pS, pE)};
+
+ out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) type.get()).scope);
+ return out;
+
}
private MethodDeclaration generateCleanMethod(List<BuilderFieldData> builderFields, EclipseNode builderType, ASTNode source) {
@@ -342,8 +515,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
public MethodDeclaration generateBuildMethod(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);
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
List<Statement> statements = new ArrayList<Statement>();
@@ -415,8 +587,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;
- MethodDeclaration out = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
out.selector = builderMethodName.toCharArray();
out.modifiers = ClassFileConstants.AccPublic;
if (isStatic) out.modifiers |= ClassFileConstants.AccStatic;
@@ -487,7 +658,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName());
MethodDeclaration setter = HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic,
- sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
+ sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList());
injectMethod(builderType, setter);
}
@@ -513,6 +684,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
return injectType(tdParent, builder);
}
+ private void addObtainVia(BuilderFieldData bfd, EclipseNode node) {
+ for (EclipseNode child : node.down()) {
+ if (!annotationTypeMatches(ObtainVia.class, child)) continue;
+ AnnotationValues<ObtainVia> ann = createAnnotation(ObtainVia.class, child);
+ bfd.obtainVia = ann.getInstance();
+ bfd.obtainViaNode = child;
+ return;
+ }
+ }
+
/**
* Returns the explicitly requested singular annotation on this node (field
* or parameter), or null if there's no {@code @Singular} annotation on it.
@@ -521,53 +702,52 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
*/
private SingularData getSingularData(EclipseNode node, ASTNode source) {
for (EclipseNode child : node.down()) {
- if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Singular.class, child)) {
- char[] pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((AbstractVariableDeclaration) node.get()).name;
- AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
- String explicitSingular = ann.getInstance().value();
- if (explicitSingular.isEmpty()) {
- if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
- node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ 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(node.getName());
+ if (explicitSingular == null) {
+ node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
explicitSingular = new String(pluralName);
- } else {
- explicitSingular = autoSingularize(node.getName());
- if (explicitSingular == null) {
- node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
- explicitSingular = new String(pluralName);
- }
- }
- }
- char[] singularName = explicitSingular.toCharArray();
-
- TypeReference type = ((AbstractVariableDeclaration) node.get()).type;
- TypeReference[] typeArgs = null;
- String typeName;
- if (type instanceof ParameterizedSingleTypeReference) {
- typeArgs = ((ParameterizedSingleTypeReference) type).typeArguments;
- typeName = new String(((ParameterizedSingleTypeReference) type).token);
- } else if (type instanceof ParameterizedQualifiedTypeReference) {
- TypeReference[][] tr = ((ParameterizedQualifiedTypeReference) type).typeArguments;
- if (tr != null) typeArgs = tr[tr.length - 1];
- char[][] tokens = ((ParameterizedQualifiedTypeReference) type).tokens;
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < tokens.length; i++) {
- if (i > 0) sb.append(".");
- sb.append(tokens[i]);
}
- typeName = sb.toString();
- } else {
- typeName = type.toString();
}
-
- String targetFqn = EclipseSingularsRecipes.get().toQualified(typeName);
- EclipseSingularizer singularizer = EclipseSingularsRecipes.get().getSingularizer(targetFqn);
- if (singularizer == null) {
- node.addError("Lombok does not know how to create the singular-form builder methods for type '" + typeName + "'; they won't be generated.");
- return null;
+ }
+ 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]);
}
-
- return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source);
+ typeName = sb.toString();
+ } else {
+ typeName = type.toString();
}
+
+ String targetFqn = EclipseSingularsRecipes.get().toQualified(typeName);
+ EclipseSingularizer singularizer = EclipseSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + typeName + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs == null ? Collections.<TypeReference>emptyList() : Arrays.asList(typeArgs), targetFqn, singularizer, source);
}
return null;
diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java
index 5bcc803a..85d1d4ed 100644
--- a/src/core/lombok/eclipse/handlers/HandleConstructor.java
+++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Project Lombok Authors.
+ * Copyright (C) 2010-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -29,6 +29,7 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import lombok.AccessLevel;
@@ -48,13 +49,20 @@ 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.Assignment;
+import org.eclipse.jdt.internal.compiler.ast.CharLiteral;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.DoubleLiteral;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.FloatLiteral;
+import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
+import org.eclipse.jdt.internal.compiler.ast.LongLiteral;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
@@ -63,7 +71,9 @@ import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.mangosdk.spi.ProviderFor;
public class HandleConstructor {
@@ -78,11 +88,13 @@ public class HandleConstructor {
AccessLevel level = ann.access();
String staticName = ann.staticName();
if (level == AccessLevel.NONE) return;
- List<EclipseNode> fields = new ArrayList<EclipseNode>();
+ boolean force = ann.force();
+
+ List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList();
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor=", annotationNode);
- new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, null, onConstructor, annotationNode);
}
}
@@ -107,19 +119,27 @@ public class HandleConstructor {
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor=", annotationNode);
new HandleConstructor().generateConstructor(
- typeNode, level, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO,
- suppressConstructorProperties, onConstructor, annotationNode);
+ typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO,
+ suppressConstructorProperties, onConstructor, annotationNode);
}
}
private static List<EclipseNode> findRequiredFields(EclipseNode typeNode) {
+ return findFields(typeNode, true);
+ }
+
+ private static List<EclipseNode> findFinalFields(EclipseNode typeNode) {
+ return findFields(typeNode, false);
+ }
+
+ private static List<EclipseNode> findFields(EclipseNode typeNode, boolean nullMarked) {
List<EclipseNode> fields = new ArrayList<EclipseNode>();
for (EclipseNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
FieldDeclaration fieldDecl = (FieldDeclaration) child.get();
if (!filterField(fieldDecl)) continue;
boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0;
- boolean isNonNull = findAnnotations(fieldDecl, NON_NULL_PATTERN).length != 0;
+ boolean isNonNull = nullMarked && findAnnotations(fieldDecl, NON_NULL_PATTERN).length != 0;
if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child);
}
return fields;
@@ -161,8 +181,8 @@ public class HandleConstructor {
List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor=", annotationNode);
new HandleConstructor().generateConstructor(
- typeNode, level, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO,
- suppressConstructorProperties, onConstructor, annotationNode);
+ typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO,
+ suppressConstructorProperties, onConstructor, annotationNode);
}
}
@@ -184,14 +204,14 @@ public class HandleConstructor {
EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode) {
- generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
+ generateConstructor(typeNode, level, findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
}
public void generateAllArgsConstructor(
EclipseNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists,
List<Annotation> onConstructor, EclipseNode sourceNode) {
- generateConstructor(typeNode, level, findAllFields(typeNode), staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
+ generateConstructor(typeNode, level, findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, onConstructor, sourceNode);
}
public enum SkipIfConstructorExists {
@@ -199,8 +219,8 @@ public class HandleConstructor {
}
public void generateConstructor(
- EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists,
- Boolean suppressConstructorProperties, List<Annotation> onConstructor, EclipseNode sourceNode) {
+ EclipseNode typeNode, AccessLevel level, List<EclipseNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists,
+ Boolean suppressConstructorProperties, List<Annotation> onConstructor, EclipseNode sourceNode) {
ASTNode source = sourceNode.get();
boolean staticConstrRequired = staticName != null && !staticName.equals("");
@@ -210,8 +230,8 @@ public class HandleConstructor {
for (EclipseNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
boolean skipGeneration = (annotationTypeMatches(NoArgsConstructor.class, child) ||
- annotationTypeMatches(AllArgsConstructor.class, child) ||
- annotationTypeMatches(RequiredArgsConstructor.class, child));
+ annotationTypeMatches(AllArgsConstructor.class, child) ||
+ annotationTypeMatches(RequiredArgsConstructor.class, child));
if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
skipGeneration = annotationTypeMatches(Builder.class, child);
@@ -224,8 +244,8 @@ public class HandleConstructor {
// the 'staticName' parameter of the @XArgsConstructor you've stuck on your type.
// We should warn that we're ignoring @Data's 'staticConstructor' param.
typeNode.addWarning(
- "Ignoring static constructor name: explicit @XxxArgsConstructor annotation present; its `staticName` parameter will be used.",
- source.sourceStart, source.sourceEnd);
+ "Ignoring static constructor name: explicit @XxxArgsConstructor annotation present; its `staticName` parameter will be used.",
+ source.sourceStart, source.sourceEnd);
}
return;
}
@@ -234,11 +254,11 @@ public class HandleConstructor {
}
ConstructorDeclaration constr = createConstructor(
- staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields,
- suppressConstructorProperties, sourceNode, onConstructor);
+ staticConstrRequired ? AccessLevel.PRIVATE : level, typeNode, fields, allToDefault,
+ suppressConstructorProperties, sourceNode, onConstructor);
injectMethod(typeNode, constr);
if (staticConstrRequired) {
- MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, fields, source);
+ MethodDeclaration staticConstr = createStaticConstructor(level, staticName, typeNode, allToDefault ? Collections.<EclipseNode>emptyList() : fields, source);
injectMethod(typeNode, staticConstr);
}
}
@@ -248,7 +268,7 @@ public class HandleConstructor {
if (fields.isEmpty()) return null;
int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
long[] poss = new long[3];
Arrays.fill(poss, p);
QualifiedTypeReference constructorPropertiesType = new QualifiedTypeReference(JAVA_BEANS_CONSTRUCTORPROPERTIES, poss);
@@ -276,14 +296,14 @@ public class HandleConstructor {
}
public static ConstructorDeclaration createConstructor(
- AccessLevel level, EclipseNode type, Collection<EclipseNode> fields,
- Boolean suppressConstructorProperties, EclipseNode sourceNode, List<Annotation> onConstructor) {
+ AccessLevel level, EclipseNode type, Collection<EclipseNode> fields, boolean allToDefault,
+ Boolean suppressConstructorProperties, EclipseNode sourceNode, List<Annotation> onConstructor) {
ASTNode source = sourceNode.get();
- TypeDeclaration typeDeclaration = ((TypeDeclaration)type.get());
- long p = (long)source.sourceStart << 32 | source.sourceEnd;
+ TypeDeclaration typeDeclaration = ((TypeDeclaration) type.get());
+ long p = (long) source.sourceStart << 32 | source.sourceEnd;
- boolean isEnum = (((TypeDeclaration)type.get()).modifiers & ClassFileConstants.AccEnum) != 0;
+ boolean isEnum = (((TypeDeclaration) type.get()).modifiers & ClassFileConstants.AccEnum) != 0;
if (isEnum) level = AccessLevel.PRIVATE;
@@ -295,8 +315,7 @@ public class HandleConstructor {
}
}
- ConstructorDeclaration constructor = new ConstructorDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
constructor.modifiers = toEclipseModifier(level);
constructor.selector = typeDeclaration.name;
@@ -319,22 +338,27 @@ public class HandleConstructor {
char[] rawName = field.name;
char[] fieldName = removePrefixFromField(fieldNode);
FieldReference thisX = new FieldReference(rawName, p);
- thisX.receiver = new ThisReference((int)(p >> 32), (int)p);
+ int s = (int) (p >> 32);
+ int e = (int) p;
+ thisX.receiver = new ThisReference(s, e);
+
+ Expression assignmentExpr = allToDefault ? getDefaultExpr(field.type, s, e) : new SingleNameReference(fieldName, p);
- SingleNameReference assignmentNameRef = new SingleNameReference(fieldName, p);
- Assignment assignment = new Assignment(thisX, assignmentNameRef, (int)p);
- assignment.sourceStart = (int)(p >> 32); assignment.sourceEnd = assignment.statementEnd = (int)(p >> 32);
+ Assignment assignment = new Assignment(thisX, assignmentExpr, (int) p);
+ assignment.sourceStart = (int) (p >> 32); assignment.sourceEnd = assignment.statementEnd = (int) (p >> 32);
assigns.add(assignment);
- long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd;
- Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL);
- Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
- Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
- if (nonNulls.length != 0) {
- Statement nullCheck = generateNullCheck(field, sourceNode);
- if (nullCheck != null) nullChecks.add(nullCheck);
+ if (!allToDefault) {
+ long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd;
+ Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL);
+ Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN);
+ Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN);
+ if (nonNulls.length != 0) {
+ Statement nullCheck = generateNullCheck(field, sourceNode);
+ if (nullCheck != null) nullChecks.add(nullCheck);
+ }
+ parameter.annotations = copyAnnotations(source, nonNulls, nullables);
+ params.add(parameter);
}
- parameter.annotations = copyAnnotations(source, nonNulls, nullables);
- params.add(parameter);
}
nullChecks.addAll(assigns);
@@ -343,19 +367,32 @@ public class HandleConstructor {
/* Generate annotations that must be put on the generated method, and attach them. */ {
Annotation[] constructorProperties = null;
- if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(type)) {
+ if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(type)) {
constructorProperties = createConstructorProperties(source, fields);
}
constructor.annotations = copyAnnotations(source,
- onConstructor.toArray(new Annotation[0]),
- constructorProperties);
+ onConstructor.toArray(new Annotation[0]),
+ constructorProperties);
}
constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope);
return constructor;
}
+ private static Expression getDefaultExpr(TypeReference type, int s, int e) {
+ char[] lastToken = type.getLastToken();
+ if (Arrays.equals(TypeConstants.BOOLEAN, lastToken)) return new FalseLiteral(s, e);
+ if (Arrays.equals(TypeConstants.CHAR, lastToken)) return new CharLiteral(new char[] {'\'', '\\', '0', '\''}, s, e);
+ if (Arrays.equals(TypeConstants.BYTE, lastToken) || Arrays.equals(TypeConstants.SHORT, lastToken) ||
+ Arrays.equals(TypeConstants.INT, lastToken)) return IntLiteral.buildIntLiteral(new char[] {'0'}, s, e);
+ if (Arrays.equals(TypeConstants.LONG, lastToken)) return LongLiteral.buildLongLiteral(new char[] {'0', 'L'}, s, e);
+ if (Arrays.equals(TypeConstants.FLOAT, lastToken)) return new FloatLiteral(new char[] {'0', 'F'}, s, e);
+ if (Arrays.equals(TypeConstants.DOUBLE, lastToken)) return new DoubleLiteral(new char[] {'0', 'D'}, s, e);
+
+ return new NullLiteral(s, e);
+ }
+
public static boolean isLocalType(EclipseNode type) {
Kind kind = type.up().getKind();
if (kind == Kind.COMPILATION_UNIT) return false;
@@ -365,10 +402,9 @@ public class HandleConstructor {
public MethodDeclaration createStaticConstructor(AccessLevel level, String name, EclipseNode type, Collection<EclipseNode> fields, ASTNode source) {
int pS = source.sourceStart, pE = source.sourceEnd;
- long p = (long)pS << 32 | pE;
+ long p = (long) pS << 32 | pE;
- MethodDeclaration constructor = new MethodDeclaration(
- ((CompilationUnitDeclaration) type.top().get()).compilationResult);
+ MethodDeclaration constructor = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult);
constructor.modifiers = toEclipseModifier(level) | ClassFileConstants.AccStatic;
TypeDeclaration typeDecl = (TypeDeclaration) type.get();
@@ -376,7 +412,7 @@ public class HandleConstructor {
constructor.annotations = null;
constructor.selector = name.toCharArray();
constructor.thrownExceptions = null;
- constructor.typeParameters = copyTypeParams(((TypeDeclaration)type.get()).typeParameters, source);
+ constructor.typeParameters = copyTypeParams(((TypeDeclaration) type.get()).typeParameters, source);
constructor.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
constructor.bodyStart = constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
constructor.bodyEnd = constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
@@ -389,7 +425,7 @@ public class HandleConstructor {
for (EclipseNode fieldNode : fields) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
- long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd;
+ long fieldPos = (((long) field.sourceStart) << 32) | field.sourceEnd;
SingleNameReference nameRef = new SingleNameReference(field.name, fieldPos);
assigns.add(nameRef);
@@ -400,7 +436,7 @@ public class HandleConstructor {
statement.arguments = assigns.isEmpty() ? null : assigns.toArray(new Expression[assigns.size()]);
constructor.arguments = params.isEmpty() ? null : params.toArray(new Argument[params.size()]);
- constructor.statements = new Statement[] { new ReturnStatement(statement, (int)(p >> 32), (int)p) };
+ constructor.statements = new Statement[] { new ReturnStatement(statement, (int) (p >> 32), (int)p) };
constructor.traverse(new SetGeneratedByVisitor(source), typeDecl.scope);
return constructor;
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index 7e2ff513..77fe3a52 100644
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -358,7 +358,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
statements.add(createResultCalculation(source, fieldAccessor));
} else /* objects */ {
/* final java.lang.Object $fieldName = this.fieldName; */
- /* $fieldName == null ? 0 : $fieldName.hashCode() */
+ /* $fieldName == null ? NULL_PRIME : $fieldName.hashCode() */
statements.add(createLocalDeclaration(source, dollarFieldName, generateQualifiedTypeRef(source, TypeConstants.JAVA_LANG_OBJECT), fieldAccessor));
SingleNameReference copy1 = new SingleNameReference(dollarFieldName, p);
@@ -375,8 +375,8 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
setGeneratedBy(nullLiteral, source);
EqualExpression objIsNull = new EqualExpression(copy2, nullLiteral, OperatorIds.EQUAL_EQUAL);
setGeneratedBy(objIsNull, source);
- IntLiteral int0 = makeIntLiteral("0".toCharArray(), source);
- ConditionalExpression nullOrHashCode = new ConditionalExpression(objIsNull, int0, hashCodeCall);
+ IntLiteral intMagic = makeIntLiteral(String.valueOf(HandlerUtil.primeForNull()).toCharArray(), source);
+ ConditionalExpression nullOrHashCode = new ConditionalExpression(objIsNull, intMagic, hashCodeCall);
nullOrHashCode.sourceStart = pS; nullOrHashCode.sourceEnd = pE;
setGeneratedBy(nullOrHashCode, source);
statements.add(createResultCalculation(source, nullOrHashCode));
diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
index 7d0702db..5ea5a210 100644
--- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java
@@ -23,12 +23,17 @@ package lombok.eclipse.handlers;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
+
+import java.util.Arrays;
+
import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
-import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseASTAdapter;
+import lombok.eclipse.EclipseASTVisitor;
import lombok.eclipse.EclipseNode;
import lombok.experimental.FieldDefaults;
import lombok.experimental.NonFinal;
@@ -37,16 +42,19 @@ import lombok.experimental.PackagePrivate;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.mangosdk.spi.ProviderFor;
/**
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
-@ProviderFor(EclipseAnnotationHandler.class)
+@ProviderFor(EclipseASTVisitor.class)
@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
-public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> {
+public class HandleFieldDefaults extends EclipseASTAdapter {
public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
if (hasAnnotation(FieldDefaults.class, typeNode)) {
@@ -96,36 +104,69 @@ public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults>
if (makeFinal && (field.modifiers & ClassFileConstants.AccFinal) == 0) {
if (!hasAnnotation(NonFinal.class, fieldNode)) {
- field.modifiers |= ClassFileConstants.AccFinal;
+ if ((field.modifiers & ClassFileConstants.AccStatic) == 0 || field.initialization != null) {
+ field.modifiers |= ClassFileConstants.AccFinal;
+ }
}
}
fieldNode.rebuild();
}
- public void handle(AnnotationValues<FieldDefaults> annotation, Annotation ast, EclipseNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
-
- EclipseNode node = annotationNode.up();
- FieldDefaults instance = annotation.getInstance();
- AccessLevel level = instance.level();
- boolean makeFinal = instance.makeFinal();
+ private static final char[] FIELD_DEFAULTS = "FieldDefaults".toCharArray();
+
+ @Override public void visitType(EclipseNode typeNode, TypeDeclaration type) {
+ AnnotationValues<FieldDefaults> fieldDefaults = null;
+ EclipseNode source = typeNode;
- if (level == AccessLevel.NONE && !makeFinal) {
- annotationNode.addError("This does nothing; provide either level or makeFinal or both.");
- return;
+ boolean levelIsExplicit = false;
+ boolean makeFinalIsExplicit = false;
+ FieldDefaults fd = null;
+ for (EclipseNode jn : typeNode.down()) {
+ if (jn.getKind() != Kind.ANNOTATION) continue;
+ Annotation ann = (Annotation) jn.get();
+ TypeReference typeTree = ann.type;
+ if (typeTree == null) continue;
+ if (typeTree instanceof SingleTypeReference) {
+ char[] t = ((SingleTypeReference) typeTree).token;
+ if (!Arrays.equals(t, FIELD_DEFAULTS)) continue;
+ } else if (typeTree instanceof QualifiedTypeReference) {
+ char[][] t = ((QualifiedTypeReference) typeTree).tokens;
+ if (!Eclipse.nameEquals(t, "lombok.experimental.FieldDefaults")) continue;
+ } else {
+ continue;
+ }
+
+ if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue;
+
+ source = jn;
+ fieldDefaults = createAnnotation(FieldDefaults.class, jn);
+ levelIsExplicit = fieldDefaults.isExplicit("level");
+ makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal");
+
+ handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
+
+ fd = fieldDefaults.getInstance();
+ if (!levelIsExplicit && !makeFinalIsExplicit) {
+ jn.addError("This does nothing; provide either level or makeFinal or both.");
+ }
+
+ if (levelIsExplicit && fd.level() == AccessLevel.NONE) {
+ jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value.");
+ levelIsExplicit = false;
+ }
+ break;
}
- if (level == AccessLevel.PACKAGE) {
- annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
- }
+ if (fd == null && (type.modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0) return;
- if (!makeFinal && annotation.isExplicit("makeFinal")) {
- annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
- }
+ boolean defaultToPrivate = levelIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE));
+ boolean defaultToFinal = makeFinalIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE));
- if (node == null) return;
+ if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return;
+ AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null;
+ boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal;
- generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
+ generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false);
}
}
diff --git a/src/core/lombok/eclipse/handlers/HandleHelper.java b/src/core/lombok/eclipse/handlers/HandleHelper.java
new file mode 100644
index 00000000..4e9a7c68
--- /dev/null
+++ b/src/core/lombok/eclipse/handlers/HandleHelper.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.eclipse.handlers;
+
+import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.mangosdk.spi.ProviderFor;
+
+import lombok.ConfigurationKeys;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.EclipseAnnotationHandler;
+import lombok.eclipse.EclipseNode;
+import lombok.experimental.Helper;
+
+/**
+ * Handles the {@code lombok.Cleanup} annotation for eclipse.
+ */
+@ProviderFor(EclipseAnnotationHandler.class)
+public class HandleHelper extends EclipseAnnotationHandler<Helper> {
+ @Override public void handle(AnnotationValues<Helper> annotation, Annotation ast, EclipseNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper");
+
+ EclipseNode annotatedType = annotationNode.up();
+ EclipseNode containingMethod = annotatedType == null ? null : annotatedType.up();
+ if (annotatedType == null || containingMethod == null || annotatedType.getKind() != Kind.TYPE || containingMethod.getKind() != Kind.METHOD) {
+ annotationNode.addError("@Helper is legal only on method-local classes.");
+ return;
+ }
+
+ TypeDeclaration annotatedType_ = (TypeDeclaration) annotatedType.get();
+ AbstractMethodDeclaration amd = (AbstractMethodDeclaration) containingMethod.get();
+ Statement[] origStatements = amd.statements;
+ int indexOfType = -1;
+ for (int i = 0; i < origStatements.length; i++) {
+ if (origStatements[i] == annotatedType_) {
+ indexOfType = i;
+ break;
+ }
+ }
+
+ final List<String> knownMethodNames = new ArrayList<String>();
+
+ for (AbstractMethodDeclaration methodOfHelper : annotatedType_.methods) {
+ if (!(methodOfHelper instanceof MethodDeclaration)) continue;
+ char[] name = methodOfHelper.selector;
+ if (name != null && name.length > 0 && name[0] != '<') knownMethodNames.add(new String(name));
+ }
+
+ Collections.sort(knownMethodNames);
+ final String[] knownMethodNames_ = knownMethodNames.toArray(new String[knownMethodNames.size()]);
+
+ final char[] helperName = new char[annotatedType_.name.length + 1];
+ final boolean[] helperUsed = new boolean[1];
+ helperName[0] = '$';
+ System.arraycopy(annotatedType_.name, 0, helperName, 1, helperName.length - 1);
+
+ ASTVisitor visitor = new ASTVisitor() {
+ @Override public boolean visit(MessageSend messageSend, BlockScope scope) {
+ if (messageSend.receiver instanceof ThisReference) {
+ if ((((ThisReference) messageSend.receiver).bits & ASTNode.IsImplicitThis) == 0) return true;
+ } else if (messageSend.receiver != null) return true;
+
+ char[] name = messageSend.selector;
+ if (name == null || name.length == 0 || name[0] == '<') return true;
+ String n = new String(name);
+ if (Arrays.binarySearch(knownMethodNames_, n) < 0) return true;
+ messageSend.receiver = new SingleNameReference(helperName, Eclipse.pos(messageSend));
+ helperUsed[0] = true;
+ return true;
+ }
+ };
+
+ for (int i = indexOfType + 1; i < origStatements.length; i++) {
+ origStatements[i].traverse(visitor, null);
+ }
+
+ if (!helperUsed[0]) {
+ annotationNode.addWarning("No methods of this helper class are ever used.");
+ return;
+ }
+
+ Statement[] newStatements = new Statement[origStatements.length + 1];
+ System.arraycopy(origStatements, 0, newStatements, 0, indexOfType + 1);
+ System.arraycopy(origStatements, indexOfType + 1, newStatements, indexOfType + 2, origStatements.length - indexOfType - 1);
+ LocalDeclaration decl = new LocalDeclaration(helperName, 0, 0);
+ decl.modifiers |= ClassFileConstants.AccFinal;
+ AllocationExpression alloc = new AllocationExpression();
+ alloc.type = new SingleTypeReference(annotatedType_.name, 0L);
+ decl.initialization = alloc;
+ decl.type = new SingleTypeReference(annotatedType_.name, 0L);
+ SetGeneratedByVisitor sgbvVisitor = new SetGeneratedByVisitor(annotationNode.get());
+ decl.traverse(sgbvVisitor, null);
+ newStatements[indexOfType + 1] = decl;
+ amd.statements = newStatements;
+ }
+}
diff --git a/src/core/lombok/eclipse/handlers/HandleNonNull.java b/src/core/lombok/eclipse/handlers/HandleNonNull.java
index d904de2f..d09993ed 100644
--- a/src/core/lombok/eclipse/handlers/HandleNonNull.java
+++ b/src/core/lombok/eclipse/handlers/HandleNonNull.java
@@ -91,7 +91,7 @@ public class HandleNonNull extends EclipseAnnotationHandler<NonNull> {
if (isGenerated(declaration)) return;
if (declaration.isAbstract()) {
- annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method.");
+ // This used to be a warning, but as @NonNull also has a documentary purpose, better to not warn about this. Since 1.16.7
return;
}
diff --git a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
index 176ff2d8..199ce102 100644
--- a/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
+++ b/src/core/lombok/eclipse/handlers/HandleUtilityClass.java
@@ -31,6 +31,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Clinit;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
@@ -59,7 +60,7 @@ import lombok.experimental.UtilityClass;
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> {
@Override public void handle(AnnotationValues<UtilityClass> annotation, Annotation ast, EclipseNode annotationNode) {
- handleFlagUsage(annotationNode, ConfigurationKeys.UTLITY_CLASS_FLAG_USAGE, "@UtilityClass");
+ handleFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass");
EclipseNode typeNode = annotationNode.up();
if (!checkLegality(typeNode, annotationNode)) return;
@@ -104,6 +105,8 @@ public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> {
classDecl.modifiers |= ClassFileConstants.AccFinal;
boolean markStatic = true;
+ boolean requiresClInit = false;
+ boolean alreadyHasClinit = false;
if (typeNode.up().getKind() == Kind.COMPILATION_UNIT) markStatic = false;
if (markStatic && typeNode.up().getKind() == Kind.TYPE) {
@@ -116,7 +119,10 @@ public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> {
for (EclipseNode element : typeNode.down()) {
if (element.getKind() == Kind.FIELD) {
FieldDeclaration fieldDecl = (FieldDeclaration) element.get();
- fieldDecl.modifiers |= ClassFileConstants.AccStatic;
+ if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) == 0) {
+ requiresClInit = true;
+ fieldDecl.modifiers |= ClassFileConstants.AccStatic;
+ }
} else if (element.getKind() == Kind.METHOD) {
AbstractMethodDeclaration amd = (AbstractMethodDeclaration) element.get();
if (amd instanceof ConstructorDeclaration) {
@@ -128,6 +134,8 @@ public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> {
}
} else if (amd instanceof MethodDeclaration) {
amd.modifiers |= ClassFileConstants.AccStatic;
+ } else if (amd instanceof Clinit) {
+ alreadyHasClinit = true;
}
} else if (element.getKind() == Kind.TYPE) {
((TypeDeclaration) element.get()).modifiers |= ClassFileConstants.AccStatic;
@@ -135,6 +143,7 @@ public class HandleUtilityClass extends EclipseAnnotationHandler<UtilityClass> {
}
if (makeConstructor) createPrivateDefaultConstructor(typeNode, annotationNode);
+ if (requiresClInit && !alreadyHasClinit) classDecl.addClinit();
}
private static final char[][] JAVA_LANG_UNSUPPORTED_OPERATION_EXCEPTION = new char[][] {
diff --git a/src/core/lombok/experimental/Accessors.java b/src/core/lombok/experimental/Accessors.java
index c2a0ca16..7d9fdc5c 100644
--- a/src/core/lombok/experimental/Accessors.java
+++ b/src/core/lombok/experimental/Accessors.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* A container for settings for the generation of getters and setters.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Accessors.html">the project lombok features page for &#64;Accessors</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Accessors.html">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.
diff --git a/src/core/lombok/experimental/Builder.java b/src/core/lombok/experimental/Builder.java
index 39f51460..4d5e0f67 100644
--- a/src/core/lombok/experimental/Builder.java
+++ b/src/core/lombok/experimental/Builder.java
@@ -48,7 +48,7 @@ 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="http://projectlombok.org/features/experimental/Builder.html">the project lombok features page for &#64;Builder</a>.
+ * 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:
diff --git a/src/core/lombok/experimental/Delegate.java b/src/core/lombok/experimental/Delegate.java
index 806d5871..d94389b0 100644
--- a/src/core/lombok/experimental/Delegate.java
+++ b/src/core/lombok/experimental/Delegate.java
@@ -40,7 +40,7 @@ import java.lang.annotation.Target;
* that exist in {@link Object}, the {@code canEqual(Object)} method, and any methods that appear in types
* that are listed in the {@code excludes} property.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Delegate.html">the project lombok features page for &#64;Delegate</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Delegate.html">the project lombok features page for &#64;Delegate</a>.
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
diff --git a/src/core/lombok/experimental/ExtensionMethod.java b/src/core/lombok/experimental/ExtensionMethod.java
index 7de8a136..3e64cf78 100644
--- a/src/core/lombok/experimental/ExtensionMethod.java
+++ b/src/core/lombok/experimental/ExtensionMethod.java
@@ -31,7 +31,7 @@ import java.lang.annotation.*;
* otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as
* if they were instance methods on the extended type.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/ExtensionMethod.html">the project lombok features page for &#64;ExtensionMethod</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/ExtensionMethod.html">the project lombok features page for &#64;ExtensionMethod</a>.
* <p>
* <p>
* Before:
diff --git a/src/core/lombok/experimental/FieldDefaults.java b/src/core/lombok/experimental/FieldDefaults.java
index 1c621f3c..dbc4993b 100644
--- a/src/core/lombok/experimental/FieldDefaults.java
+++ b/src/core/lombok/experimental/FieldDefaults.java
@@ -31,7 +31,7 @@ import lombok.AccessLevel;
/**
* Adds modifiers to each field in the type with this annotation.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/FieldDefaults.html">the project lombok features page for &#64;FieldDefaults</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/FieldDefaults.html">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.
* <p>
diff --git a/src/core/lombok/experimental/Helper.java b/src/core/lombok/experimental/Helper.java
new file mode 100644
index 00000000..34745cbe
--- /dev/null
+++ b/src/core/lombok/experimental/Helper.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.experimental;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Use on a method local class to indicate that all methods inside should be exposed to the rest of
+ * the method as if they were helper methods.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.SOURCE)
+public @interface Helper {}
diff --git a/src/core/lombok/experimental/Value.java b/src/core/lombok/experimental/Value.java
index b7700bb5..46ec7a05 100644
--- a/src/core/lombok/experimental/Value.java
+++ b/src/core/lombok/experimental/Value.java
@@ -31,7 +31,7 @@ import java.lang.annotation.Target;
* <p>
* Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @RequiredArgsConstructor @ToString @EqualsAndHashCode}.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Value.html">the project lombok features page for &#64;Value</a>.
*
* @see lombok.Getter
* @see Wither
diff --git a/src/core/lombok/experimental/Wither.java b/src/core/lombok/experimental/Wither.java
index fe113bb0..b4131187 100644
--- a/src/core/lombok/experimental/Wither.java
+++ b/src/core/lombok/experimental/Wither.java
@@ -31,7 +31,7 @@ import lombok.AccessLevel;
/**
* Put on any field to make lombok build a 'wither' - a withX method which produces a clone of this object (except for 1 field which gets a new value).
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/experimental/Wither.html">the project lombok features page for &#64;Wither</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Wither.html">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>
diff --git a/src/core/lombok/experimental/package-info.java b/src/core/lombok/experimental/package-info.java
index 776f2c27..d85e2969 100644
--- a/src/core/lombok/experimental/package-info.java
+++ b/src/core/lombok/experimental/package-info.java
@@ -28,6 +28,6 @@
* to the official feature documentation.
*
* @see lombok
- * @see <a href="http://projectlombok.org/features/experimental/index.html">Lombok features (experimental)</a>
+ * @see <a href="https://projectlombok.org/features/experimental/index.html">Lombok features (experimental)</a>
*/
package lombok.experimental;
diff --git a/src/core/lombok/extern/apachecommons/CommonsLog.java b/src/core/lombok/extern/apachecommons/CommonsLog.java
index 45345098..6e64d2d8 100644
--- a/src/core/lombok/extern/apachecommons/CommonsLog.java
+++ b/src/core/lombok/extern/apachecommons/CommonsLog.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
diff --git a/src/core/lombok/extern/java/Log.java b/src/core/lombok/extern/java/Log.java
index bac2742e..d2cf8c17 100644
--- a/src/core/lombok/extern/java/Log.java
+++ b/src/core/lombok/extern/java/Log.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
diff --git a/src/core/lombok/extern/log4j/Log4j.java b/src/core/lombok/extern/log4j/Log4j.java
index 9490acb8..31fedbe7 100644
--- a/src/core/lombok/extern/log4j/Log4j.java
+++ b/src/core/lombok/extern/log4j/Log4j.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
diff --git a/src/core/lombok/extern/log4j/Log4j2.java b/src/core/lombok/extern/log4j/Log4j2.java
index 43125e6b..96d793f7 100644
--- a/src/core/lombok/extern/log4j/Log4j2.java
+++ b/src/core/lombok/extern/log4j/Log4j2.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
@@ -42,7 +42,7 @@ import java.lang.annotation.Target;
*
* <pre>
* public class LogExample {
- * private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.Logger.getLogger(LogExample.class);
+ * private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
* }
* </pre>
*
diff --git a/src/core/lombok/extern/slf4j/Slf4j.java b/src/core/lombok/extern/slf4j/Slf4j.java
index 04df6498..571ebd58 100644
--- a/src/core/lombok/extern/slf4j/Slf4j.java
+++ b/src/core/lombok/extern/slf4j/Slf4j.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
diff --git a/src/core/lombok/extern/slf4j/XSlf4j.java b/src/core/lombok/extern/slf4j/XSlf4j.java
index 8a311c79..369728c4 100644
--- a/src/core/lombok/extern/slf4j/XSlf4j.java
+++ b/src/core/lombok/extern/slf4j/XSlf4j.java
@@ -29,7 +29,7 @@ import java.lang.annotation.Target;
/**
* Causes lombok to generate a logger field.
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>.
* <p>
* Example:
* <pre>
diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java
index ab833195..efe40da3 100644
--- a/src/core/lombok/javac/handlers/HandleBuilder.java
+++ b/src/core/lombok/javac/handlers/HandleBuilder.java
@@ -31,6 +31,7 @@ import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
@@ -51,6 +52,7 @@ import com.sun.tools.javac.util.Name;
import lombok.AccessLevel;
import lombok.Builder;
+import lombok.Builder.ObtainVia;
import lombok.ConfigurationKeys;
import lombok.Singular;
import lombok.core.AST.Kind;
@@ -81,8 +83,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
private static class BuilderFieldData {
JCExpression type;
+ Name rawName;
Name name;
SingularData singularData;
+ ObtainVia obtainVia;
+ JavacNode obtainViaNode;
java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>();
}
@@ -97,6 +102,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
String builderMethodName = builderInstance.builderMethodName();
String buildMethodName = builderInstance.buildMethodName();
String builderClassName = builderInstance.builderClassName();
+ String toBuilderMethodName = "toBuilder";
+ boolean toBuilder = builderInstance.toBuilder();
+ java.util.List<Name> typeArgsForToBuilder = null;
if (builderMethodName == null) builderMethodName = "builder";
if (buildMethodName == null) buildMethodName = "build";
@@ -138,14 +146,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
// 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;
BuilderFieldData bfd = new BuilderFieldData();
+ bfd.rawName = fd.name;
bfd.name = removePrefixFromField(fieldNode);
bfd.type = fd.vartype;
bfd.singularData = getSingularData(fieldNode);
+ addObtainVia(bfd, fieldNode);
builderFields.add(bfd);
allFields.append(fieldNode);
}
- new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode);
+ new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode);
returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams);
typeParams = td.typarams;
@@ -171,14 +181,15 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JCClassDecl td = (JCClassDecl) tdParent.get();
JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get();
isStatic = (jmd.mods.flags & Flags.STATIC) != 0;
- returnType = jmd.restype;
+ JCExpression fullReturnType = jmd.restype;
+ returnType = fullReturnType;
typeParams = jmd.typarams;
thrownExceptions = jmd.thrown;
nameOfBuilderMethod = jmd.name;
+ if (returnType instanceof JCTypeApply) {
+ returnType = ((JCTypeApply) returnType).clazz;
+ }
if (builderClassName.isEmpty()) {
- if (returnType instanceof JCTypeApply) {
- returnType = ((JCTypeApply) returnType).clazz;
- }
if (returnType instanceof JCFieldAccess) {
builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder";
} else if (returnType instanceof JCIdent) {
@@ -196,13 +207,76 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (Character.isLowerCase(builderClassName.charAt(0))) {
builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1);
}
-
} else {
// This shouldn't happen.
System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass());
builderClassName = td.name.toString() + "Builder";
}
}
+ if (toBuilder) {
+ final String TO_BUILDER_NOT_SUPPORTED = "@Builder(toBuilder=true) is only supported if you return your own type.";
+ if (returnType instanceof JCArrayTypeTree) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ Name simpleName;
+ String pkg;
+ List<JCExpression> tpOnRet = List.nil();
+
+ if (fullReturnType instanceof JCTypeApply) {
+ tpOnRet = ((JCTypeApply) fullReturnType).arguments;
+ }
+
+ if (returnType instanceof JCIdent) {
+ simpleName = ((JCIdent) returnType).name;
+ pkg = null;
+ } else if (returnType instanceof JCFieldAccess) {
+ JCFieldAccess jcfa = (JCFieldAccess) returnType;
+ simpleName = jcfa.name;
+ pkg = unpack(jcfa.selected);
+ if (pkg.startsWith("ERR:")) {
+ String err = pkg.substring(4, pkg.indexOf("__ERR__"));
+ annotationNode.addError(err);
+ return;
+ }
+ } else {
+ annotationNode.addError("Expected a (parameterized) type here instead of a " + returnType.getClass().getName());
+ return;
+ }
+
+ if (pkg != null && !parent.getPackageDeclaration().equals(pkg)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ if (!tdParent.getName().contentEquals(simpleName)) {
+ annotationNode.addError(TO_BUILDER_NOT_SUPPORTED);
+ return;
+ }
+
+ List<JCTypeParameter> tpOnMethod = jmd.typarams;
+ List<JCTypeParameter> tpOnType = ((JCClassDecl) tdParent.get()).typarams;
+ typeArgsForToBuilder = new ArrayList<Name>();
+
+ for (JCTypeParameter tp : tpOnMethod) {
+ int pos = -1;
+ int idx = -1;
+ for (JCExpression tOnRet : tpOnRet) {
+ idx++;
+ if (!(tOnRet instanceof JCIdent)) continue;
+ if (((JCIdent) tOnRet).name != tp.name) continue;
+ pos = idx;
+ }
+
+ if (pos == -1 || tpOnType.size() <= pos) {
+ annotationNode.addError("**" + 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.");
+ return;
+ }
+ typeArgsForToBuilder.add(tpOnType.get(pos).name);
+ }
+ }
} else {
annotationNode.addError("@Builder is only supported on types, constructors, and methods.");
return;
@@ -214,8 +288,10 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
BuilderFieldData bfd = new BuilderFieldData();
JCVariableDecl raw = (JCVariableDecl) param.get();
bfd.name = raw.name;
+ bfd.rawName = raw.name;
bfd.type = raw.vartype;
bfd.singularData = getSingularData(param);
+ addObtainVia(bfd, param);
builderFields.add(bfd);
}
}
@@ -254,6 +330,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
break;
}
}
+ if (bfd.obtainVia != null) {
+ if (bfd.obtainVia.field().isEmpty() == bfd.obtainVia.method().isEmpty()) {
+ bfd.obtainViaNode.addError("The syntax is either @ObtainVia(field = \"fieldName\") or @ObtainVia(method = \"methodName\").");
+ return;
+ }
+ if (bfd.obtainVia.method().isEmpty() && bfd.obtainVia.isStatic()) {
+ bfd.obtainViaNode.addError("@ObtainVia(isStatic = true) is not valid unless 'method' has been set.");
+ return;
+ }
+ }
}
generateBuilderFields(builderType, builderFields, ast);
@@ -264,7 +350,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(), null, annotationNode);
+ JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), false, null, annotationNode);
if (cd != null) injectMethod(builderType, cd);
}
@@ -294,9 +380,95 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
if (md != null) injectMethod(tdParent, md);
}
+ if (toBuilder) {
+ switch (methodExists(toBuilderMethodName, tdParent, 0)) {
+ case EXISTS_BY_USER:
+ annotationNode.addWarning("Not generating toBuilder() as it already exists.");
+ return;
+ case NOT_EXISTS:
+ List<JCTypeParameter> tps = typeParams;
+ if (typeArgsForToBuilder != null) {
+ ListBuffer<JCTypeParameter> lb = new ListBuffer<JCTypeParameter>();
+ JavacTreeMaker maker = tdParent.getTreeMaker();
+ for (Name n : typeArgsForToBuilder) {
+ lb.append(maker.TypeParameter(n, List.<JCExpression>nil()));
+ }
+ tps = lb.toList();
+ }
+ JCMethodDecl md = generateToBuilderMethod(toBuilderMethodName, builderClassName, tdParent, tps, builderFields, fluent, ast);
+ if (md != null) injectMethod(tdParent, md);
+ }
+ }
+
recursiveSetGeneratedBy(builderType.get(), ast, annotationNode.getContext());
}
+ private static String unpack(JCExpression expr) {
+ StringBuilder sb = new StringBuilder();
+ unpack(sb, expr);
+ return sb.toString();
+ }
+
+ private static void unpack(StringBuilder sb, JCExpression expr) {
+ if (expr instanceof JCIdent) {
+ sb.append(((JCIdent) expr).name.toString());
+ return;
+ }
+
+ if (expr instanceof JCFieldAccess) {
+ JCFieldAccess jcfa = (JCFieldAccess) expr;
+ unpack(sb, jcfa.selected);
+ sb.append(".").append(jcfa.name.toString());
+ return;
+ }
+
+ if (expr instanceof JCTypeApply) {
+ sb.setLength(0);
+ sb.append("ERR:");
+ sb.append("@Builder(toBuilder=true) is not supported if returning a type with generics applied to an intermediate.");
+ sb.append("__ERR__");
+ return;
+ }
+
+ sb.setLength(0);
+ sb.append("ERR:");
+ sb.append("Expected a type of some sort, not a " + expr.getClass().getName());
+ sb.append("__ERR__");
+ }
+
+ private JCMethodDecl generateToBuilderMethod(String toBuilderMethodName, String builderClassName, JavacNode type, List<JCTypeParameter> typeParams, java.util.List<BuilderFieldData> builderFields, boolean fluent, JCAnnotation ast) {
+ // return new ThingieBuilder<A, B>().setA(this.a).setB(this.b);
+ JavacTreeMaker maker = type.getTreeMaker();
+
+ ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
+ for (JCTypeParameter typeParam : typeParams) {
+ typeArgs.append(maker.Ident(typeParam.name));
+ }
+
+ JCExpression call = maker.NewClass(null, List.<JCExpression>nil(), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCExpression>nil(), null);
+ JCExpression invoke = call;
+ for (BuilderFieldData bfd : builderFields) {
+ Name setterName = fluent ? bfd.name : type.toName(HandlerUtil.buildAccessorName("set", bfd.name.toString()));
+ JCExpression arg;
+ if (bfd.obtainVia == null || !bfd.obtainVia.field().isEmpty()) {
+ arg = maker.Select(maker.Ident(type.toName("this")), bfd.obtainVia == null ? bfd.rawName : type.toName(bfd.obtainVia.field()));
+ } else {
+ if (bfd.obtainVia.isStatic()) {
+ JCExpression c = maker.Select(maker.Ident(type.toName(type.getName())), type.toName(bfd.obtainVia.method()));
+ arg = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>of(maker.Ident(type.toName("this"))));
+ } else {
+ JCExpression c = maker.Select(maker.Ident(type.toName("this")), type.toName(bfd.obtainVia.method()));
+ arg = maker.Apply(List.<JCExpression>nil(), c, List.<JCExpression>nil());
+ }
+ }
+ invoke = maker.Apply(List.<JCExpression>nil(), maker.Select(invoke, setterName), List.of(arg));
+ }
+ JCStatement statement = maker.Return(invoke);
+
+ JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
+ return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(toBuilderMethodName), namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null);
+ }
+
private JCMethodDecl generateCleanMethod(java.util.List<BuilderFieldData> builderFields, JavacNode type, JCTree source) {
JavacTreeMaker maker = type.getTreeMaker();
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
@@ -465,6 +637,17 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
return injectType(tdParent, builder);
}
+ private void addObtainVia(BuilderFieldData bfd, JavacNode node) {
+ for (JavacNode child : node.down()) {
+ if (!annotationTypeMatches(ObtainVia.class, child)) continue;
+ AnnotationValues<ObtainVia> ann = createAnnotation(ObtainVia.class, child);
+ bfd.obtainVia = ann.getInstance();
+ bfd.obtainViaNode = child;
+ deleteAnnotationIfNeccessary(child, ObtainVia.class);
+ return;
+ }
+ }
+
/**
* Returns the explicitly requested singular annotation on this node (field
* or parameter), or null if there's no {@code @Singular} annotation on it.
@@ -473,48 +656,47 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
*/
private SingularData getSingularData(JavacNode node) {
for (JavacNode child : node.down()) {
- if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Singular.class, child)) {
- Name pluralName = node.getKind() == Kind.FIELD ? removePrefixFromField(node) : ((JCVariableDecl) node.get()).name;
- AnnotationValues<Singular> ann = createAnnotation(Singular.class, child);
- deleteAnnotationIfNeccessary(child, Singular.class);
- String explicitSingular = ann.getInstance().value();
- if (explicitSingular.isEmpty()) {
- if (Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_AUTO))) {
- node.addError("The singular must be specified explicitly (e.g. @Singular(\"task\")) because auto singularization is disabled.");
+ 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(node.getName());
+ if (explicitSingular == null) {
+ node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
explicitSingular = pluralName.toString();
- } else {
- explicitSingular = autoSingularize(node.getName());
- if (explicitSingular == null) {
- node.addError("Can't singularize this name; please specify the singular explicitly (i.e. @Singular(\"sheep\"))");
- explicitSingular = pluralName.toString();
- }
}
}
- Name singularName = node.toName(explicitSingular);
-
- JCExpression type = null;
- if (node.get() instanceof JCVariableDecl) {
- type = ((JCVariableDecl) node.get()).vartype;
- }
-
- String name = null;
- List<JCExpression> typeArgs = List.nil();
- if (type instanceof JCTypeApply) {
- typeArgs = ((JCTypeApply) type).arguments;
- type = ((JCTypeApply) type).clazz;
- }
-
- name = type.toString();
-
- String targetFqn = JavacSingularsRecipes.get().toQualified(name);
- JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
- if (singularizer == null) {
- node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
- return null;
- }
-
- return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
}
+ Name singularName = node.toName(explicitSingular);
+
+ JCExpression type = null;
+ if (node.get() instanceof JCVariableDecl) {
+ type = ((JCVariableDecl) node.get()).vartype;
+ }
+
+ String name = null;
+ List<JCExpression> typeArgs = List.nil();
+ if (type instanceof JCTypeApply) {
+ typeArgs = ((JCTypeApply) type).arguments;
+ type = ((JCTypeApply) type).clazz;
+ }
+
+ name = type.toString();
+
+ String targetFqn = JavacSingularsRecipes.get().toQualified(name);
+ JavacSingularizer singularizer = JavacSingularsRecipes.get().getSingularizer(targetFqn);
+ if (singularizer == null) {
+ node.addError("Lombok does not know how to create the singular-form builder methods for type '" + name + "'; they won't be generated.");
+ return null;
+ }
+
+ return new SingularData(child, singularName, pluralName, typeArgs, targetFqn, singularizer);
}
return null;
diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java
index c5b309c2..4a4ec49c 100644
--- a/src/core/lombok/javac/handlers/HandleConstructor.java
+++ b/src/core/lombok/javac/handlers/HandleConstructor.java
@@ -23,6 +23,8 @@ 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;
@@ -41,13 +43,13 @@ import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
-import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
@@ -71,8 +73,9 @@ public class HandleConstructor {
AccessLevel level = ann.access();
if (level == AccessLevel.NONE) return;
String staticName = ann.staticName();
- List<JavacNode> fields = List.nil();
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, staticName, SkipIfConstructorExists.NO, null, annotationNode);
+ boolean force = ann.force();
+ List<JavacNode> fields = force ? findFinalFields(typeNode) : List.<JavacNode>nil();
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, null, annotationNode);
}
}
@@ -97,11 +100,19 @@ public class HandleConstructor {
suppressConstructorProperties = suppress;
}
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
public static List<JavacNode> findRequiredFields(JavacNode typeNode) {
+ return findFields(typeNode, true);
+ }
+
+ public static List<JavacNode> findFinalFields(JavacNode typeNode) {
+ return findFields(typeNode, false);
+ }
+
+ public static List<JavacNode> findFields(JavacNode typeNode, boolean nullMarked) {
ListBuffer<JavacNode> fields = new ListBuffer<JavacNode>();
for (JavacNode child : typeNode.down()) {
if (child.getKind() != Kind.FIELD) continue;
@@ -112,7 +123,7 @@ public class HandleConstructor {
//Skip static fields.
if ((fieldFlags & Flags.STATIC) != 0) continue;
boolean isFinal = (fieldFlags & Flags.FINAL) != 0;
- boolean isNonNull = !findAnnotations(child, NON_NULL_PATTERN).isEmpty();
+ boolean isNonNull = nullMarked && !findAnnotations(child, NON_NULL_PATTERN).isEmpty();
if ((isFinal || isNonNull) && fieldDecl.init == null) fields.append(child);
}
return fields.toList();
@@ -138,7 +149,7 @@ public class HandleConstructor {
boolean suppress = ann.suppressConstructorProperties();
suppressConstructorProperties = suppress;
}
- new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
+ new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, suppressConstructorProperties, annotationNode);
}
}
@@ -174,7 +185,7 @@ public class HandleConstructor {
}
public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
- generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), staticName, skipIfConstructorExists, null, source);
+ generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findRequiredFields(typeNode), false, staticName, skipIfConstructorExists, null, source);
}
public enum SkipIfConstructorExists {
@@ -182,10 +193,10 @@ public class HandleConstructor {
}
public void generateAllArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, SkipIfConstructorExists skipIfConstructorExists, JavacNode source) {
- generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), staticName, skipIfConstructorExists, null, source);
+ generateConstructor(typeNode, level, List.<JCAnnotation>nil(), findAllFields(typeNode), false, staticName, skipIfConstructorExists, null, source);
}
- public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, String staticName, SkipIfConstructorExists skipIfConstructorExists, Boolean suppressConstructorProperties, JavacNode source) {
+ public void generateConstructor(JavacNode typeNode, AccessLevel level, List<JCAnnotation> onConstructor, List<JavacNode> fields, boolean allToDefault, String staticName, SkipIfConstructorExists skipIfConstructorExists, Boolean suppressConstructorProperties, JavacNode source) {
boolean staticConstrRequired = staticName != null && !staticName.equals("");
if (skipIfConstructorExists != SkipIfConstructorExists.NO && constructorExists(typeNode) != MemberExistsResult.NOT_EXISTS) return;
@@ -193,8 +204,8 @@ public class HandleConstructor {
for (JavacNode child : typeNode.down()) {
if (child.getKind() == Kind.ANNOTATION) {
boolean skipGeneration = annotationTypeMatches(NoArgsConstructor.class, child) ||
- annotationTypeMatches(AllArgsConstructor.class, child) ||
- annotationTypeMatches(RequiredArgsConstructor.class, child);
+ annotationTypeMatches(AllArgsConstructor.class, child) ||
+ annotationTypeMatches(RequiredArgsConstructor.class, child);
if (!skipGeneration && skipIfConstructorExists == SkipIfConstructorExists.YES) {
skipGeneration = annotationTypeMatches(Builder.class, child);
@@ -214,10 +225,10 @@ public class HandleConstructor {
}
}
- JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, suppressConstructorProperties, source);
+ JCMethodDecl constr = createConstructor(staticConstrRequired ? AccessLevel.PRIVATE : level, onConstructor, typeNode, fields, allToDefault, suppressConstructorProperties, source);
injectMethod(typeNode, constr);
if (staticConstrRequired) {
- JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, fields, source.get());
+ JCMethodDecl staticConstr = createStaticConstructor(staticName, level, typeNode, allToDefault ? List.<JavacNode>nil() : fields, source.get());
injectMethod(typeNode, staticConstr);
}
}
@@ -236,7 +247,7 @@ public class HandleConstructor {
mods.annotations = mods.annotations.append(annotation);
}
- public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, Boolean suppressConstructorProperties, JavacNode source) {
+ public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean allToDefault, Boolean suppressConstructorProperties, JavacNode source) {
JavacTreeMaker maker = typeNode.getTreeMaker();
boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0;
@@ -259,29 +270,55 @@ public class HandleConstructor {
Name fieldName = removePrefixFromField(fieldNode);
Name rawName = field.name;
List<JCAnnotation> nonNulls = findAnnotations(fieldNode, NON_NULL_PATTERN);
- List<JCAnnotation> nullables = findAnnotations(fieldNode, NULLABLE_PATTERN);
- long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
- JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null);
- params.append(param);
+ if (!allToDefault) {
+ List<JCAnnotation> nullables = findAnnotations(fieldNode, NULLABLE_PATTERN);
+ long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
+ JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null);
+ params.append(param);
+ if (!nonNulls.isEmpty()) {
+ JCStatement nullCheck = generateNullCheck(maker, fieldNode, source);
+ if (nullCheck != null) nullChecks.append(nullCheck);
+ }
+ }
JCFieldAccess thisX = maker.Select(maker.Ident(fieldNode.toName("this")), rawName);
- JCAssign assign = maker.Assign(thisX, maker.Ident(fieldName));
+ JCExpression assign = maker.Assign(thisX, allToDefault ? getDefaultExpr(maker, field.vartype) : maker.Ident(fieldName));
assigns.append(maker.Exec(assign));
-
- if (!nonNulls.isEmpty()) {
- JCStatement nullCheck = generateNullCheck(maker, fieldNode, source);
- if (nullCheck != null) nullChecks.append(nullCheck);
- }
}
JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil());
- if (!suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
+ if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) {
addConstructorProperties(mods, typeNode, fields);
}
if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor));
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"),
- null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
- maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext());
+ null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(),
+ maker.Block(0L, nullChecks.appendList(assigns).toList()), null), source.get(), typeNode.getContext());
+ }
+
+ private static JCExpression getDefaultExpr(JavacTreeMaker maker, JCExpression type) {
+ if (type instanceof JCPrimitiveTypeTree) {
+ switch (((JCPrimitiveTypeTree) type).getPrimitiveTypeKind()) {
+ case BOOLEAN:
+ return maker.Literal(CTC_BOOLEAN, 0);
+ case CHAR:
+ return maker.Literal(CTC_CHAR, 0);
+ default:
+ case BYTE:
+ case SHORT:
+ case INT:
+ return maker.Literal(CTC_INT, 0);
+ case LONG:
+ return maker.Literal(CTC_LONG, 0L);
+ case FLOAT:
+ return maker.Literal(CTC_FLOAT, 0F);
+ case DOUBLE:
+ return maker.Literal(CTC_DOUBLE, 0D);
+ }
+ }
+
+ return maker.Literal(CTC_BOT, null);
+
}
public static boolean isLocalType(JavacNode type) {
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 1a6f4a8f..4bc79f03 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -124,10 +124,11 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
public void generateMethods(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
+ Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
+
boolean notAClass = true;
if (typeNode.get() instanceof JCClassDecl) {
- long flags = ((JCClassDecl)typeNode.get()).mods.flags;
+ long flags = ((JCClassDecl) typeNode.get()).mods.flags;
notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0;
}
@@ -140,7 +141,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
boolean implicitCallSuper = callSuper == null;
if (callSuper == null) {
try {
- callSuper = ((Boolean)EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
+ callSuper = ((Boolean) EqualsAndHashCode.class.getMethod("callSuper").getDefaultValue()).booleanValue();
} catch (Exception ignore) {
throw new InternalError("Lombok bug - this cannot happen - can't find callSuper field in EqualsAndHashCode annotation.");
}
@@ -184,7 +185,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
}
- boolean isFinal = (((JCClassDecl)typeNode.get()).mods.flags & Flags.FINAL) != 0;
+ boolean isFinal = (((JCClassDecl) typeNode.get()).mods.flags & Flags.FINAL) != 0;
boolean needsCanEqual = !isFinal || !isDirectDescendantOfObject;
MemberExistsResult equalsExists = methodExists("equals", typeNode, 1);
MemberExistsResult hashCodeExists = methodExists("hashCode", typeNode, 0);
@@ -202,8 +203,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
// The user code couldn't possibly (barring really weird subclassing shenanigans) be in a shippable state anyway; the implementations of these 2 methods are
// all inter-related and should be written by the same entity.
String msg = String.format("Not generating %s: One of equals or hashCode exists. " +
- "You should either write both of these or none of these (in the latter case, lombok generates them).",
- equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
+ "You should either write both of these or none of these (in the latter case, lombok generates them).",
+ equalsExists == MemberExistsResult.NOT_EXISTS ? "equals" : "hashCode");
source.addWarning(msg);
}
return;
@@ -248,8 +249,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
if (callSuper) {
JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")),
- List.<JCExpression>nil());
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")),
+ List.<JCExpression>nil());
statements.append(createResultCalculation(typeNode, callToSuper));
}
@@ -258,14 +259,14 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression fType = getFieldType(fieldNode, fieldAccess);
JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
if (fType instanceof JCPrimitiveTypeTree) {
- switch (((JCPrimitiveTypeTree)fType).getPrimitiveTypeKind()) {
+ switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) {
case BOOLEAN:
/* this.fieldName ? X : Y */
- statements.append(createResultCalculation(typeNode, maker.Conditional(fieldAccessor,
- maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse()))));
+ statements.append(createResultCalculation(typeNode, maker.Parens(maker.Conditional(fieldAccessor,
+ maker.Literal(HandlerUtil.primeForTrue()), maker.Literal(HandlerUtil.primeForFalse())))));
break;
case LONG: {
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), fieldAccessor));
statements.append(createResultCalculation(typeNode, longToIntForHashCode(maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
}
@@ -273,17 +274,17 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
case FLOAT:
/* Float.floatToIntBits(this.fieldName) */
statements.append(createResultCalculation(typeNode, maker.Apply(
- List.<JCExpression>nil(),
- genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"),
- List.of(fieldAccessor))));
+ List.<JCExpression>nil(),
+ genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"),
+ List.of(fieldAccessor))));
break;
case DOUBLE: {
/* longToIntForHashCode(Double.doubleToLongBits(this.fieldName)) */
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
JCExpression init = maker.Apply(
- List.<JCExpression>nil(),
- genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"),
- List.of(fieldAccessor));
+ List.<JCExpression>nil(),
+ genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"),
+ List.of(fieldAccessor));
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, maker.TypeIdent(CTC_LONG), init));
statements.append(createResultCalculation(typeNode, longToIntForHashCode(maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
}
@@ -299,23 +300,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
} else if (fType instanceof JCArrayTypeTree) {
/* java.util.Arrays.deepHashCode(this.fieldName) //use just hashCode() for primitive arrays. */
- boolean multiDim = ((JCArrayTypeTree)fType).elemtype instanceof JCArrayTypeTree;
- boolean primitiveArray = ((JCArrayTypeTree)fType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree;
+ boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree;
boolean useDeepHC = multiDim || !primitiveArray;
JCExpression hcMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepHC ? "deepHashCode" : "hashCode");
statements.append(createResultCalculation(typeNode, maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(fieldAccessor))));
} else /* objects */ {
/* final java.lang.Object $fieldName = this.fieldName; */
- /* $fieldName == null ? 0 : $fieldName.hashCode() */
+ /* ($fieldName == null ? NULL_PRIME : $fieldName.hashCode()) */
- Name dollarFieldName = dollar.append(((JCVariableDecl)fieldNode.get()).name);
+ Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
statements.append(maker.VarDef(maker.Modifiers(finalFlag), dollarFieldName, genJavaLangTypeRef(typeNode, "Object"), fieldAccessor));
JCExpression hcCall = maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(dollarFieldName), typeNode.toName("hashCode")),
- List.<JCExpression>nil());
+ List.<JCExpression>nil());
JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(dollarFieldName), maker.Literal(CTC_BOT, null));
- statements.append(createResultCalculation(typeNode, maker.Conditional(thisEqualsNull, maker.Literal(0), hcCall)));
+ statements.append(createResultCalculation(typeNode, maker.Parens(maker.Conditional(thisEqualsNull, maker.Literal(HandlerUtil.primeForNull()), hcCall))));
}
}
@@ -325,11 +326,11 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCBlock body = maker.Block(0, statements.toList());
return recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("hashCode"), returnType,
- List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
+ List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null), source, typeNode.getContext());
}
public JCExpressionStatement createResultCalculation(JavacNode typeNode, JCExpression expr) {
- /* result = result * PRIME + (expr); */
+ /* result = result * PRIME + expr; */
JavacTreeMaker maker = typeNode.getTreeMaker();
Name resultName = typeNode.toName(RESULT_NAME);
JCExpression mult = maker.Binary(CTC_MUL, maker.Ident(resultName), maker.Ident(typeNode.toName(PRIME_NAME)));
@@ -339,10 +340,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/** The 2 references must be clones of each other. */
public JCExpression longToIntForHashCode(JavacTreeMaker maker, JCExpression ref1, JCExpression ref2) {
- /* (int)(ref >>> 32 ^ ref) */
+ /* (int) (ref >>> 32 ^ ref) */
JCExpression shift = maker.Binary(CTC_UNSIGNED_SHIFT_RIGHT, ref1, maker.Literal(32));
JCExpression xorBits = maker.Binary(CTC_BITXOR, shift, ref2);
- return maker.TypeCast(maker.TypeIdent(CTC_INT), xorBits);
+ return maker.TypeCast(maker.TypeIdent(CTC_INT), maker.Parens(xorBits));
}
public JCExpression createTypeReference(JavacNode type) {
@@ -385,12 +386,12 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* if (o == this) return true; */ {
statements.append(maker.If(maker.Binary(CTC_EQUAL, maker.Ident(oName),
- maker.Ident(thisName)), returnBool(maker, true), null));
+ maker.Ident(thisName)), returnBool(maker, true), null));
}
- /* if (!(o instanceof Outer.Inner.MyType) return false; */ {
+ /* if (!(o instanceof Outer.Inner.MyType)) return false; */ {
- JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode)));
+ JCUnary notInstanceOf = maker.Unary(CTC_NOT, maker.Parens(maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode))));
statements.append(maker.If(notInstanceOf, returnBool(maker, false), null));
}
@@ -413,7 +414,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
statements.append(
- maker.VarDef(maker.Modifiers(finalFlag), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName))));
+ maker.VarDef(maker.Modifiers(finalFlag), otherName, selfType1, maker.TypeCast(selfType2, maker.Ident(oName))));
}
}
@@ -423,8 +424,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression thisRef = maker.Ident(thisName);
JCExpression castThisRef = maker.TypeCast(genJavaLangTypeRef(typeNode, "Object"), thisRef);
JCExpression equalityCheck = maker.Apply(exprNil,
- maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")),
- List.of(castThisRef));
+ maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")),
+ List.of(castThisRef));
statements.append(maker.If(maker.Unary(CTC_NOT, equalityCheck), returnBool(maker, false), null));
}
}
@@ -432,8 +433,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* if (!super.equals(o)) return false; */
if (callSuper) {
JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
- List.<JCExpression>of(maker.Ident(oName)));
+ maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
+ List.<JCExpression>of(maker.Ident(oName)));
JCUnary superNotEqual = maker.Unary(CTC_NOT, callToSuper);
statements.append(maker.If(superNotEqual, returnBool(maker, false), null));
}
@@ -462,19 +463,19 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
} else if (fType instanceof JCArrayTypeTree) {
/* if (!java.util.Arrays.deepEquals(this.fieldName, other.fieldName)) return false; //use equals for primitive arrays. */
- boolean multiDim = ((JCArrayTypeTree)fType).elemtype instanceof JCArrayTypeTree;
- boolean primitiveArray = ((JCArrayTypeTree)fType).elemtype instanceof JCPrimitiveTypeTree;
+ boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree;
+ boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree;
boolean useDeepEquals = multiDim || !primitiveArray;
JCExpression eqMethod = chainDots(typeNode, "java", "util", "Arrays", useDeepEquals ? "deepEquals" : "equals");
List<JCExpression> args = List.of(thisFieldAccessor, otherFieldAccessor);
statements.append(maker.If(maker.Unary(CTC_NOT,
- maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null));
+ maker.Apply(List.<JCExpression>nil(), eqMethod, args)), returnBool(maker, false), null));
} else /* objects */ {
/* final java.lang.Object this$fieldName = this.fieldName; */
/* final java.lang.Object other$fieldName = other.fieldName; */
- /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false;; */
- Name fieldName = ((JCVariableDecl)fieldNode.get()).name;
+ /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */
+ Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
Name thisDollarFieldName = thisDollar.append(fieldName);
Name otherDollarFieldName = otherDollar.append(fieldName);
@@ -484,8 +485,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
JCExpression thisEqualsNull = maker.Binary(CTC_EQUAL, maker.Ident(thisDollarFieldName), maker.Literal(CTC_BOT, null));
JCExpression otherNotEqualsNull = maker.Binary(CTC_NOT_EQUAL, maker.Ident(otherDollarFieldName), maker.Literal(CTC_BOT, null));
JCExpression thisEqualsThat = maker.Apply(List.<JCExpression>nil(),
- maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")),
- List.<JCExpression>of(maker.Ident(otherDollarFieldName)));
+ maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")),
+ List.<JCExpression>of(maker.Ident(otherDollarFieldName)));
JCExpression fieldsAreNotEqual = maker.Conditional(thisEqualsNull, otherNotEqualsNull, maker.Unary(CTC_NOT, thisEqualsThat));
statements.append(maker.If(fieldsAreNotEqual, returnBool(maker, false), null));
}
@@ -521,12 +522,13 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
public JCStatement generateCompareFloatOrDouble(JCExpression thisDotField, JCExpression otherDotField,
- JavacTreeMaker maker, JavacNode node, boolean isDouble) {
+ JavacTreeMaker maker, JavacNode node, boolean isDouble) {
+
/* if (Float.compare(fieldName, other.fieldName) != 0) return false; */
JCExpression clazz = genJavaLangTypeRef(node, isDouble ? "Double" : "Float");
List<JCExpression> args = List.of(thisDotField, otherDotField);
JCBinary compareCallEquals0 = maker.Binary(CTC_NOT_EQUAL, maker.Apply(
- List.<JCExpression>nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0));
+ List.<JCExpression>nil(), maker.Select(clazz, node.toName("compare")), args), maker.Literal(0));
return maker.If(compareCallEquals0, returnBool(maker, false), null);
}
diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
index 335ab1fe..12c22059 100644
--- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java
+++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2014 The Project Lombok Authors.
+ * Copyright (C) 2012-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -31,23 +31,24 @@ import lombok.core.HandlerPriority;
import lombok.experimental.FieldDefaults;
import lombok.experimental.NonFinal;
import lombok.experimental.PackagePrivate;
-import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacASTAdapter;
+import lombok.javac.JavacASTVisitor;
import lombok.javac.JavacNode;
import org.mangosdk.spi.ProviderFor;
import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
-import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
/**
* Handles the {@code lombok.FieldDefaults} annotation for eclipse.
*/
-@ProviderFor(JavacAnnotationHandler.class)
+@ProviderFor(JavacASTVisitor.class)
@HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier.
-public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
+public class HandleFieldDefaults extends JavacASTAdapter {
public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) {
if (checkForTypeLevelFieldDefaults) {
if (hasAnnotation(FieldDefaults.class, typeNode)) {
@@ -72,13 +73,13 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
//Skip fields that start with $
if (fieldDecl.name.toString().startsWith("$")) continue;
- setFieldDefaultsForField(field, errorNode.get(), level, makeFinal);
+ setFieldDefaultsForField(field, level, makeFinal);
}
return true;
}
- public void setFieldDefaultsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean makeFinal) {
+ public void setFieldDefaultsForField(JavacNode fieldNode, AccessLevel level, boolean makeFinal) {
JCVariableDecl field = (JCVariableDecl) fieldNode.get();
if (level != null && level != AccessLevel.NONE) {
if ((field.mods.flags & (Flags.PUBLIC | Flags.PRIVATE | Flags.PROTECTED)) == 0) {
@@ -90,38 +91,62 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> {
if (makeFinal && (field.mods.flags & Flags.FINAL) == 0) {
if (!hasAnnotationAndDeleteIfNeccessary(NonFinal.class, fieldNode)) {
- field.mods.flags |= Flags.FINAL;
+ if ((field.mods.flags & Flags.STATIC) == 0 || field.init != null) {
+ field.mods.flags |= Flags.FINAL;
+ }
}
}
fieldNode.rebuild();
}
- @Override public void handle(AnnotationValues<FieldDefaults> annotation, JCAnnotation ast, JavacNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
-
- deleteAnnotationIfNeccessary(annotationNode, FieldDefaults.class);
- deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");
- JavacNode node = annotationNode.up();
- FieldDefaults instance = annotation.getInstance();
- AccessLevel level = instance.level();
- boolean makeFinal = instance.makeFinal();
+ @Override public void visitType(JavacNode typeNode, JCClassDecl type) {
+ AnnotationValues<FieldDefaults> fieldDefaults = null;
+ JavacNode source = typeNode;
- if (level == AccessLevel.NONE && !makeFinal) {
- annotationNode.addError("This does nothing; provide either level or makeFinal or both.");
- return;
+ boolean levelIsExplicit = false;
+ boolean makeFinalIsExplicit = false;
+ FieldDefaults fd = null;
+ for (JavacNode jn : typeNode.down()) {
+ if (jn.getKind() != Kind.ANNOTATION) continue;
+ JCAnnotation ann = (JCAnnotation) jn.get();
+ JCTree typeTree = ann.annotationType;
+ if (typeTree == null) continue;
+ String typeTreeToString = typeTree.toString();
+ if (!typeTreeToString.equals("FieldDefaults") && !typeTreeToString.equals("lombok.experimental.FieldDefaults")) continue;
+ if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue;
+
+ source = jn;
+ fieldDefaults = createAnnotation(FieldDefaults.class, jn);
+ levelIsExplicit = fieldDefaults.isExplicit("level");
+ makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal");
+
+ handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults");
+
+ fd = fieldDefaults.getInstance();
+ if (!levelIsExplicit && !makeFinalIsExplicit) {
+ jn.addError("This does nothing; provide either level or makeFinal or both.");
+ }
+
+ if (levelIsExplicit && fd.level() == AccessLevel.NONE) {
+ jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value.");
+ levelIsExplicit = false;
+ }
+
+ deleteAnnotationIfNeccessary(jn, FieldDefaults.class);
+ deleteImportFromCompilationUnit(jn, "lombok.AccessLevel");
+ break;
}
- if (level == AccessLevel.PACKAGE) {
- annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field.");
- }
+ if (fd == null && (type.mods.flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0) return;
- if (!makeFinal && annotation.isExplicit("makeFinal")) {
- annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field.");
- }
+ boolean defaultToPrivate = levelIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE));
+ boolean defaultToFinal = makeFinalIsExplicit ? false : Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE));
- if (node == null) return;
+ if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return;
+ AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null;
+ boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal;
- generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false);
+ generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false);
}
}
diff --git a/src/core/lombok/javac/handlers/HandleHelper.java b/src/core/lombok/javac/handlers/HandleHelper.java
new file mode 100644
index 00000000..99131f70
--- /dev/null
+++ b/src/core/lombok/javac/handlers/HandleHelper.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 The Project Lombok Authors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package lombok.javac.handlers;
+
+import static lombok.core.handlers.HandlerUtil.*;
+import static lombok.javac.handlers.JavacHandlerUtil.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.mangosdk.spi.ProviderFor;
+
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.TreeVisitor;
+import com.sun.source.util.TreeScanner;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import com.sun.tools.javac.util.Name;
+
+import lombok.ConfigurationKeys;
+import lombok.core.AST.Kind;
+import lombok.core.AnnotationValues;
+import lombok.experimental.Helper;
+import lombok.javac.JavacAnnotationHandler;
+import lombok.javac.JavacNode;
+import lombok.javac.JavacTreeMaker;
+
+@ProviderFor(JavacAnnotationHandler.class)
+public class HandleHelper extends JavacAnnotationHandler<Helper> {
+ @Override public void handle(AnnotationValues<Helper> annotation, JCAnnotation ast, JavacNode annotationNode) {
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.HELPER_FLAG_USAGE, "@Helper");
+
+ deleteAnnotationIfNeccessary(annotationNode, Helper.class);
+ JavacNode annotatedType = annotationNode.up();
+ JavacNode containingMethod = annotatedType == null ? null : annotatedType.up();
+
+ if (annotatedType == null || containingMethod == null || annotatedType.getKind() != Kind.TYPE || containingMethod.getKind() != Kind.METHOD) {
+ annotationNode.addError("@Helper is legal only on method-local classes.");
+ return;
+ }
+
+ JCClassDecl annotatedType_ = (JCClassDecl) annotatedType.get();
+ JCMethodDecl amd = (JCMethodDecl) containingMethod.get();
+ List<JCStatement> origStatements = amd.body.stats;
+ Iterator<JCStatement> it = origStatements.iterator();
+ while (it.hasNext()) {
+ if (it.next() == annotatedType_) {
+ break;
+ }
+ }
+
+ java.util.List<String> knownMethodNames = new ArrayList<String>();
+
+ for (JavacNode ch : annotatedType.down()) {
+ if (ch.getKind() != Kind.METHOD) continue;
+ String n = ch.getName();
+ if (n == null || n.isEmpty() || n.charAt(0) == '<') continue;
+ knownMethodNames.add(n);
+ }
+
+ Collections.sort(knownMethodNames);
+ final String[] knownMethodNames_ = knownMethodNames.toArray(new String[knownMethodNames.size()]);
+
+ final Name helperName = annotationNode.toName("$" + annotatedType_.name);
+ final boolean[] helperUsed = new boolean[1];
+ final JavacTreeMaker maker = annotationNode.getTreeMaker();
+
+ TreeVisitor<Void, Void> visitor = new TreeScanner<Void, Void>() {
+ @Override public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
+ JCMethodInvocation jcmi = (JCMethodInvocation) node;
+ apply(jcmi);
+ return super.visitMethodInvocation(node, p);
+ }
+
+ private void apply(JCMethodInvocation jcmi) {
+ if (!(jcmi.meth instanceof JCIdent)) return;
+ JCIdent jci = (JCIdent) jcmi.meth;
+ if (Arrays.binarySearch(knownMethodNames_, jci.name.toString()) < 0) return;
+ jcmi.meth = maker.Select(maker.Ident(helperName), jci.name);
+ helperUsed[0] = true;
+ }
+ };
+
+ while (it.hasNext()) {
+ JCStatement stat = it.next();
+ stat.accept(visitor, null);
+ }
+
+ if (!helperUsed[0]) {
+ annotationNode.addWarning("No methods of this helper class are ever used.");
+ return;
+ }
+
+ ListBuffer<JCStatement> newStatements = new ListBuffer<JCStatement>();
+
+ boolean mark = false;
+ for (JCStatement stat : origStatements) {
+ newStatements.append(stat);
+ if (mark || stat != annotatedType_) continue;
+ mark = true;
+ JCExpression init = maker.NewClass(null, List.<JCExpression>nil(), maker.Ident(annotatedType_.name), List.<JCExpression>nil(), null);
+ JCExpression varType = maker.Ident(annotatedType_.name);
+ JCVariableDecl decl = maker.VarDef(maker.Modifiers(Flags.FINAL), helperName, varType, init);
+ newStatements.append(decl);
+ }
+ amd.body.stats = newStatements.toList();
+ }
+}
diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java
index cd8e3402..81aa1525 100644
--- a/src/core/lombok/javac/handlers/HandleNonNull.java
+++ b/src/core/lombok/javac/handlers/HandleNonNull.java
@@ -85,7 +85,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
}
if (declaration.body == null) {
- annotationNode.addWarning("@NonNull is meaningless on a parameter of an abstract method.");
+ // This used to be a warning, but as @NonNull also has a documentary purpose, better to not warn about this. Since 1.16.7
return;
}
@@ -141,6 +141,7 @@ public class HandleNonNull extends JavacAnnotationHandler<NonNull> {
List<JCStatement> newList = tail.prepend(nullCheck);
for (JCStatement stat : head) newList = newList.prepend(stat);
declaration.body.stats = newList;
+ annotationNode.getAst().setChanged();
}
public boolean isNullCheck(JCStatement stat) {
diff --git a/src/core/lombok/javac/handlers/HandleUtilityClass.java b/src/core/lombok/javac/handlers/HandleUtilityClass.java
index a4f8cb45..010c05a5 100644
--- a/src/core/lombok/javac/handlers/HandleUtilityClass.java
+++ b/src/core/lombok/javac/handlers/HandleUtilityClass.java
@@ -52,7 +52,7 @@ import com.sun.tools.javac.util.Name;
@ProviderFor(JavacAnnotationHandler.class)
public class HandleUtilityClass extends JavacAnnotationHandler<UtilityClass> {
@Override public void handle(AnnotationValues<UtilityClass> annotation, JCAnnotation ast, JavacNode annotationNode) {
- handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.UTLITY_CLASS_FLAG_USAGE, "@UtilityClass");
+ handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.UTILITY_CLASS_FLAG_USAGE, "@UtilityClass");
deleteAnnotationIfNeccessary(annotationNode, UtilityClass.class);
diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java
index 9eadd750..337ab2d7 100644
--- a/src/core/lombok/javac/handlers/HandleVal.java
+++ b/src/core/lombok/javac/handlers/HandleVal.java
@@ -51,11 +51,11 @@ import com.sun.tools.javac.util.List;
@ResolutionResetNeeded
public class HandleVal extends JavacASTAdapter {
@Override public void visitLocal(JavacNode localNode, JCVariableDecl local) {
- if (local.vartype == null || (!local.vartype.toString().equals("val") && !local.vartype.toString().equals("lombok.val"))) return;
-
- JCTree source = local.vartype;
-
- if (!typeMatches(val.class, localNode, local.vartype)) return;
+ 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");
@@ -88,7 +88,7 @@ public class HandleVal extends JavacASTAdapter {
local.mods.flags |= Flags.FINAL;
if (!localNode.shouldDeleteLombokAnnotations()) {
- JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), source, localNode.getContext());
+ JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeTree, localNode.getContext());
local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation);
}
@@ -156,7 +156,7 @@ public class HandleVal extends JavacASTAdapter {
local.vartype = JavacResolution.createJavaLangObject(localNode.getAst());
throw e;
} finally {
- recursiveSetGeneratedBy(local.vartype, source, localNode.getContext());
+ recursiveSetGeneratedBy(local.vartype, typeTree, localNode.getContext());
}
}
}
diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
index 0db59da1..4e9be00b 100644
--- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java
+++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java
@@ -21,6 +21,7 @@
*/
package lombok.javac.handlers;
+import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.Javac.*;
import static lombok.javac.JavacAugments.JCTree_generatedNode;
@@ -840,14 +841,18 @@ public class JavacHandlerUtil {
List<JCTree> insertAfter = null;
List<JCTree> insertBefore = type.defs;
- while (insertBefore.tail != null) {
+ while (true) {
+ boolean skip = false;
if (insertBefore.head instanceof JCVariableDecl) {
JCVariableDecl f = (JCVariableDecl) insertBefore.head;
- if (isEnumConstant(f) || isGenerated(f)) {
- insertAfter = insertBefore;
- insertBefore = insertBefore.tail;
- continue;
- }
+ if (isEnumConstant(f) || isGenerated(f)) skip = true;
+ } else if (insertBefore.head instanceof JCMethodDecl) {
+ if ((((JCMethodDecl) insertBefore.head).mods.flags & GENERATEDCONSTR) != 0) skip = true;
+ }
+ if (skip) {
+ insertAfter = insertBefore;
+ insertBefore = insertBefore.tail;
+ continue;
}
break;
}
@@ -1485,6 +1490,18 @@ public class JavacHandlerUtil {
} catch (Exception ignore) {}
}
+ private static final Pattern FIND_RETURN = Pattern.compile("^\\s*\\**\\s*@returns?\\s+.*$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
+ static String addReturnsThisIfNeeded(String in) {
+ if (FIND_RETURN.matcher(in).find()) return in;
+
+ return addJavadocLine(in, "@return this");
+ }
+
+ static String addJavadocLine(String in, String line) {
+ if (in.endsWith("\n")) return in + line + "\n";
+ return in + "\n" + line;
+ }
+
private static class CopyJavadoc_8 {
static void copyJavadoc(JavacNode from, JCTree to, CopyJavadoc copyMode, Object dc) {
DocCommentTable dct = (DocCommentTable) dc;
@@ -1492,6 +1509,9 @@ public class JavacHandlerUtil {
if (javadoc != null) {
String[] filtered = copyMode.split(javadoc.getText());
+ if (copyMode == CopyJavadoc.SETTER && shouldReturnThis(from)) {
+ filtered[0] = addReturnsThisIfNeeded(filtered[0]);
+ }
dct.putComment(to, createJavadocComment(filtered[0], from));
dct.putComment(from.get(), createJavadocComment(filtered[1], from));
}
@@ -1525,6 +1545,9 @@ public class JavacHandlerUtil {
if (javadoc != null) {
String[] filtered = copyMode.split(javadoc);
+ if (copyMode == CopyJavadoc.SETTER && shouldReturnThis(from)) {
+ filtered[0] = addReturnsThisIfNeeded(filtered[0]);
+ }
docComments.put(to, filtered[0]);
docComments.put(from.get(), filtered[1]);
}
diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
index 7fca01ae..c6d601bd 100644
--- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
+++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java
@@ -267,13 +267,15 @@ public class JavacSingularsRecipes {
}
/** 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) {
+ protected JCExpression getSize(JavacTreeMaker maker, JavacNode builderType, Name name, boolean nullGuard, boolean parens) {
Name thisName = builderType.toName("this");
JCExpression fn = maker.Select(maker.Select(maker.Ident(thisName), name), builderType.toName("size"));
JCExpression sizeInvoke = maker.Apply(List.<JCExpression>nil(), fn, List.<JCExpression>nil());
if (nullGuard) {
JCExpression isNull = maker.Binary(CTC_EQUAL, maker.Select(maker.Ident(thisName), name), maker.Literal(CTC_BOT, 0));
- return maker.Conditional(isNull, maker.Literal(CTC_INT, 0), sizeInvoke);
+ JCExpression out = maker.Conditional(isNull, maker.Literal(CTC_INT, 0), sizeInvoke);
+ if (parens) return maker.Parens(out);
+ return out;
}
return sizeInvoke;
}
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
index 9ec77e78..3002a98f 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSingularizer.java
@@ -87,7 +87,7 @@ public class JavacJavaUtilListSingularizer extends JavacJavaUtilListSetSingulari
cases.append(defaultCase);
}
- JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true), cases.toList());
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, data.getPluralName(), true, false), cases.toList());
JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
localShadowerType = addTypeArgs(1, false, builderType, localShadowerType, data.getTypeArgs(), source);
JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
index f419a014..25669721 100644
--- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
+++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java
@@ -89,7 +89,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
cases.append(defaultCase);
}
- JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true), cases.toList());
+ JCStatement switchStat = maker.Switch(getSize(maker, builderType, mapMode ? builderType.toName(data.getPluralName() + "$key") : data.getPluralName(), true, false), cases.toList());
JCExpression localShadowerType = chainDotsString(builderType, data.getTargetFqn());
localShadowerType = addTypeArgs(mapMode ? 2 : 1, false, builderType, localShadowerType, data.getTypeArgs(), source);
JCStatement varDefStat = maker.VarDef(maker.Modifiers(0), data.getPluralName(), localShadowerType, null);
@@ -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), maker.Literal(CTC_INT, 0x40000000));
+ JCExpression lessThanCutoff = maker.Binary(CTC_LESS_THAN, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 0x40000000));
JCExpression integerMaxValue = genJavaLangTypeRef(builderType, "Integer", "MAX_VALUE");
- JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard));
- JCExpression sizeFormulaRightLeft = maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard), maker.Literal(CTC_INT, 3));
+ JCExpression sizeFormulaLeft = maker.Binary(CTC_PLUS, maker.Literal(CTC_INT, 1), getSize(maker, builderType, varName, nullGuard, true));
+ JCExpression sizeFormulaRightLeft = maker.Parens(maker.Binary(CTC_MINUS, getSize(maker, builderType, varName, nullGuard, true), maker.Literal(CTC_INT, 3)));
JCExpression sizeFormulaRight = maker.Binary(CTC_DIV, sizeFormulaRightLeft, maker.Literal(CTC_INT, 3));
JCExpression sizeFormula = maker.Binary(CTC_PLUS, sizeFormulaLeft, sizeFormulaRight);
constructorArgs = List.<JCExpression>of(maker.Conditional(lessThanCutoff, sizeFormula, integerMaxValue));
@@ -167,7 +167,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer {
JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar)));
JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2)));
JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0));
- JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard));
+ JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true));
JCExpression incrementExpr = maker.Unary(CTC_POSTINC, maker.Ident(ivar));
fillStat = maker.ForLoop(List.of(forInit), checkExpr, List.of(maker.Exec(incrementExpr)), putStatement);
} else {
diff --git a/src/core/lombok/package-info.java b/src/core/lombok/package-info.java
index b5406a74..5e34c914 100644
--- a/src/core/lombok/package-info.java
+++ b/src/core/lombok/package-info.java
@@ -29,6 +29,6 @@
* <li>{@code lombok.experimental} – This package contains lombok features that are new or likely to change before committing to long-term support.
* </ul>
*
- * @see <a href="http://projectlombok.org/features/index.html">Lombok features</a>
+ * @see <a href="https://projectlombok.org/features/index.html">Lombok features</a>
*/
package lombok;
diff --git a/src/core/lombok/val.java b/src/core/lombok/val.java
index cd8652d6..a398fd34 100644
--- a/src/core/lombok/val.java
+++ b/src/core/lombok/val.java
@@ -28,7 +28,7 @@ package lombok;
* <p>
* Note that this is an annotation type because {@code val x = 10;} will be desugared to {@code @val final int x = 10;}
* <p>
- * Complete documentation is found at <a href="http://projectlombok.org/features/val.html">the project lombok features page for &#64;val</a>.
+ * Complete documentation is found at <a href="https://projectlombok.org/features/val.html">the project lombok features page for &#64;val</a>.
*/
public @interface val {
}
diff --git a/src/delombok/lombok/delombok/DelombokResult.java b/src/delombok/lombok/delombok/DelombokResult.java
index 84aeb68b..8985b257 100644
--- a/src/delombok/lombok/delombok/DelombokResult.java
+++ b/src/delombok/lombok/delombok/DelombokResult.java
@@ -65,7 +65,8 @@ public class DelombokResult {
else comments_ = com.sun.tools.javac.util.List.from(comments.toArray(new CommentInfo[0]));
FormatPreferences preferences = new FormatPreferenceScanner().scan(formatPreferences, getContent());
- compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments_, preferences));
+ //compilationUnit.accept(new PrettyCommentsPrinter(out, compilationUnit, comments_, preferences));
+ compilationUnit.accept(new PrettyPrinter(out, compilationUnit, comments_, preferences));
}
private CharSequence getContent() throws IOException {
diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
deleted file mode 100644
index f57b74a2..00000000
--- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java
+++ /dev/null
@@ -1,1715 +0,0 @@
-/*
- * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-/*
- * Code derived from com.sun.tools.javac.tree.Pretty, from the langtools project.
- * A version can be found at, for example, http://hg.openjdk.java.net/jdk7/build/langtools
- */
-package lombok.delombok;
-
-import static com.sun.tools.javac.code.Flags.*;
-import static lombok.javac.Javac.*;
-import static lombok.javac.JavacTreeMaker.TreeTag.treeTag;
-import static lombok.javac.JavacTreeMaker.TypeTag.typeTag;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Map;
-
-import lombok.javac.CommentInfo;
-import lombok.javac.CommentInfo.EndConnection;
-import lombok.javac.CommentInfo.StartConnection;
-import lombok.javac.JavacTreeMaker.TreeTag;
-import lombok.javac.JavacTreeMaker.TypeTag;
-
-import com.sun.source.tree.Tree;
-import com.sun.tools.javac.code.Flags;
-import com.sun.tools.javac.code.Symbol;
-import com.sun.tools.javac.tree.DocCommentTable;
-import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.tree.JCTree.JCAnnotation;
-import com.sun.tools.javac.tree.JCTree.JCArrayAccess;
-import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
-import com.sun.tools.javac.tree.JCTree.JCAssert;
-import com.sun.tools.javac.tree.JCTree.JCAssign;
-import com.sun.tools.javac.tree.JCTree.JCAssignOp;
-import com.sun.tools.javac.tree.JCTree.JCBinary;
-import com.sun.tools.javac.tree.JCTree.JCBlock;
-import com.sun.tools.javac.tree.JCTree.JCBreak;
-import com.sun.tools.javac.tree.JCTree.JCCase;
-import com.sun.tools.javac.tree.JCTree.JCCatch;
-import com.sun.tools.javac.tree.JCTree.JCClassDecl;
-import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
-import com.sun.tools.javac.tree.JCTree.JCConditional;
-import com.sun.tools.javac.tree.JCTree.JCContinue;
-import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
-import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
-import com.sun.tools.javac.tree.JCTree.JCErroneous;
-import com.sun.tools.javac.tree.JCTree.JCExpression;
-import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
-import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
-import com.sun.tools.javac.tree.JCTree.JCForLoop;
-import com.sun.tools.javac.tree.JCTree.JCIdent;
-import com.sun.tools.javac.tree.JCTree.JCIf;
-import com.sun.tools.javac.tree.JCTree.JCImport;
-import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
-import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
-import com.sun.tools.javac.tree.JCTree.JCLiteral;
-import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
-import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
-import com.sun.tools.javac.tree.JCTree.JCModifiers;
-import com.sun.tools.javac.tree.JCTree.JCNewArray;
-import com.sun.tools.javac.tree.JCTree.JCNewClass;
-import com.sun.tools.javac.tree.JCTree.JCParens;
-import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
-import com.sun.tools.javac.tree.JCTree.JCReturn;
-import com.sun.tools.javac.tree.JCTree.JCSkip;
-import com.sun.tools.javac.tree.JCTree.JCStatement;
-import com.sun.tools.javac.tree.JCTree.JCSwitch;
-import com.sun.tools.javac.tree.JCTree.JCSynchronized;
-import com.sun.tools.javac.tree.JCTree.JCThrow;
-import com.sun.tools.javac.tree.JCTree.JCTry;
-import com.sun.tools.javac.tree.JCTree.JCTypeApply;
-import com.sun.tools.javac.tree.JCTree.JCTypeCast;
-import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
-import com.sun.tools.javac.tree.JCTree.JCUnary;
-import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
-import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
-import com.sun.tools.javac.tree.JCTree.JCWildcard;
-import com.sun.tools.javac.tree.JCTree.LetExpr;
-import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
-import com.sun.tools.javac.tree.TreeInfo;
-import com.sun.tools.javac.tree.TreeScanner;
-import com.sun.tools.javac.util.List;
-import com.sun.tools.javac.util.Name;
-import com.sun.tools.javac.util.Position;
-//import com.sun.tools.javac.code.TypeTags;
-
-/** Prints out a tree as an indented Java source program.
- *
- * <p><b>This is NOT part of any API supported by Sun Microsystems. If
- * you write code that depends on this, you do so at your own risk.
- * This code and its internal interfaces are subject to change or
- * deletion without notice.</b>
- */
-@SuppressWarnings("all") // Mainly sun code that has other warning settings
-public class PrettyCommentsPrinter extends JCTree.Visitor {
- private static final TreeTag PARENS = treeTag("PARENS");
- private static final TreeTag IMPORT = treeTag("IMPORT");
- private static final TreeTag VARDEF = treeTag("VARDEF");
- private static final TreeTag SELECT = treeTag("SELECT");
-
- private static final Map<TreeTag, String> OPERATORS;
-
- // StandardFlags | DEFAULT
- private static final long EXTENDED_STANDARD_FLAGS = 0x0fffL | 1L<<43;
-
- static {
- Map<TreeTag, String> map = new HashMap<TreeTag, String>();
-
- map.put(treeTag("POS"), "+");
- map.put(treeTag("NEG"), "-");
- map.put(treeTag("NOT"), "!");
- map.put(treeTag("COMPL"), "~");
- map.put(treeTag("PREINC"), "++");
- map.put(treeTag("PREDEC"), "--");
- map.put(treeTag("POSTINC"), "++");
- map.put(treeTag("POSTDEC"), "--");
- map.put(treeTag("NULLCHK"), "<*nullchk*>");
- map.put(treeTag("OR"), "||");
- map.put(treeTag("AND"), "&&");
- map.put(treeTag("EQ"), "==");
- map.put(treeTag("NE"), "!=");
- map.put(treeTag("LT"), "<");
- map.put(treeTag("GT"), ">");
- map.put(treeTag("LE"), "<=");
- map.put(treeTag("GE"), ">=");
- map.put(treeTag("BITOR"), "|");
- map.put(treeTag("BITXOR"), "^");
- map.put(treeTag("BITAND"), "&");
- map.put(treeTag("SL"), "<<");
- map.put(treeTag("SR"), ">>");
- map.put(treeTag("USR"), ">>>");
- map.put(treeTag("PLUS"), "+");
- map.put(treeTag("MINUS"), "-");
- map.put(treeTag("MUL"), "*");
- map.put(treeTag("DIV"), "/");
- map.put(treeTag("MOD"), "%");
-
- map.put(treeTag("BITOR_ASG"), "|=");
- map.put(treeTag("BITXOR_ASG"), "^=");
- map.put(treeTag("BITAND_ASG"), "&=");
- map.put(treeTag("SL_ASG"), "<<=");
- map.put(treeTag("SR_ASG"), ">>=");
- map.put(treeTag("USR_ASG"), ">>>=");
- map.put(treeTag("PLUS_ASG"), "+=");
- map.put(treeTag("MINUS_ASG"), "-=");
- map.put(treeTag("MUL_ASG"), "*=");
- map.put(treeTag("DIV_ASG"), "/=");
- map.put(treeTag("MOD_ASG"), "%=");
-
- OPERATORS = map;
- }
-
- private List<CommentInfo> comments;
- private final JCCompilationUnit cu;
- private boolean onNewLine = true;
- private boolean aligned = false;
- private boolean inParams = false;
-
- private boolean needsSpace = false;
- private boolean needsNewLine = false;
- private boolean needsAlign = false;
-
- // Flag for try-with-resources to make them not final and not print the last semicolon.
- // This flag is set just before printing the vardef and cleared when printing its modifiers.
- private boolean suppressFinalAndSemicolonsInTry = false;
-
- private final FormatPreferences formatPreferences;
-
- public PrettyCommentsPrinter(Writer out, JCCompilationUnit cu, List<CommentInfo> comments, FormatPreferences preferences) {
- this.out = out;
- this.comments = comments;
- this.cu = cu;
- this.formatPreferences = preferences;
- }
-
- private int endPos(JCTree tree) {
- return getEndPosition(tree, cu);
- }
-
- private void consumeComments(int until) throws IOException {
- consumeComments(until, null);
- }
-
- private void consumeComments(int until, JCTree tree) throws IOException {
- boolean prevNewLine = onNewLine;
- CommentInfo head = comments.head;
- while (comments.nonEmpty() && head.pos < until) {
- printComment(head);
- comments = comments.tail;
- head = comments.head;
- }
- if (!onNewLine && prevNewLine) {
- println();
- }
- }
-
- private void consumeTrailingComments(int from) throws IOException {
- boolean prevNewLine = onNewLine;
- CommentInfo head = comments.head;
- boolean stop = false;
- while (comments.nonEmpty() && head.prevEndPos == from && !stop && !(head.start == StartConnection.ON_NEXT_LINE || head.start == StartConnection.START_OF_LINE)) {
- from = head.endPos;
- printComment(head);
- stop = (head.end == EndConnection.ON_NEXT_LINE);
- comments = comments.tail;
- head = comments.head;
- }
- if (!onNewLine && prevNewLine) {
- println();
- }
- }
-
- private void printComment(CommentInfo comment) throws IOException {
- prepareComment(comment.start);
- print(comment.content);
- switch (comment.end) {
- case ON_NEXT_LINE:
- if (!aligned) {
- needsNewLine = true;
- needsAlign = true;
- }
- break;
- case AFTER_COMMENT:
- needsSpace = true;
- break;
- case DIRECT_AFTER_COMMENT:
- // do nothing
- break;
- }
- }
-
- private void prepareComment(StartConnection start) throws IOException {
- switch (start) {
- case DIRECT_AFTER_PREVIOUS:
- needsSpace = false;
- break;
- case AFTER_PREVIOUS:
- needsSpace = true;
- break;
- case START_OF_LINE:
- needsNewLine = true;
- needsAlign = false;
- break;
- case ON_NEXT_LINE:
- if (!aligned) {
- needsNewLine = true;
- needsAlign = true;
- }
- break;
- }
- }
-
- /** The output stream on which trees are printed.
- */
- Writer out;
-
- /** The current left margin.
- */
- int lmargin = 0;
-
- /** The enclosing class name.
- */
- Name enclClassName;
-
- /** A hashtable mapping trees to their documentation comments
- * (can be null)
- */
- Map<JCTree, String> docComments = null;
- DocCommentTable docTable = null;
-
- String getJavadocFor(JCTree node) {
- if (docComments != null) return docComments.get(node);
- if (docTable != null) return docTable.getCommentText(node);
- return null;
- }
-
- /** Align code to be indented to left margin.
- */
- void align() throws IOException {
- onNewLine = false;
- aligned = true;
- needsAlign = false;
- for (int i = 0; i < lmargin; i++) out.write(formatPreferences.indent());
- }
-
- /** Increase left margin by indentation width.
- */
- void indent() {
- lmargin++;
- }
-
- /** Decrease left margin by indentation width.
- */
- void undent() {
- lmargin--;
- }
-
- /** Enter a new precedence level. Emit a `(' if new precedence level
- * is less than precedence level so far.
- * @param contextPrec The precedence level in force so far.
- * @param ownPrec The new precedence level.
- */
- void open(int contextPrec, int ownPrec) throws IOException {
- if (ownPrec < contextPrec) out.write("(");
- }
-
- /** Leave precedence level. Emit a `(' if inner precedence level
- * is less than precedence level we revert to.
- * @param contextPrec The precedence level we revert to.
- * @param ownPrec The inner precedence level.
- */
- void close(int contextPrec, int ownPrec) throws IOException {
- if (ownPrec < contextPrec) out.write(")");
- }
-
- /** Print string, replacing all non-ascii character with unicode escapes.
- */
- public void print(Object s) throws IOException {
- boolean align = needsAlign;
- if (needsNewLine && !onNewLine) {
- println();
- }
- if (align && !aligned) {
- align();
- }
- if (needsSpace && !onNewLine && !aligned) {
- out.write(' ');
- }
- needsSpace = false;
-
- out.write(s.toString());
-
- onNewLine = false;
- aligned = false;
- }
-
- /** Print new line.
- */
- public void println() throws IOException {
- onNewLine = true;
- aligned = false;
- needsNewLine = false;
- out.write(lineSep);
- }
-
- String lineSep = System.getProperty("line.separator");
-
- /**************************************************************************
- * Traversal methods
- *************************************************************************/
-
- /** Exception to propagate IOException through visitXXX methods */
- private static class UncheckedIOException extends Error {
- static final long serialVersionUID = -4032692679158424751L;
- UncheckedIOException(IOException e) {
- super(e.getMessage(), e);
- }
- }
-
- /** Visitor argument: the current precedence level.
- */
- int prec;
-
- /** Visitor method: print expression tree.
- * @param prec The current precedence level.
- */
- public void printExpr(JCTree tree, int prec) throws IOException {
-
- int prevPrec = this.prec;
- try {
- this.prec = prec;
- if (tree == null) print("/*missing*/");
- else {
- consumeComments(tree.pos, tree);
- tree.accept(this);
- int endPos = endPos(tree);
- consumeTrailingComments(endPos);
- }
- } catch (UncheckedIOException ex) {
- IOException e = new IOException(ex.getMessage());
- e.initCause(ex);
- throw e;
- } finally {
- this.prec = prevPrec;
- }
- }
-
- /** Derived visitor method: print expression tree at minimum precedence level
- * for expression.
- */
- public void printExpr(JCTree tree) throws IOException {
- printExpr(tree, TreeInfo.noPrec);
- }
-
- /** Derived visitor method: print statement tree.
- */
- public void printStat(JCTree tree) throws IOException {
- if (isEmptyStat(tree)) {
- // printEmptyStat(); // -- starting in java 7, these get lost, so to be consistent, we never print them.
- } else {
- printExpr(tree, TreeInfo.notExpression);
- }
- }
-
- public void printEmptyStat() throws IOException {
- print(";");
- }
-
- public boolean isEmptyStat(JCTree tree) {
- if (!(tree instanceof JCBlock)) return false;
- JCBlock block = (JCBlock) tree;
- return (Position.NOPOS == block.pos) && block.stats.isEmpty();
- }
-
- /** Derived visitor method: print list of expression trees, separated by given string.
- * @param sep the separator string
- */
- public <T extends JCTree> void printExprs(List<T> trees, String sep) throws IOException {
- if (trees.nonEmpty()) {
- printExpr(trees.head);
- for (List<T> l = trees.tail; l.nonEmpty(); l = l.tail) {
- print(sep);
- printExpr(l.head);
- }
- }
- }
-
- /** Derived visitor method: print list of expression trees, separated by commas.
- */
- public <T extends JCTree> void printExprs(List<T> trees) throws IOException {
- printExprs(trees, ", ");
- }
-
- /** Derived visitor method: print list of statements, each on a separate line.
- */
- public void printStats(List<? extends JCTree> trees) throws IOException {
- for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) {
- if (isSuppressed(l.head)) continue;
- if (!suppressAlignmentForEmptyLines(l.head)) align();
- printStat(l.head);
- println();
- }
- }
-
- private boolean suppressAlignmentForEmptyLines(JCTree tree) {
- return !formatPreferences.fillEmpties() && startsWithNewLine(tree);
- }
-
- private boolean startsWithNewLine(JCTree tree) {
- return tree instanceof JCMethodDecl || tree instanceof JCClassDecl;
- }
-
- private boolean isSuppressed(JCTree tree) {
- if (isEmptyStat(tree)) {
- return true;
- }
- if (tree instanceof JCExpressionStatement) {
- return isNoArgsSuperCall(((JCExpressionStatement)tree).expr);
- }
- return false;
- }
-
- /** Print a set of modifiers.
- */
- public void printFlags(long flags) throws IOException {
- if ((flags & SYNTHETIC) != 0) print("/*synthetic*/ ");
- if (suppressFinalAndSemicolonsInTry) {
- flags = flags & ~FINAL;
- suppressFinalAndSemicolonsInTry = false;
- }
- print(TreeInfo.flagNames(flags));
- if ((flags & EXTENDED_STANDARD_FLAGS) != 0) print(" ");
- if ((flags & ANNOTATION) != 0) print("@");
- }
-
- public void printAnnotations(List<JCAnnotation> trees) throws IOException {
- for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
- printStat(l.head);
- if (inParams) {
- print(" ");
- }
- else {
- println();
- align();
- }
- }
- }
-
- /** Print documentation comment, if it exists
- * @param tree The tree for which a documentation comment should be printed.
- */
- public void printDocComment(JCTree tree) throws IOException {
- String dc = getJavadocFor(tree);
- if (dc == null) return;
- print("/**"); println();
- int pos = 0;
- int endpos = lineEndPos(dc, pos);
- boolean atStart = true;
- while (pos < dc.length()) {
- String line = dc.substring(pos, endpos);
- if (line.trim().isEmpty() && atStart) {
- atStart = false;
- continue;
- }
- atStart = false;
- align();
- print(" *");
- if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
- print(dc.substring(pos, endpos)); println();
- pos = endpos + 1;
- endpos = lineEndPos(dc, pos);
- }
- align(); print(" */"); println();
- align();
- }
-//where
- static int lineEndPos(String s, int start) {
- int pos = s.indexOf('\n', start);
- if (pos < 0) pos = s.length();
- return pos;
- }
-
- /** If type parameter list is non-empty, print it enclosed in "<...>" brackets.
- */
- public void printTypeParameters(List<JCTypeParameter> trees) throws IOException {
- if (trees.nonEmpty()) {
- print("<");
- printExprs(trees);
- print(">");
- }
- }
-
- /** Print a block.
- */
- public void printBlock(List<? extends JCTree> stats, JCTree container) throws IOException {
- print("{");
- println();
- indent();
- printStats(stats);
- consumeComments(endPos(container));
- undent();
- align();
- print("}");
- }
-
- /** Print a block.
- */
- public void printEnumBody(List<JCTree> stats) throws IOException {
- print("{");
- println();
- indent();
- boolean first = true;
- for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
- if (isEnumerator(l.head)) {
- if (!first) {
- print(",");
- println();
- }
- align();
- printStat(l.head);
- first = false;
- }
- }
- print(";");
- println();
- int x = 0;
- for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
- x++;
- if (!isEnumerator(l.head)) {
- if (!suppressAlignmentForEmptyLines(l.head)) align();
- printStat(l.head);
- println();
- }
- }
- undent();
- align();
- print("}");
- }
-
- public void printEnumMember(JCVariableDecl tree) throws IOException {
- printAnnotations(tree.mods.annotations);
- print(tree.name);
- if (tree.init instanceof JCNewClass) {
- JCNewClass constructor = (JCNewClass) tree.init;
- if (constructor.args != null && constructor.args.nonEmpty()) {
- print("(");
- printExprs(constructor.args);
- print(")");
- }
- if (constructor.def != null && constructor.def.defs != null) {
- print(" ");
- printBlock(constructor.def.defs, constructor.def);
- }
- }
- }
-
- /** Is the given tree an enumerator definition? */
- boolean isEnumerator(JCTree t) {
- return VARDEF.equals(treeTag(t)) && (((JCVariableDecl) t).mods.flags & ENUM) != 0;
- }
-
- /** Print unit consisting of package clause and import statements in toplevel,
- * followed by class definition. if class definition == null,
- * print all definitions in toplevel.
- * @param tree The toplevel tree
- * @param cdef The class definition, which is assumed to be part of the
- * toplevel tree.
- */
- public void printUnit(JCCompilationUnit tree, JCClassDecl cdef) throws IOException {
- Object dc = getDocComments(tree);
- loadDocCommentsTable(dc);
- printDocComment(tree);
- if (tree.pid != null) {
- consumeComments(tree.pos, tree);
- print("package ");
- printExpr(tree.pid);
- print(";");
- println();
- }
- boolean firstImport = true;
- for (List<JCTree> l = tree.defs;
- l.nonEmpty() && (cdef == null || IMPORT.equals(treeTag(l.head)));
- l = l.tail) {
- if (IMPORT.equals(treeTag(l.head))) {
- JCImport imp = (JCImport)l.head;
- Name name = TreeInfo.name(imp.qualid);
- if (name == name.table.fromChars(new char[] {'*'}, 0, 1) ||
- cdef == null ||
- isUsed(TreeInfo.symbol(imp.qualid), cdef)) {
- if (firstImport) {
- firstImport = false;
- println();
- }
- printStat(imp);
- }
- } else {
- printStat(l.head);
- }
- }
- if (cdef != null) {
- printStat(cdef);
- println();
- }
- }
- // where
- @SuppressWarnings("unchecked")
- private void loadDocCommentsTable(Object dc) {
- if (dc instanceof Map<?, ?>) this.docComments = (Map) dc;
- else if (dc instanceof DocCommentTable) this.docTable = (DocCommentTable) dc;
- }
-
- boolean isUsed(final Symbol t, JCTree cdef) {
- class UsedVisitor extends TreeScanner {
- public void scan(JCTree tree) {
- if (tree!=null && !result) tree.accept(this);
- }
- boolean result = false;
- public void visitIdent(JCIdent tree) {
- if (tree.sym == t) result = true;
- }
- }
- UsedVisitor v = new UsedVisitor();
- v.scan(cdef);
- return v.result;
- }
-
- /**************************************************************************
- * Visitor methods
- *************************************************************************/
-
- public void visitTopLevel(JCCompilationUnit tree) {
- try {
- printUnit(tree, null);
- consumeComments(Integer.MAX_VALUE);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitImport(JCImport tree) {
- try {
- print("import ");
- if (tree.staticImport) print("static ");
- printExpr(tree.qualid);
- print(";");
- println();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitClassDef(JCClassDecl tree) {
- try {
- consumeComments(tree.pos, tree);
- println(); align();
- printDocComment(tree);
- printAnnotations(tree.mods.annotations);
- printFlags(tree.mods.flags & ~INTERFACE);
- Name enclClassNamePrev = enclClassName;
- enclClassName = tree.name;
- if ((tree.mods.flags & INTERFACE) != 0) {
- print("interface " + tree.name);
- printTypeParameters(tree.typarams);
- if (tree.implementing.nonEmpty()) {
- print(" extends ");
- printExprs(tree.implementing);
- }
- } else {
- if ((tree.mods.flags & ENUM) != 0)
- print("enum " + tree.name);
- else
- print("class " + tree.name);
- printTypeParameters(tree.typarams);
- if (getExtendsClause(tree) != null) {
- print(" extends ");
- printExpr(getExtendsClause(tree));
- }
- if (tree.implementing.nonEmpty()) {
- print(" implements ");
- printExprs(tree.implementing);
- }
- }
- print(" ");
- // <Added for delombok by Reinier Zwitserloot>
- if ((tree.mods.flags & INTERFACE) != 0) {
- removeImplicitModifiersForInterfaceMembers(tree.defs);
- }
- // </Added for delombok by Reinier Zwitserloot>
- if ((tree.mods.flags & ENUM) != 0) {
- printEnumBody(tree.defs);
- } else {
- printBlock(tree.defs, tree);
- }
- enclClassName = enclClassNamePrev;
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- // Added for delombok by Reinier Zwitserloot
- private void removeImplicitModifiersForInterfaceMembers(List<JCTree> defs) {
- for (JCTree def :defs) {
- if (def instanceof JCVariableDecl) {
- ((JCVariableDecl) def).mods.flags &= ~(Flags.PUBLIC | Flags.STATIC | Flags.FINAL);
- }
- if (def instanceof JCMethodDecl) {
- ((JCMethodDecl) def).mods.flags &= ~(Flags.PUBLIC | Flags.ABSTRACT);
- }
- if (def instanceof JCClassDecl) {
- ((JCClassDecl) def).mods.flags &= ~(Flags.PUBLIC | Flags.STATIC);
- }
- }
- }
-
- public void visitMethodDef(JCMethodDecl tree) {
- try {
- boolean isConstructor = tree.name == tree.name.table.fromChars("<init>".toCharArray(), 0, 6);
- // when producing source output, omit anonymous constructors
- if (isConstructor && enclClassName == null) return;
- boolean isGeneratedConstructor = isConstructor && ((tree.mods.flags & Flags.GENERATEDCONSTR) != 0);
- if (isGeneratedConstructor) return;
- println(); align();
- printDocComment(tree);
- printExpr(tree.mods);
- printTypeParameters(tree.typarams);
- if (tree.typarams != null && tree.typarams.length() > 0) print(" ");
- if (tree.name == tree.name.table.fromChars("<init>".toCharArray(), 0, 6)) {
- print(enclClassName != null ? enclClassName : tree.name);
- } else {
- printExpr(tree.restype);
- print(" " + tree.name);
- }
- print("(");
- inParams = true;
- printExprs(tree.params);
- inParams = false;
- print(")");
- if (tree.thrown.nonEmpty()) {
- print(" throws ");
- printExprs(tree.thrown);
- }
- if (tree.defaultValue != null) {
- print(" default ");
- print(tree.defaultValue);
- }
- if (tree.body != null) {
- print(" ");
- printBlock(tree.body.stats, tree.body);
- } else {
- print(";");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitVarDef(JCVariableDecl tree) {
- try {
- boolean suppressSemi = suppressFinalAndSemicolonsInTry;
- if (getJavadocFor(tree) != null) {
- println(); align();
- }
- printDocComment(tree);
- if ((tree.mods.flags & ENUM) != 0) {
- printEnumMember(tree);
- } else {
- printExpr(tree.mods);
- if ((tree.mods.flags & VARARGS) != 0) {
- printExpr(((JCArrayTypeTree) tree.vartype).elemtype);
- print("... " + tree.name);
- } else {
- printExpr(tree.vartype);
- print(" " + tree.name);
- }
- if (tree.init != null) {
- print(" = ");
- printExpr(tree.init);
- }
- if (prec == TreeInfo.notExpression && !suppressSemi) print(";");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitSkip(JCSkip tree) {
- try {
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitBlock(JCBlock tree) {
- try {
- consumeComments(tree.pos);
- printFlags(tree.flags);
- printBlock(tree.stats, tree);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitDoLoop(JCDoWhileLoop tree) {
- try {
- print("do ");
- printStat(tree.body);
- align();
- print(" while ");
- if (PARENS.equals(treeTag(tree.cond))) {
- printExpr(tree.cond);
- } else {
- print("(");
- printExpr(tree.cond);
- print(")");
- }
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitWhileLoop(JCWhileLoop tree) {
- try {
- print("while ");
- if (PARENS.equals(treeTag(tree.cond))) {
- printExpr(tree.cond);
- } else {
- print("(");
- printExpr(tree.cond);
- print(")");
- }
- print(" ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitForLoop(JCForLoop tree) {
- try {
- print("for (");
- if (tree.init.nonEmpty()) {
- if (VARDEF.equals(treeTag(tree.init.head))) {
- printExpr(tree.init.head);
- for (List<JCStatement> l = tree.init.tail; l.nonEmpty(); l = l.tail) {
- JCVariableDecl vdef = (JCVariableDecl)l.head;
- print(", " + vdef.name + " = ");
- printExpr(vdef.init);
- }
- } else {
- printExprs(tree.init);
- }
- }
- print("; ");
- if (tree.cond != null) printExpr(tree.cond);
- print("; ");
- printExprs(tree.step);
- print(") ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitForeachLoop(JCEnhancedForLoop tree) {
- try {
- print("for (");
- printExpr(tree.var);
- print(" : ");
- printExpr(tree.expr);
- print(") ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitLabelled(JCLabeledStatement tree) {
- try {
- print(tree.label + ":");
- if (isEmptyStat(tree.body) || tree.body instanceof JCSkip) {
- print(" ;");
- } else if (tree.body instanceof JCBlock) {
- print(" ");
- printStat(tree.body);
- } else {
- println();
- align();
- printStat(tree.body);
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitSwitch(JCSwitch tree) {
- try {
- print("switch ");
- if (PARENS.equals(treeTag(tree.selector))) {
- printExpr(tree.selector);
- } else {
- print("(");
- printExpr(tree.selector);
- print(")");
- }
- print(" {");
- println();
- printStats(tree.cases);
- align();
- print("}");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitCase(JCCase tree) {
- try {
- if (tree.pat == null) {
- print("default");
- } else {
- print("case ");
- printExpr(tree.pat);
- }
- print(": ");
- println();
- indent();
- printStats(tree.stats);
- undent();
- align();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitSynchronized(JCSynchronized tree) {
- try {
- print("synchronized ");
- if (PARENS.equals(treeTag(tree.lock))) {
- printExpr(tree.lock);
- } else {
- print("(");
- printExpr(tree.lock);
- print(")");
- }
- print(" ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTry(JCTry tree) {
- try {
- print("try ");
- List<?> resources = null;
- try {
- Field f = JCTry.class.getField("resources");
- resources = (List<?>) f.get(tree);
- } catch (Exception ignore) {
- // In JDK6 and down this field does not exist; resources will retain its initializer value which is what we want.
- }
-
- if (resources != null && resources.nonEmpty()) {
- print("(");
- int remaining = resources.size();
- if (remaining == 1) {
- JCTree var = (JCTree) resources.get(0);
- suppressFinalAndSemicolonsInTry = true;
- printStat(var);
- print(") ");
- } else {
- indent(); indent();
- for (Object var0 : resources) {
- println();
- align();
- JCTree var = (JCTree) var0;
- suppressFinalAndSemicolonsInTry = true;
- printStat(var);
- remaining--;
- if (remaining > 0) print(";");
- }
- print(") ");
- undent(); undent();
- }
- }
-
- printStat(tree.body);
- for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
- printStat(l.head);
- }
- if (tree.finalizer != null) {
- print(" finally ");
- printStat(tree.finalizer);
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitCatch(JCCatch tree) {
- try {
- print(" catch (");
- printExpr(tree.param);
- print(") ");
- printStat(tree.body);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitConditional(JCConditional tree) {
- try {
- open(prec, TreeInfo.condPrec);
- printExpr(tree.cond, TreeInfo.condPrec);
- print(" ? ");
- printExpr(tree.truepart, TreeInfo.condPrec);
- print(" : ");
- printExpr(tree.falsepart, TreeInfo.condPrec);
- close(prec, TreeInfo.condPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitIf(JCIf tree) {
- try {
- print("if ");
- if (PARENS.equals(treeTag(tree.cond))) {
- printExpr(tree.cond);
- } else {
- print("(");
- printExpr(tree.cond);
- print(")");
- }
- print(" ");
- printStat(tree.thenpart);
- if (tree.elsepart != null) {
- print(" else ");
- printStat(tree.elsepart);
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private boolean isNoArgsSuperCall(JCExpression expr) {
- if (!(expr instanceof JCMethodInvocation)) return false;
- JCMethodInvocation tree = (JCMethodInvocation) expr;
- if (!tree.typeargs.isEmpty() || !tree.args.isEmpty()) return false;
- if (!(tree.meth instanceof JCIdent)) return false;
- return ((JCIdent) tree.meth).name.toString().equals("super");
- }
-
- public void visitExec(JCExpressionStatement tree) {
- if (isNoArgsSuperCall(tree.expr)) return;
- try {
- printExpr(tree.expr);
- if (prec == TreeInfo.notExpression) print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitBreak(JCBreak tree) {
- try {
- print("break");
- if (tree.label != null) print(" " + tree.label);
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitContinue(JCContinue tree) {
- try {
- print("continue");
- if (tree.label != null) print(" " + tree.label);
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitReturn(JCReturn tree) {
- try {
- print("return");
- if (tree.expr != null) {
- print(" ");
- printExpr(tree.expr);
- }
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitThrow(JCThrow tree) {
- try {
- print("throw ");
- printExpr(tree.expr);
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitAssert(JCAssert tree) {
- try {
- print("assert ");
- printExpr(tree.cond);
- if (tree.detail != null) {
- print(" : ");
- printExpr(tree.detail);
- }
- print(";");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitApply(JCMethodInvocation tree) {
- try {
- if (!tree.typeargs.isEmpty()) {
- if (SELECT.equals(treeTag(tree.meth))) {
- JCFieldAccess left = (JCFieldAccess)tree.meth;
- printExpr(left.selected);
- print(".<");
- printExprs(tree.typeargs);
- print(">" + left.name);
- } else {
- print("<");
- printExprs(tree.typeargs);
- print(">");
- printExpr(tree.meth);
- }
- } else {
- printExpr(tree.meth);
- }
- print("(");
- printExprs(tree.args);
- print(")");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitNewClass(JCNewClass tree) {
- try {
- if (tree.encl != null) {
- printExpr(tree.encl);
- print(".");
- }
- print("new ");
- if (!tree.typeargs.isEmpty()) {
- print("<");
- printExprs(tree.typeargs);
- print(">");
- }
- printExpr(tree.clazz);
- print("(");
- printExprs(tree.args);
- print(")");
- if (tree.def != null) {
- Name enclClassNamePrev = enclClassName;
- enclClassName =
- tree.def.name != null ? tree.def.name :
- tree.type != null && tree.type.tsym.name != tree.type.tsym.name.table.fromChars(new char[0], 0, 0) ? tree.type.tsym.name :
- null;
- if ((tree.def.mods.flags & Flags.ENUM) != 0) print("/*enum*/");
- printBlock(tree.def.defs, tree.def);
- enclClassName = enclClassNamePrev;
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitNewArray(JCNewArray tree) {
- try {
- if (tree.elemtype != null) {
- print("new ");
- JCTree elem = tree.elemtype;
- if (elem instanceof JCArrayTypeTree)
- printBaseElementType((JCArrayTypeTree) elem);
- else
- printExpr(elem);
- for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
- print("[");
- printExpr(l.head);
- print("]");
- }
- if (elem instanceof JCArrayTypeTree)
- printBrackets((JCArrayTypeTree) elem);
- }
- if (tree.elems != null) {
- if (tree.elemtype != null) print("[]");
- print("{");
- printExprs(tree.elems);
- print("}");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitParens(JCParens tree) {
- try {
- print("(");
- printExpr(tree.expr);
- print(")");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitAssign(JCAssign tree) {
- try {
- open(prec, TreeInfo.assignPrec);
- printExpr(tree.lhs, TreeInfo.assignPrec + 1);
- print(" = ");
- printExpr(tree.rhs, TreeInfo.assignPrec);
- close(prec, TreeInfo.assignPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public String operatorName(TreeTag tag) {
- String result = OPERATORS.get(tag);
- if (result == null) throw new Error();
- return result;
- }
-
- public void visitAssignop(JCAssignOp tree) {
- try {
- open(prec, TreeInfo.assignopPrec);
- printExpr(tree.lhs, TreeInfo.assignopPrec + 1);
- String opname = operatorName(treeTag(tree));
- print(" " + opname + " ");
- printExpr(tree.rhs, TreeInfo.assignopPrec);
- close(prec, TreeInfo.assignopPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitUnary(JCUnary tree) {
- try {
- int ownprec = isOwnPrec(tree);
- String opname = operatorName(treeTag(tree));
- open(prec, ownprec);
- if (isPrefixUnary(tree)) {
- print(opname);
- printExpr(tree.arg, ownprec);
- } else {
- printExpr(tree.arg, ownprec);
- print(opname);
- }
- close(prec, ownprec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private int isOwnPrec(JCExpression tree) {
- return treeTag(tree).getOperatorPrecedenceLevel();
- }
-
- private boolean isPrefixUnary(JCUnary tree) {
- return treeTag(tree).isPrefixUnaryOp();
- }
-
- public void visitBinary(JCBinary tree) {
- try {
- int ownprec = isOwnPrec(tree);
- String opname = operatorName(treeTag(tree));
- open(prec, ownprec);
- printExpr(tree.lhs, ownprec);
- print(" " + opname + " ");
- printExpr(tree.rhs, ownprec + 1);
- close(prec, ownprec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeCast(JCTypeCast tree) {
- try {
- open(prec, TreeInfo.prefixPrec);
- print("(");
- printExpr(tree.clazz);
- print(")");
- printExpr(tree.expr, TreeInfo.prefixPrec);
- close(prec, TreeInfo.prefixPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeTest(JCInstanceOf tree) {
- try {
- open(prec, TreeInfo.ordPrec);
- printExpr(tree.expr, TreeInfo.ordPrec);
- print(" instanceof ");
- printExpr(tree.clazz, TreeInfo.ordPrec + 1);
- close(prec, TreeInfo.ordPrec);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitIndexed(JCArrayAccess tree) {
- try {
- printExpr(tree.indexed, TreeInfo.postfixPrec);
- print("[");
- printExpr(tree.index);
- print("]");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitSelect(JCFieldAccess tree) {
- try {
- printExpr(tree.selected, TreeInfo.postfixPrec);
- print("." + tree.name);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitIdent(JCIdent tree) {
- try {
- print(tree.name);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitLiteral(JCLiteral tree) {
- TypeTag typeTag = typeTag(tree);
- try {
- if (CTC_INT.equals(typeTag)) print(tree.value.toString());
- else if (CTC_LONG.equals(typeTag)) print(tree.value + "L");
- else if (CTC_FLOAT.equals(typeTag)) print(tree.value + "F");
- else if (CTC_DOUBLE.equals(typeTag)) print(tree.value.toString());
- else if (CTC_CHAR.equals(typeTag)) {
- print("\'" + quoteChar((char)((Number)tree.value).intValue()) + "\'");
- }
- else if (CTC_BOOLEAN.equals(typeTag)) print(((Number)tree.value).intValue() == 1 ? "true" : "false");
- else if (CTC_BOT.equals(typeTag)) print("null");
- else print("\"" + quoteChars(tree.value.toString()) + "\"");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public static String quoteChars(String s) {
- StringBuilder buf = new StringBuilder();
- for (int i = 0; i < s.length(); i++) {
- buf.append(quoteChar(s.charAt(i)));
- }
- return buf.toString();
- }
-
- /**
- * Escapes a character if it has an escape sequence or is non-printable
- * ASCII. Leaves non-ASCII characters alone.
- */
- public static String quoteChar(char ch) {
- switch (ch) {
- case '\b':
- return "\\b";
- case '\f':
- return "\\f";
- case '\n':
- return "\\n";
- case '\r':
- return "\\r";
- case '\t':
- return "\\t";
- case '\'':
- return "\\'";
- case '\"':
- return "\\\"";
- case '\\':
- return "\\\\";
- default:
- return ch < 32 ? String.format("\\%03o", (int) ch) : String.valueOf(ch);
- }
- }
-
- public void visitTypeIdent(JCPrimitiveTypeTree tree) {
- TypeTag typetag = typeTag(tree);
- try {
- if (CTC_BYTE.equals(typetag)) print ("byte");
- else if (CTC_CHAR.equals(typetag)) print ("char");
- else if (CTC_SHORT.equals(typetag)) print ("short");
- else if (CTC_INT.equals(typetag)) print ("int");
- else if (CTC_LONG.equals(typetag)) print ("long");
- else if (CTC_FLOAT.equals(typetag)) print ("float");
- else if (CTC_DOUBLE.equals(typetag)) print ("double");
- else if (CTC_BOOLEAN.equals(typetag)) print ("boolean");
- else if (CTC_VOID.equals(typetag)) print ("void");
- else print("error");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeArray(JCArrayTypeTree tree) {
- try {
- printBaseElementType(tree);
- printBrackets(tree);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- // Prints the inner element type of a nested array
- private void printBaseElementType(JCArrayTypeTree tree) throws IOException {
- JCTree elem = tree.elemtype;
- while (elem instanceof JCWildcard)
- elem = ((JCWildcard) elem).inner;
- if (elem instanceof JCArrayTypeTree)
- printBaseElementType((JCArrayTypeTree) elem);
- else
- printExpr(elem);
- }
-
- // prints the brackets of a nested array in reverse order
- private void printBrackets(JCArrayTypeTree tree) throws IOException {
- JCTree elem;
- while (true) {
- elem = tree.elemtype;
- print("[]");
- if (!(elem instanceof JCArrayTypeTree)) break;
- tree = (JCArrayTypeTree) elem;
- }
- }
-
- public void visitTypeApply(JCTypeApply tree) {
- try {
- printExpr(tree.clazz);
- print("<");
- printExprs(tree.arguments);
- print(">");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTypeParameter(JCTypeParameter tree) {
- try {
- print(tree.name);
- if (tree.bounds.nonEmpty()) {
- print(" extends ");
- printExprs(tree.bounds, " & ");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- @Override
- public void visitWildcard(JCWildcard tree) {
- try {
- Object kind = tree.getClass().getField("kind").get(tree);
- print(kind);
- if (kind != null && kind.getClass().getSimpleName().equals("TypeBoundKind")) {
- kind = kind.getClass().getField("kind").get(kind);
- }
-
- if (tree.getKind() != Tree.Kind.UNBOUNDED_WILDCARD)
- printExpr(tree.inner);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- } catch (NoSuchFieldException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void visitTypeBoundKind(TypeBoundKind tree) {
- try {
- print(String.valueOf(tree.kind));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitErroneous(JCErroneous tree) {
- try {
- print("(ERROR)");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitLetExpr(LetExpr tree) {
- try {
- print("(let " + tree.defs + " in " + tree.expr + ")");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitModifiers(JCModifiers mods) {
- try {
- printAnnotations(mods.annotations);
- printFlags(mods.flags);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitAnnotation(JCAnnotation tree) {
- try {
- print("@");
- printExpr(tree.annotationType);
- if (tree.args.nonEmpty()) {
- print("(");
- if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) {
- JCExpression lhs = ((JCAssign)tree.args.get(0)).lhs;
- if (lhs instanceof JCIdent && ((JCIdent)lhs).name.toString().equals("value")) tree.args = List.of(((JCAssign)tree.args.get(0)).rhs);
- }
- printExprs(tree.args);
- print(")");
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public void visitTree(JCTree tree) {
- try {
- String simpleName = tree.getClass().getSimpleName();
- if ("JCTypeUnion".equals(simpleName)) {
- printExprs(readExpressionList(tree, "alternatives"), " | ");
- return;
- } else if ("JCTypeIntersection".equals(simpleName)) {
- printExprs(readExpressionList(tree, "bounds"), " & ");
- return;
- } else if ("JCLambda".equals(simpleName)) {
- visitLambda0(tree);
- return;
- } else if ("JCMemberReference".equals(simpleName)) {
- visitReference0(tree);
- return;
- } else {
- print("(UNKNOWN[" + tree.getClass().getSimpleName() + "]: " + tree + ")");
- println();
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private void visitLambda0(JCTree tree) {
- try {
- @SuppressWarnings("unchecked")
- List<JCVariableDecl> params = (List<JCVariableDecl>) readTreeList(tree, "params");
- boolean explicit = true;
- int paramLength = params.size();
- if (paramLength != 1) print("(");
- try {
- explicit = readObject(tree, "paramKind").toString().equals("EXPLICIT");
- } catch (Exception e) {}
- if (explicit) {
- printExprs(params);
- } else {
- String sep = "";
- for (JCVariableDecl param : params) {
- print(sep);
- print(param.name);
- sep = ", ";
- }
- }
- if (paramLength != 1) print(")");
- print(" -> ");
- printExpr(readTree(tree, "body"));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
-
- public void visitReference0(JCTree tree) {
- try {
- printExpr(readTree(tree, "expr"));
- print("::");
- List<JCExpression> typeArgs = readExpressionList(tree, "typeargs");
- if (typeArgs != null) {
- print("<");
- printExprs(typeArgs);
- print(">");
- }
- ;
- print(readObject(tree, "mode").toString().equals("INVOKE") ? readObject(tree, "name") : "new");
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- private JCTree readTree(JCTree tree, String fieldName) {
- try {
- return (JCTree) readObject0(tree, fieldName);
- } catch (Exception e) {
- return null;
- }
- }
-
- @SuppressWarnings("unchecked")
- private List<? extends JCTree> readTreeList(JCTree tree, String fieldName) throws IOException {
- try {
- return (List<? extends JCTree>) readObject0(tree, fieldName);
- } catch (Exception e) {
- return List.nil();
- }
- }
-
- @SuppressWarnings("unchecked")
- private List<JCExpression> readExpressionList(JCTree tree, String fieldName) throws IOException {
- try {
- return (List<JCExpression>) readObject0(tree, fieldName);
- } catch (Exception e) {
- return List.nil();
- }
- }
-
- private Object readObject(JCTree tree, String fieldName) {
- try {
- return readObject0(tree, fieldName);
- } catch (Exception e) {
- return null;
- }
- }
-
- @SuppressWarnings("unchecked")
- private Object readObject0(JCTree tree, String fieldName) throws Exception {
- try {
- return tree.getClass().getDeclaredField(fieldName).get(tree);
- } catch (Exception e) {
- print("ERROR_READING_FIELD");
- throw e;
- }
- }
-}
diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java
new file mode 100644
index 00000000..d3b65cb8
--- /dev/null
+++ b/src/delombok/lombok/delombok/PrettyPrinter.java
@@ -0,0 +1,1488 @@
+package lombok.delombok;
+
+import static com.sun.tools.javac.code.Flags.*;
+import static lombok.javac.Javac.*;
+import static lombok.javac.JavacTreeMaker.TreeTag.treeTag;
+import static lombok.javac.JavacTreeMaker.TypeTag.typeTag;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.sun.tools.javac.tree.DocCommentTable;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCArrayAccess;
+import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
+import com.sun.tools.javac.tree.JCTree.JCAssert;
+import com.sun.tools.javac.tree.JCTree.JCAssign;
+import com.sun.tools.javac.tree.JCTree.JCAssignOp;
+import com.sun.tools.javac.tree.JCTree.JCBinary;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCBreak;
+import com.sun.tools.javac.tree.JCTree.JCCase;
+import com.sun.tools.javac.tree.JCTree.JCCatch;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCConditional;
+import com.sun.tools.javac.tree.JCTree.JCContinue;
+import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
+import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop;
+import com.sun.tools.javac.tree.JCTree.JCErroneous;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCForLoop;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCIf;
+import com.sun.tools.javac.tree.JCTree.JCImport;
+import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
+import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
+import com.sun.tools.javac.tree.JCTree.JCLiteral;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCNewArray;
+import com.sun.tools.javac.tree.JCTree.JCNewClass;
+import com.sun.tools.javac.tree.JCTree.JCParens;
+import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
+import com.sun.tools.javac.tree.JCTree.JCReturn;
+import com.sun.tools.javac.tree.JCTree.JCSkip;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCSwitch;
+import com.sun.tools.javac.tree.JCTree.JCSynchronized;
+import com.sun.tools.javac.tree.JCTree.JCThrow;
+import com.sun.tools.javac.tree.JCTree.JCTry;
+import com.sun.tools.javac.tree.JCTree.JCTypeApply;
+import com.sun.tools.javac.tree.JCTree.JCTypeCast;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCUnary;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
+import com.sun.tools.javac.tree.JCTree.JCWildcard;
+import com.sun.tools.javac.tree.JCTree.TypeBoundKind;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Position;
+
+import lombok.javac.CommentInfo;
+import lombok.javac.CommentInfo.EndConnection;
+import lombok.javac.CommentInfo.StartConnection;
+import lombok.javac.JavacTreeMaker.TreeTag;
+import lombok.javac.JavacTreeMaker.TypeTag;
+
+public class PrettyPrinter extends JCTree.Visitor {
+ private static final String LINE_SEP = System.getProperty("line.separator");
+ private static final Map<TreeTag, String> OPERATORS;
+
+ static {
+ Map<TreeTag, String> map = new HashMap<TreeTag, String>();
+
+ map.put(treeTag("POS"), "+");
+ map.put(treeTag("NEG"), "-");
+ map.put(treeTag("NOT"), "!");
+ map.put(treeTag("COMPL"), "~");
+ map.put(treeTag("PREINC"), "++");
+ map.put(treeTag("PREDEC"), "--");
+ map.put(treeTag("POSTINC"), "++");
+ map.put(treeTag("POSTDEC"), "--");
+ map.put(treeTag("NULLCHK"), "<*nullchk*>");
+ map.put(treeTag("OR"), "||");
+ map.put(treeTag("AND"), "&&");
+ map.put(treeTag("EQ"), "==");
+ map.put(treeTag("NE"), "!=");
+ map.put(treeTag("LT"), "<");
+ map.put(treeTag("GT"), ">");
+ map.put(treeTag("LE"), "<=");
+ map.put(treeTag("GE"), ">=");
+ map.put(treeTag("BITOR"), "|");
+ map.put(treeTag("BITXOR"), "^");
+ map.put(treeTag("BITAND"), "&");
+ map.put(treeTag("SL"), "<<");
+ map.put(treeTag("SR"), ">>");
+ map.put(treeTag("USR"), ">>>");
+ map.put(treeTag("PLUS"), "+");
+ map.put(treeTag("MINUS"), "-");
+ map.put(treeTag("MUL"), "*");
+ map.put(treeTag("DIV"), "/");
+ map.put(treeTag("MOD"), "%");
+
+ map.put(treeTag("BITOR_ASG"), "|=");
+ map.put(treeTag("BITXOR_ASG"), "^=");
+ map.put(treeTag("BITAND_ASG"), "&=");
+ map.put(treeTag("SL_ASG"), "<<=");
+ map.put(treeTag("SR_ASG"), ">>=");
+ map.put(treeTag("USR_ASG"), ">>>=");
+ map.put(treeTag("PLUS_ASG"), "+=");
+ map.put(treeTag("MINUS_ASG"), "-=");
+ map.put(treeTag("MUL_ASG"), "*=");
+ map.put(treeTag("DIV_ASG"), "/=");
+ map.put(treeTag("MOD_ASG"), "%=");
+
+ OPERATORS = map;
+ }
+
+ private final Writer out;
+ private final JCCompilationUnit compilationUnit;
+ private List<CommentInfo> comments;
+ private final FormatPreferences formatPreferences;
+
+ private final Map<JCTree, String> docComments;
+ private final DocCommentTable docTable;
+ private int indent = 0;
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public PrettyPrinter(Writer out, JCCompilationUnit cu, List<CommentInfo> comments, FormatPreferences preferences) {
+ this.out = out;
+ this.comments = comments;
+ this.compilationUnit = cu;
+ this.formatPreferences = preferences;
+
+ /* load doc comments */ {
+ Object dc = getDocComments(compilationUnit);
+ if (dc instanceof Map<?, ?>) {
+ this.docComments = (Map) dc;
+ this.docTable = null;
+ } else if (dc instanceof DocCommentTable) {
+ this.docComments = null;
+ this.docTable = (DocCommentTable) dc;
+ } else {
+ this.docComments = null;
+ this.docTable = null;
+ }
+ }
+ }
+
+ private int endPos(JCTree tree) {
+ return getEndPosition(tree, compilationUnit);
+ }
+
+ private static int lineEndPos(String s, int start) {
+ int pos = s.indexOf('\n', start);
+ if (pos < 0) pos = s.length();
+ return pos;
+ }
+
+ private boolean needsAlign, needsNewLine, onNewLine = true, needsSpace, aligned;
+
+ public static final class UncheckedIOException extends RuntimeException {
+ UncheckedIOException(IOException source) {
+ super(toMsg(source));
+ setStackTrace(source.getStackTrace());
+ }
+
+ private static String toMsg(Throwable t) {
+ String msg = t.getMessage();
+ String n = t.getClass().getSimpleName();
+ if (msg == null || msg.isEmpty()) return n;
+ return n + ": " + msg;
+ }
+ }
+
+ private void align() {
+ if (!onNewLine) return;
+ try {
+ for (int i = 0; i < indent; i++) out.write(formatPreferences.indent());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ onNewLine = false;
+ aligned = true;
+ needsAlign = false;
+ }
+
+ private void print(JCTree tree) {
+ if (tree == null) {
+ print("/*missing*/");
+ return;
+ }
+
+ consumeComments(tree);
+ tree.accept(this);
+ consumeTrailingComments(endPos(tree));
+ }
+
+ private void print(List<? extends JCTree> trees, String infix) {
+ boolean first = true;
+ JCTree prev = null;
+ for (JCTree tree : trees) {
+ if (suppress(tree)) continue;
+ if (!first && infix != null && !infix.isEmpty()) {
+ if ("\n".equals(infix)) println(prev);
+ else print(infix);
+ }
+ first = false;
+ print(tree);
+ prev = tree;
+ }
+ }
+
+ private boolean suppress(JCTree tree) {
+ if (tree instanceof JCBlock) {
+ JCBlock block = (JCBlock) tree;
+ return (Position.NOPOS == block.pos) && block.stats.isEmpty();
+ }
+
+ if (tree instanceof JCExpressionStatement) {
+ JCExpression expr = ((JCExpressionStatement)tree).expr;
+ if (expr instanceof JCMethodInvocation) {
+ JCMethodInvocation inv = (JCMethodInvocation) expr;
+ if (!inv.typeargs.isEmpty() || !inv.args.isEmpty()) return false;
+ if (!(inv.meth instanceof JCIdent)) return false;
+ return ((JCIdent) inv.meth).name.toString().equals("super");
+ }
+ }
+
+ return false;
+ }
+
+ private void print(CharSequence s) {
+ boolean align = needsAlign;
+ if (needsNewLine && !onNewLine) println();
+ if (align && !aligned) align();
+ try {
+ if (needsSpace && !onNewLine && !aligned) out.write(' ');
+ out.write(s.toString());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ needsSpace = false;
+ onNewLine = false;
+ aligned = false;
+ }
+
+
+ private void println() {
+ try {
+ out.write(LINE_SEP);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ onNewLine = true;
+ aligned = false;
+ needsNewLine = false;
+ }
+
+ private void println(JCTree completed) {
+ if (completed != null) {
+ int endPos = endPos(completed);
+ consumeTrailingComments(endPos);
+ }
+ try {
+ out.write(LINE_SEP);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ onNewLine = true;
+ aligned = false;
+ needsNewLine = false;
+ }
+
+ private void println(CharSequence s) {
+ print(s);
+ println();
+ }
+
+ private void println(CharSequence s, JCTree completed) {
+ print(s);
+ println(completed);
+ }
+
+ private void aPrint(CharSequence s) {
+ align();
+ print(s);
+ }
+
+ private void aPrintln(CharSequence s) {
+ align();
+ print(s);
+ println();
+ }
+
+ private void aPrintln(CharSequence s, JCTree completed) {
+ align();
+ print(s);
+ println(completed);
+ }
+
+ private void consumeComments(int until) {
+ CommentInfo head = comments.head;
+ while (comments.nonEmpty() && head.pos < until) {
+ printComment(head);
+ comments = comments.tail;
+ head = comments.head;
+ }
+ }
+
+ private void consumeComments(JCTree tree) {
+ consumeComments(tree.pos);
+ }
+
+ private void consumeTrailingComments(int from) {
+ boolean prevNewLine = onNewLine;
+ CommentInfo head = comments.head;
+ boolean stop = false;
+
+ while (comments.nonEmpty() && head.prevEndPos == from && !stop && !(head.start == StartConnection.ON_NEXT_LINE || head.start == StartConnection.START_OF_LINE)) {
+ from = head.endPos;
+ printComment(head);
+ stop = (head.end == EndConnection.ON_NEXT_LINE);
+ comments = comments.tail;
+ head = comments.head;
+ }
+
+ if (!onNewLine && prevNewLine) {
+ println();
+ }
+ }
+
+ private String getJavadocFor(JCTree node) {
+ if (docComments != null) return docComments.get(node);
+ if (docTable != null) return docTable.getCommentText(node);
+ return null;
+ }
+
+ private int dims(JCExpression vartype) {
+ if (vartype instanceof JCArrayTypeTree) {
+ return 1 + dims(((JCArrayTypeTree) vartype).elemtype);
+ }
+
+ return 0;
+ }
+
+ private void printComment(CommentInfo comment) {
+ switch (comment.start) {
+ case DIRECT_AFTER_PREVIOUS:
+ needsSpace = false;
+ break;
+ case AFTER_PREVIOUS:
+ needsSpace = true;
+ break;
+ case START_OF_LINE:
+ needsNewLine = true;
+ needsAlign = false;
+ break;
+ case ON_NEXT_LINE:
+ if (!onNewLine) {
+ needsNewLine = true;
+ needsAlign = true;
+ } else if (!aligned) {
+ needsAlign = true;
+ }
+ break;
+ }
+
+ if (onNewLine && !aligned && comment.start != StartConnection.START_OF_LINE) needsAlign = true;
+
+ print(comment.content);
+
+ switch (comment.end) {
+ case ON_NEXT_LINE:
+ if (!aligned) {
+ needsNewLine = true;
+ needsAlign = true;
+ }
+ break;
+ case AFTER_COMMENT:
+ needsSpace = true;
+ break;
+ case DIRECT_AFTER_COMMENT:
+ // do nothing
+ break;
+ }
+ }
+
+ private void printDocComment(JCTree tree) {
+ String dc = getJavadocFor(tree);
+ if (dc == null) return;
+ aPrintln("/**");
+ int pos = 0;
+ int endpos = lineEndPos(dc, pos);
+ boolean atStart = true;
+ while (pos < dc.length()) {
+ String line = dc.substring(pos, endpos);
+ if (line.trim().isEmpty() && atStart) {
+ atStart = false;
+ continue;
+ }
+ atStart = false;
+ aPrint(" *");
+ if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
+ println(dc.substring(pos, endpos));
+ pos = endpos + 1;
+ endpos = lineEndPos(dc, pos);
+ }
+ aPrintln(" */");
+ }
+
+ private Name __INIT__, __VALUE__;
+ private Name name_init(Name someName) {
+ if (__INIT__ == null) __INIT__ = someName.table.fromChars("<init>".toCharArray(), 0, 6);
+ return __INIT__;
+ }
+ private Name name_value(Name someName) {
+ if (__VALUE__ == null) __VALUE__ = someName.table.fromChars("value".toCharArray(), 0, 5);
+ return __VALUE__;
+ }
+
+ @Override public void visitTopLevel(JCCompilationUnit tree) {
+ printDocComment(tree);
+ if (tree.pid != null) {
+ consumeComments(tree);
+ aPrint("package ");
+ print(tree.pid);
+ println(";", tree.pid);
+ }
+
+ boolean first = true;
+
+ for (JCTree child : tree.defs) {
+ if (!(child instanceof JCImport)) continue;
+ if (first) println();
+ first = false;
+ print(child);
+ }
+
+ for (JCTree child : tree.defs) {
+ if (child instanceof JCImport) continue;
+ print(child);
+ }
+ consumeComments(Integer.MAX_VALUE);
+ }
+
+ @Override public void visitImport(JCImport tree) {
+ aPrint("import ");
+ if (tree.staticImport) print("static ");
+ print(tree.qualid);
+ println(";", tree);
+ }
+
+ private Name currentTypeName;
+ @Override public void visitClassDef(JCClassDecl tree) {
+ println();
+ printDocComment(tree);
+ align();
+ print(tree.mods);
+
+ boolean isInterface = (tree.mods.flags & INTERFACE) != 0;
+ boolean isAnnotationInterface = isInterface && (tree.mods.flags & ANNOTATION) != 0;
+ boolean isEnum = (tree.mods.flags & ENUM) != 0;
+
+ if (isAnnotationInterface) print("@interface ");
+ else if (isInterface) print("interface ");
+ else if (isEnum) print("enum ");
+ else print("class ");
+
+ print(tree.name);
+ Name prevTypeName = currentTypeName;
+ currentTypeName = tree.name;
+
+ if (tree.typarams.nonEmpty()) {
+ print("<");
+ print(tree.typarams, ", ");
+ print(">");
+ }
+ JCTree extendsClause = getExtendsClause(tree);
+ if (extendsClause != null) {
+ print(" extends ");
+ print(extendsClause);
+ }
+
+ if (tree.implementing.nonEmpty()) {
+ print(isInterface ? " extends " : " implements ");
+ print(tree.implementing, ", ");
+ }
+
+ println(" {");
+ indent++;
+ printClassMembers(tree.defs, isEnum, isInterface);
+ consumeComments(endPos(tree));
+ indent--;
+ aPrintln("}", tree);
+ currentTypeName = prevTypeName;
+ }
+
+ private void printClassMembers(List<JCTree> members, boolean isEnum, boolean isInterface) {
+ Class<?> prefType = null;
+ int typeOfPrevEnumMember = isEnum ? 3 : 0; // 1 = normal, 2 = with body, 3 = no enum field yet.
+ boolean prevWasEnumMember = isEnum;
+
+ for (JCTree member : members) {
+ if (typeOfPrevEnumMember == 3 && member instanceof JCMethodDecl && (((JCMethodDecl) member).mods.flags & GENERATEDCONSTR) != 0) continue;
+ boolean isEnumVar = isEnum && member instanceof JCVariableDecl && (((JCVariableDecl) member).mods.flags & ENUM) != 0;
+ if (!isEnumVar && prevWasEnumMember) {
+ prevWasEnumMember = false;
+ if (typeOfPrevEnumMember == 3) align();
+ println(";");
+ }
+
+ if (isEnumVar) {
+ if (prefType != null && prefType != JCVariableDecl.class) println();
+ switch (typeOfPrevEnumMember) {
+ case 1:
+ print(", ");
+ break;
+ case 2:
+ println(",");
+ align();
+ break;
+ }
+ print(member);
+ JCTree init = ((JCVariableDecl) member).init;
+ typeOfPrevEnumMember = init instanceof JCNewClass && ((JCNewClass) init).def != null ? 2 : 1;
+ } else if (member instanceof JCVariableDecl) {
+ if (prefType != null && prefType != JCVariableDecl.class) println();
+ if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC | FINAL);
+ print(member);
+ } else if (member instanceof JCMethodDecl) {
+ if ((((JCMethodDecl) member).mods.flags & GENERATEDCONSTR) != 0) continue;
+ if (prefType != null) println();
+ if (isInterface) flagMod = -1L & ~(PUBLIC | ABSTRACT);
+ print(member);
+ } else if (member instanceof JCClassDecl) {
+ if (prefType != null) println();
+ if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC);
+ print(member);
+ } else {
+ if (prefType != null) println();
+ print(member);
+ }
+
+ prefType = member.getClass();
+ }
+
+ if (prevWasEnumMember) {
+ prevWasEnumMember = false;
+ if (typeOfPrevEnumMember == 3) align();
+ println(";");
+ }
+ }
+
+ @Override public void visitTypeParameter(JCTypeParameter tree) {
+ List<JCExpression> annotations = readObject(tree, "annotations", List.<JCExpression>nil());
+ if (!annotations.isEmpty()) {
+ print(annotations, " ");
+ print(" ");
+ }
+ print(tree.name);
+ if (tree.bounds.nonEmpty()) {
+ print(" extends ");
+ print(tree.bounds, " & ");
+ }
+ consumeComments(tree);
+ }
+
+ @Override public void visitVarDef(JCVariableDecl tree) {
+ printDocComment(tree);
+ align();
+ if ((tree.mods.flags & ENUM) != 0) {
+ printEnumMember(tree);
+ return;
+ }
+ printAnnotations(tree.mods.annotations, true);
+ printModifierKeywords(tree.mods);
+ printVarDef0(tree);
+ println(";", tree);
+ }
+
+ private void printVarDefInline(JCVariableDecl tree) {
+ printAnnotations(tree.mods.annotations, false);
+ printModifierKeywords(tree.mods);
+ printVarDef0(tree);
+ }
+
+ private void printVarDef0(JCVariableDecl tree) {
+ boolean varargs = (tree.mods.flags & VARARGS) != 0;
+ if (varargs && tree.vartype instanceof JCArrayTypeTree) {
+ print(((JCArrayTypeTree) tree.vartype).elemtype);
+ print("...");
+ } else {
+ print(tree.vartype);
+ }
+ print(" ");
+ print(tree.name);
+ if (tree.init != null) {
+ print(" = ");
+ print(tree.init);
+ }
+ }
+
+ private void printEnumMember(JCVariableDecl tree) {
+ printAnnotations(tree.mods.annotations, true);
+ print(tree.name);
+ if (tree.init instanceof JCNewClass) {
+ JCNewClass constructor = (JCNewClass) tree.init;
+ if (constructor.args != null && constructor.args.nonEmpty()) {
+ print("(");
+ print(constructor.args, ", ");
+ print(")");
+ }
+
+ if (constructor.def != null && constructor.def.defs != null) {
+ println(" {");
+ indent++;
+ printClassMembers(constructor.def.defs, false, false);
+ consumeComments(endPos(tree));
+ indent--;
+ aPrint("}");
+ }
+ }
+ }
+
+ // TODO: Test postfix syntax for methods (?), for decls. Multiline vardefs, possibly with comments. enums with bodies. constructor-local generics, method-local generics, also do/while, finally, try-with-resources, lambdas, annotations in java8 places...
+ // TODO: Whatever is JCAnnotatedType? We handle it in the 7+ bucket in the old one...
+
+ @Override public void visitTypeApply(JCTypeApply tree) {
+ print(tree.clazz);
+ print("<");
+ print(tree.arguments, ", ");
+ print(">");
+ }
+
+ @Override public void visitWildcard(JCWildcard tree) {
+ switch (tree.getKind()) {
+ default:
+ case UNBOUNDED_WILDCARD:
+ print("?");
+ return;
+ case EXTENDS_WILDCARD:
+ print("? extends ");
+ print(tree.inner);
+ return;
+ case SUPER_WILDCARD:
+ print("? super ");
+ print(tree.inner);
+ return;
+ }
+ }
+
+ @Override public void visitLiteral(JCLiteral tree) {
+ TypeTag typeTag = typeTag(tree);
+ if (CTC_INT.equals(typeTag)) print("" + tree.value);
+ else if (CTC_LONG.equals(typeTag)) print(tree.value + "L");
+ else if (CTC_FLOAT.equals(typeTag)) print(tree.value + "F");
+ else if (CTC_DOUBLE.equals(typeTag)) print("" + tree.value);
+ else if (CTC_CHAR.equals(typeTag)) {
+ print("\'" + quoteChar((char)((Number)tree.value).intValue()) + "\'");
+ }
+ else if (CTC_BOOLEAN.equals(typeTag)) print(((Number)tree.value).intValue() == 1 ? "true" : "false");
+ else if (CTC_BOT.equals(typeTag)) print("null");
+ else print("\"" + quoteChars(tree.value.toString()) + "\"");
+ }
+
+ @Override public void visitMethodDef(JCMethodDecl tree) {
+ boolean isConstructor = tree.name == name_init(tree.name);
+ if (isConstructor && (tree.mods.flags & GENERATEDCONSTR) != 0) return;
+ printDocComment(tree);
+ align();
+ print(tree.mods);
+ if (tree.typarams != null && tree.typarams.nonEmpty()) {
+ print("<");
+ print(tree.typarams, ", ");
+ print("> ");
+ }
+
+ if (isConstructor) {
+ print(currentTypeName == null ? "<init>" : currentTypeName);
+ } else {
+ print(tree.restype);
+ print(" ");
+ print(tree.name);
+ }
+
+ print("(");
+ boolean first = true;
+ for (JCVariableDecl param : tree.params) {
+ if (!first) print(", ");
+ first = false;
+ printVarDefInline(param);
+ }
+ print(")");
+
+ if (tree.thrown.nonEmpty()) {
+ print(" throws ");
+ print(tree.thrown, ", ");
+ }
+
+ if (tree.defaultValue != null) {
+ print(" default ");
+ print(tree.defaultValue);
+ }
+
+ if (tree.body != null) {
+ print(" ");
+ print(tree.body);
+ } else println(";", tree);
+ }
+
+ @Override public void visitSkip(JCSkip that) {
+ if (onNewLine && !aligned) {
+ align();
+ }
+ println(";");
+ }
+
+ @Override public void visitAnnotation(JCAnnotation tree) {
+ print("@");
+ print(tree.annotationType);
+ if (tree.args.isEmpty()) return;
+ print("(");
+ boolean done = false;
+ if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) {
+ JCAssign arg1 = (JCAssign) tree.args.get(0);
+ JCIdent arg1Name = arg1.lhs instanceof JCIdent ? ((JCIdent) arg1.lhs) : null;
+ if (arg1Name != null && arg1Name.name == name_value(arg1Name.name)) {
+ print(arg1.rhs);
+ done = true;
+ }
+ }
+ if (!done) print(tree.args, ", ");
+ print(")");
+ }
+
+ @Override public void visitTypeArray(JCArrayTypeTree tree) {
+ JCTree elem = tree.elemtype;
+ while (elem instanceof JCWildcard) elem = ((JCWildcard) elem).inner;
+ print(elem);
+ print("[]");
+ }
+
+ @Override public void visitNewArray(JCNewArray tree) {
+ JCTree elem = tree.elemtype;
+ int dims = 0;
+ if (elem != null) {
+ print("new ");
+
+ while (elem instanceof JCArrayTypeTree) {
+ dims++;
+ elem = ((JCArrayTypeTree) elem).elemtype;
+ }
+ print(elem);
+
+ for (JCExpression expr : tree.dims) {
+ print("[");
+ print(expr);
+ print("]");
+ }
+ }
+
+ for (int i = 0; i < dims; i++) print("[]");
+
+ if (tree.elems != null) {
+ if (elem != null) print("[] ");
+ print("{");
+ print(tree.elems, ", ");
+ print("}");
+ }
+ }
+
+ @Override public void visitNewClass(JCNewClass tree) {
+ if (tree.encl != null) {
+ print(tree.encl);
+ print(".");
+ }
+
+ print("new ");
+ if (!tree.typeargs.isEmpty()) {
+ print("<");
+ print(tree.typeargs, ", ");
+ print(">");
+ }
+ print(tree.clazz);
+ print("(");
+ print(tree.args, ", ");
+ print(")");
+ if (tree.def != null) {
+ Name previousTypeName = currentTypeName;
+ currentTypeName = null;
+ println(" {");
+ indent++;
+ print(tree.def.defs, "");
+ indent--;
+ aPrint("}");
+ currentTypeName = previousTypeName;
+ }
+ }
+
+ @Override public void visitIndexed(JCArrayAccess tree) {
+ print(tree.indexed);
+ print("[");
+ print(tree.index);
+ print("]");
+ }
+
+ @Override public void visitTypeIdent(JCPrimitiveTypeTree tree) {
+ TypeTag typeTag = typeTag(tree);
+
+ if (CTC_BYTE.equals(typeTag)) print("byte");
+ else if (CTC_CHAR.equals(typeTag)) print("char");
+ else if (CTC_SHORT.equals(typeTag)) print("short");
+ else if (CTC_INT.equals(typeTag)) print("int");
+ else if (CTC_LONG.equals(typeTag)) print("long");
+ else if (CTC_FLOAT.equals(typeTag)) print("float");
+ else if (CTC_DOUBLE.equals(typeTag)) print("double");
+ else if (CTC_BOOLEAN.equals(typeTag)) print("boolean");
+ else if (CTC_VOID.equals(typeTag)) print("void");
+ else print("error");
+ }
+
+ @Override public void visitLabelled(JCLabeledStatement tree) {
+ aPrint(tree.label);
+ print(":");
+ if (tree.body instanceof JCSkip || suppress(tree)) {
+ println(" ;", tree);
+ } else if (tree.body instanceof JCBlock) {
+ print(" ");
+ print(tree.body);
+ } else {
+ println(tree);
+ print(tree.body);
+ }
+ }
+
+ private long flagMod = -1L;
+ private static final long DEFAULT = 1L<<43;
+
+ @Override public void visitModifiers(JCModifiers tree) {
+ printAnnotations(tree.annotations, true);
+ printModifierKeywords(tree);
+ }
+
+ private void printAnnotations(List<JCAnnotation> annotations, boolean newlines) {
+ for (JCAnnotation ann : annotations) {
+ print(ann);
+ if (newlines) {
+ println();
+ align();
+ } else print(" ");
+ }
+ }
+
+ private void printModifierKeywords(JCModifiers tree) {
+ long v = flagMod & tree.flags;
+ flagMod = -1L;
+
+ if ((v & SYNTHETIC) != 0) print("/* synthetic */ ");
+ if ((v & PUBLIC) != 0) print("public ");
+ if ((v & PRIVATE) != 0) print("private ");
+ if ((v & PROTECTED) != 0) print("protected ");
+ if ((v & STATIC) != 0) print("static ");
+ if ((v & FINAL) != 0) print("final ");
+ if ((v & SYNCHRONIZED) != 0) print("synchronized ");
+ if ((v & VOLATILE) != 0) print("volatile ");
+ if ((v & TRANSIENT) != 0) print("transient ");
+ if ((v & NATIVE) != 0) print("native ");
+ if ((v & ABSTRACT) != 0) print("abstract ");
+ if ((v & STRICTFP) != 0) print("strictfp ");
+ if ((v & DEFAULT) != 0 && (v & INTERFACE) == 0) print("default ");
+ }
+
+ @Override public void visitSelect(JCFieldAccess tree) {
+ print(tree.selected);
+ print(".");
+ print(tree.name);
+ }
+
+ @Override public void visitIdent(JCIdent tree) {
+ print(tree.name);
+ }
+
+ @Override public void visitApply(JCMethodInvocation tree) {
+ if (tree.typeargs.nonEmpty()) {
+ if (tree.meth instanceof JCFieldAccess) {
+ JCFieldAccess fa = (JCFieldAccess) tree.meth;
+ print(fa.selected);
+ print(".<");
+ print(tree.typeargs, ", ");
+ print(">");
+ print(fa.name);
+ } else {
+ print("<");
+ print(tree.typeargs, ", ");
+ print(">");
+ print(tree.meth);
+ }
+ } else {
+ print(tree.meth);
+ }
+
+ print("(");
+ print(tree.args, ", ");
+ print(")");
+ }
+
+ @Override public void visitAssert(JCAssert tree) {
+ aPrint("assert ");
+ print(tree.cond);
+ if (tree.detail != null) {
+ print(" : ");
+ print(tree.detail);
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitAssign(JCAssign tree) {
+ print(tree.lhs);
+ print(" = ");
+ print(tree.rhs);
+ }
+
+ @Override public void visitAssignop(JCAssignOp tree) {
+ print(tree.lhs);
+ String opname = operator(treeTag(tree));
+ print(" " + opname + " ");
+ print(tree.rhs);
+ }
+
+ private static final int PREFIX = 14;
+
+ @Override public void visitUnary(JCUnary tree) {
+ String op = operator(treeTag(tree));
+ if (treeTag(tree).getOperatorPrecedenceLevel() == PREFIX) {
+ print(op);
+ print(tree.arg);
+ } else {
+ print(tree.arg);
+ print(op);
+ }
+ }
+
+ @Override public void visitBinary(JCBinary tree) {
+ String op = operator(treeTag(tree));
+ print(tree.lhs);
+ print(" ");
+ print(op);
+ print(" ");
+ print(tree.rhs);
+ }
+
+ @Override public void visitTypeTest(JCInstanceOf tree) {
+ print(tree.expr);
+ print(" instanceof ");
+ print(tree.clazz);
+ }
+
+ @Override public void visitTypeCast(JCTypeCast tree) {
+ print("(");
+ print(tree.clazz);
+ print(") ");
+ print(tree.expr);
+ }
+
+ @Override public void visitBlock(JCBlock tree) {
+ if (tree.pos == Position.NOPOS && tree.stats.isEmpty()) return;
+ if (onNewLine) align();
+ if ((tree.flags & STATIC) != 0) print("static ");
+ println("{");
+ indent++;
+ print(tree.stats, "");
+ consumeComments(endPos(tree));
+ indent--;
+ aPrintln("}", tree);
+ }
+
+ @Override public void visitBreak(JCBreak tree) {
+ aPrint("break");
+ if (tree.label != null) {
+ print(" ");
+ print(tree.label);
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitContinue(JCContinue tree) {
+ aPrint("continue");
+ if (tree.label != null) {
+ print(" ");
+ print(tree.label);
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitConditional(JCConditional tree) {
+ print(tree.cond);
+ print(" ? ");
+ print(tree.truepart);
+ print(" : ");
+ print(tree.falsepart);
+ }
+
+ @Override public void visitParens(JCParens tree) {
+ print("(");
+ print(tree.expr);
+ print(")");
+ }
+
+ @Override public void visitReturn(JCReturn tree) {
+ aPrint("return");
+ if (tree.expr != null) {
+ print(" ");
+ print(tree.expr);
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitThrow(JCThrow tree) {
+ aPrint("throw ");
+ print(tree.expr);
+ println(";", tree);
+ }
+
+ @Override public void visitWhileLoop(JCWhileLoop tree) {
+ aPrint("while ");
+ if (tree.cond instanceof JCParens) {
+ print(tree.cond);
+ } else {
+ print("(");
+ print(tree.cond);
+ print(")");
+ }
+ print(" ");
+ print(tree.body);
+ // make sure to test while (true) ; and while(true){} and while(true) x = 5;
+ }
+
+ @Override public void visitForLoop(JCForLoop tree) {
+ aPrint("for (");
+ if (tree.init.nonEmpty()) {
+ if (tree.init.head instanceof JCVariableDecl) {
+ boolean first = true;
+ int dims = 0;
+ for (JCStatement i : tree.init) {
+ JCVariableDecl vd = (JCVariableDecl) i;
+ if (first) {
+ printVarDefInline(vd);
+ dims = dims(vd.vartype);
+ } else {
+ print(", ");
+ print(vd.name);
+ int dimDiff = dims(vd.vartype) - dims;
+ for (int j = 0; j < dimDiff; j++) print("[]");
+ if (vd.init != null) {
+ print(" = ");
+ print(vd.init);
+ }
+ }
+ first = false;
+ }
+ } else {
+ print(tree.init, ", ");
+ }
+ }
+ print("; ");
+ if (tree.cond != null) print(tree.cond);
+ print("; ");
+ boolean first = true;
+ for (JCExpressionStatement exprStatement : tree.step) {
+ if (!first) print(", ");
+ first = false;
+ print(exprStatement.expr);
+ }
+ print(") ");
+ print(tree.body);
+ }
+
+ @Override public void visitForeachLoop(JCEnhancedForLoop tree) {
+ aPrint("for (");
+ printVarDefInline(tree.var);
+ print(" : ");
+ print(tree.expr);
+ print(") ");
+ print(tree.body);
+ }
+
+ @Override public void visitIf(JCIf tree) {
+ aPrint("if ");
+ if (tree.cond instanceof JCParens) {
+ print(tree.cond);
+ } else {
+ print("(");
+ print(tree.cond);
+ print(")");
+ }
+ print(" ");
+ if (tree.thenpart instanceof JCBlock) {
+ println("{");
+ indent++;
+ print(((JCBlock) tree.thenpart).stats, "");
+ indent--;
+ if (tree.elsepart == null) {
+ aPrintln("}", tree);
+ } else {
+ aPrint("}");
+ }
+ } else {
+ print(tree.thenpart);
+ }
+ if (tree.elsepart != null) {
+ aPrint(" else ");
+ print(tree.elsepart);
+ }
+ }
+
+ @Override public void visitExec(JCExpressionStatement tree) {
+ align();
+ print(tree.expr);
+ println(";", tree);
+ }
+
+ @Override public void visitDoLoop(JCDoWhileLoop tree) {
+ aPrint("do ");
+ if (tree.body instanceof JCBlock) {
+ println("{");
+ indent++;
+ print(((JCBlock) tree.body).stats, "");
+ indent--;
+ aPrint("}");
+
+ } else print(tree.body);
+ print(" while ");
+ if (tree.cond instanceof JCParens) {
+ print(tree.cond);
+ } else {
+ print("(");
+ print(tree.cond);
+ print(")");
+ }
+ println(";", tree);
+ }
+
+ @Override public void visitSynchronized(JCSynchronized tree) {
+ aPrint("synchronized ");
+ if (tree.lock instanceof JCParens) {
+ print(tree.lock);
+ } else {
+ print("(");
+ print(tree.lock);
+ print(")");
+ }
+ print(" ");
+ print(tree.body);
+ }
+
+ @Override public void visitCase(JCCase tree) {
+ if (tree.pat == null) {
+ aPrint("default");
+ } else {
+ aPrint("case ");
+ print(tree.pat);
+ }
+ println(": ");
+ indent++;
+ print(tree.stats, "");
+ indent--;
+ }
+
+ @Override public void visitCatch(JCCatch tree) {
+ print(" catch (");
+ print(tree.param);
+ print(") ");
+ print(tree.body);
+ }
+
+ @Override public void visitSwitch(JCSwitch tree) {
+ aPrint("switch ");
+ if (tree.selector instanceof JCParens) {
+ print(tree.selector);
+ } else {
+ print("(");
+ print(tree.selector);
+ print(")");
+ }
+ println(" {");
+ print(tree.cases, "\n");
+ aPrintln("}", tree);
+ }
+
+ @Override public void visitTry(JCTry tree) {
+ aPrint("try ");
+ List<?> resources = readObject(tree, "resources", List.nil());
+ int len = resources.length();
+ switch (len) {
+ case 0:
+ break;
+ case 1:
+ print("(");
+ JCVariableDecl decl = (JCVariableDecl) resources.get(0);
+ flagMod = -1L & ~FINAL;
+ printVarDefInline(decl);
+ print(") ");
+ break;
+ default:
+ println("(");
+ indent++;
+ int c = 0;
+ for (Object i : resources) {
+ align();
+ flagMod = -1L & ~FINAL;
+ printVarDefInline((JCVariableDecl) i);
+ if (++c == len) {
+ print(") ");
+ } else {
+ println(";", (JCTree) i);
+ }
+ }
+ indent--;
+ }
+ println("{");
+ indent++;
+ for (JCStatement stat : tree.body.stats) print(stat);
+ indent--;
+ aPrint("}");
+ for (JCCatch catchBlock : tree.catchers) {
+ printCatch(catchBlock);
+ }
+ if (tree.finalizer != null) {
+ println(" finally {");
+ indent++;
+ for (JCStatement stat : tree.finalizer.stats) print(stat);
+ indent--;
+ aPrint("}");
+ }
+ println(tree);
+ }
+
+ private void printCatch(JCCatch catchBlock) {
+ print(" catch (");
+ printVarDefInline(catchBlock.param); // ExprType1 | ExprType2 handled via JCTypeUnion.
+ println(") {");
+ indent++;
+ for (JCStatement stat : catchBlock.body.stats) print(stat);
+ indent--;
+ aPrint("}");
+ }
+
+ public void visitErroneous(JCErroneous tree) {
+ print("(ERROR)");
+ }
+
+ private static String operator(TreeTag tag) {
+ String op = OPERATORS.get(tag);
+ if (op == null) return "(?op?)";
+ return op;
+ }
+
+ private static String quoteChars(String s) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < s.length(); i++) sb.append(quoteChar(s.charAt(i)));
+ return sb.toString();
+ }
+
+ private static String quoteChar(char ch) {
+ switch (ch) {
+ case '\b': return "\\b";
+ case '\f': return "\\f";
+ case '\n': return "\\n";
+ case '\r': return "\\r";
+ case '\t': return "\\t";
+ case '\'': return "\\'";
+ case '\"': return "\\\"";
+ case '\\': return "\\\\";
+ default:
+ if (ch < 32) return String.format("\\%03o", (int) ch);
+ return String.valueOf(ch);
+ }
+ }
+
+ private static final Method getExtendsClause, getEndPosition, storeEnd;
+
+ static {
+ getExtendsClause = getMethod(JCClassDecl.class, "getExtendsClause", new Class<?>[0]);
+ getExtendsClause.setAccessible(true);
+
+ if (getJavaCompilerVersion() < 8) {
+ getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class);
+ storeEnd = getMethod(java.util.Map.class, "put", Object.class, Object.class);
+ } else {
+ getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", "com.sun.tools.javac.tree.EndPosTable");
+ Method storeEndMethodTemp;
+ Class<?> endPosTable;
+ try {
+ endPosTable = Class.forName("com.sun.tools.javac.tree.EndPosTable");
+ } catch (ClassNotFoundException ex) {
+ throw sneakyThrow(ex);
+ }
+ try {
+ storeEndMethodTemp = endPosTable.getMethod("storeEnd", JCTree.class, int.class);
+ } catch (NoSuchMethodException e) {
+ try {
+ endPosTable = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable");
+ storeEndMethodTemp = endPosTable.getDeclaredMethod("storeEnd", JCTree.class, int.class);
+ } catch (NoSuchMethodException ex) {
+ throw sneakyThrow(ex);
+ } catch (ClassNotFoundException ex) {
+ throw sneakyThrow(ex);
+ }
+ }
+ storeEnd = storeEndMethodTemp;
+ }
+ getEndPosition.setAccessible(true);
+ storeEnd.setAccessible(true);
+ }
+
+ private static Method getMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
+ try {
+ return clazz.getMethod(name, paramTypes);
+ } catch (NoSuchMethodException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ private static Method getMethod(Class<?> clazz, String name, String... paramTypes) {
+ try {
+ Class<?>[] c = new Class[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]);
+ return clazz.getMethod(name, c);
+ } catch (NoSuchMethodException e) {
+ throw sneakyThrow(e);
+ } catch (ClassNotFoundException e) {
+ throw sneakyThrow(e);
+ }
+ }
+
+ public static JCTree getExtendsClause(JCClassDecl decl) {
+ try {
+ return (JCTree) getExtendsClause.invoke(decl);
+ } catch (IllegalAccessException e) {
+ throw sneakyThrow(e);
+ } catch (InvocationTargetException e) {
+ throw sneakyThrow(e.getCause());
+ }
+ }
+
+ static RuntimeException sneakyThrow(Throwable t) {
+ if (t == null) throw new NullPointerException("t");
+ PrettyPrinter.<RuntimeException>sneakyThrow0(t);
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
+ throw (T)t;
+ }
+
+ private static final Map<Class<?>, Map<String, Field>> reflectionCache = new HashMap<Class<?>, Map<String, Field>>();
+
+ @SuppressWarnings("unchecked")
+ private <T> T readObject(JCTree tree, String fieldName, T defaultValue) {
+ Class<?> tClass = tree.getClass();
+ Map<String, Field> c = reflectionCache.get(tClass);
+ if (c == null) reflectionCache.put(tClass, c = new HashMap<String, Field>());
+ Field f = c.get(fieldName);
+ if (f == null) {
+ try {
+ f = tClass.getDeclaredField(fieldName);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ f.setAccessible(true);
+ c.put(fieldName, f);
+ }
+
+ try {
+ return (T) f.get(tree);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ @Override public void visitTypeBoundKind(TypeBoundKind tree) {
+ print(String.valueOf(tree.kind));
+ }
+
+ @Override public void visitTree(JCTree tree) {
+ String simpleName = tree.getClass().getSimpleName();
+ if ("JCTypeUnion".equals(simpleName)) {
+ List<JCExpression> types = readObject(tree, "alternatives", List.<JCExpression>nil());
+ print(types, " | ");
+ return;
+ } else if ("JCTypeIntersection".equals(simpleName)) {
+ print(readObject(tree, "bounds", List.<JCExpression>nil()), " & ");
+ return;
+ } else if ("JCMemberReference".equals(simpleName)) {
+ printMemberReference0(tree);
+ return;
+ } else if ("JCLambda".equals(simpleName)) {
+ printLambda0(tree);
+ return;
+ } else if ("JCAnnotatedType".equals(simpleName)) {
+ printAnnotatedType0(tree);
+ return;
+ }
+
+ throw new AssertionError("Unhandled tree type: " + tree.getClass() + ": " + tree);
+ }
+
+ private void printMemberReference0(JCTree tree) {
+ print(readObject(tree, "expr", (JCExpression) null));
+ print("::");
+ List<JCExpression> typeArgs = readObject(tree, "typeargs", List.<JCExpression>nil());
+ if (typeArgs != null && !typeArgs.isEmpty()) {
+ print("<");
+ print(typeArgs, ", ");
+ print(">");
+ }
+ print(readObject(tree, "mode", new Object()).toString().equals("INVOKE") ? readObject(tree, "name", (Name) null) : "new");
+ }
+
+ private void printLambda0(JCTree tree) {
+ List<JCVariableDecl> params = readObject(tree, "params", List.<JCVariableDecl>nil());
+ boolean explicit = true;
+ int paramLength = params.size();
+ if (paramLength != 1) print("(");
+ try {
+ explicit = readObject(tree, "paramKind", new Object()).toString().equals("EXPLICIT");
+ } catch (Exception e) {}
+ if (explicit) {
+ boolean first = true;
+ for (JCVariableDecl vd : params) {
+ if (!first) print(", ");
+ first = false;
+ printVarDefInline(vd);
+ }
+ } else {
+ String sep = "";
+ for (JCVariableDecl param : params) {
+ print(sep);
+ print(param.name);
+ sep = ", ";
+ }
+ }
+ if (paramLength != 1) print(")");
+ print(" -> ");
+ JCTree body = readObject(tree, "body", (JCTree) null);
+ if (body instanceof JCBlock) {
+ println("{");
+ indent++;
+ print(((JCBlock) body).stats, "");
+ indent--;
+ aPrint("}");
+ } else {
+ print(body);
+ }
+ }
+
+ private void printAnnotatedType0(JCTree tree) {
+ JCTree underlyingType = readObject(tree, "underlyingType", (JCTree) null);
+ if (underlyingType instanceof JCFieldAccess) {
+ print(((JCFieldAccess) underlyingType).selected);
+ print(".");
+ print(readObject(tree, "annotations", List.<JCExpression>nil()), " ");
+ print(" ");
+ print(((JCFieldAccess) underlyingType).name);
+ } else {
+ print(readObject(tree, "annotations", List.<JCExpression>nil()), " ");
+ print(" ");
+ print(underlyingType);
+ }
+ }
+}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java
index 8616e9bc..aa01c13d 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcher.java
@@ -44,7 +44,7 @@ public class EclipseLoaderPatcher {
.transplant()
.request(StackRequest.THIS, StackRequest.PARAM1, StackRequest.PARAM2).build());
- sm.addScript(ScriptBuilder.addField().setPublic()
+ sm.addScript(ScriptBuilder.addField().setPublic().setVolatile()
.fieldType("Ljava/lang/ClassLoader;")
.fieldName("lombok$shadowLoader")
.targetClass("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader")
@@ -52,6 +52,14 @@ public class EclipseLoaderPatcher {
.targetClass("org.eclipse.osgi.internal.loader.ModuleClassLoader")
.build());
+ sm.addScript(ScriptBuilder.addField().setPublic().setVolatile().setStatic()
+ .fieldType("Ljava/lang/Class;")
+ .fieldName("lombok$shadowLoaderClass")
+ .targetClass("org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader")
+ .targetClass("org.eclipse.osgi.framework.adapter.core.AbstractClassLoader")
+ .targetClass("org.eclipse.osgi.internal.loader.ModuleClassLoader")
+ .build());
+
sm.addScript(ScriptBuilder.addField().setPublic().setStatic().setFinal()
.fieldType("Ljava/lang/String;")
.fieldName("lombok$location")
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java
index f50e6987..ea72f56a 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipseLoaderPatcherTransplants.java
@@ -26,6 +26,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.Arrays;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
@@ -55,44 +56,54 @@ public class EclipseLoaderPatcherTransplants {
Field shadowLoaderField = original.getClass().getField("lombok$shadowLoader");
ClassLoader shadowLoader = (ClassLoader) shadowLoaderField.get(original);
if (shadowLoader == null) {
- String jarLoc = (String) original.getClass().getField("lombok$location").get(null);
- JarFile jf = new JarFile(jarLoc);
- InputStream in = null;
- try {
- ZipEntry entry = jf.getEntry("lombok/launch/ShadowClassLoader.class");
- in = jf.getInputStream(entry);
- byte[] bytes = new byte[65536];
- int len = 0;
- while (true) {
- int r = in.read(bytes, len, bytes.length - len);
- if (r == -1) break;
- len += r;
- if (len == bytes.length) throw new IllegalStateException("lombok.launch.ShadowClassLoader too large.");
+ synchronized ("lombok$shadowLoader$globalLock".intern()) {
+ shadowLoader = (ClassLoader) shadowLoaderField.get(original);
+ if (shadowLoader == null) {
+ Class shadowClassLoaderClass = (Class) original.getClass().getField("lombok$shadowLoaderClass").get(null);
+ Class classLoaderClass = Class.forName("java.lang.ClassLoader");
+ String jarLoc = (String) original.getClass().getField("lombok$location").get(null);
+ if (shadowClassLoaderClass == null) {
+ JarFile jf = new JarFile(jarLoc);
+ InputStream in = null;
+ try {
+ ZipEntry entry = jf.getEntry("lombok/launch/ShadowClassLoader.class");
+ in = jf.getInputStream(entry);
+ byte[] bytes = new byte[65536];
+ int len = 0;
+ while (true) {
+ int r = in.read(bytes, len, bytes.length - len);
+ if (r == -1) break;
+ len += r;
+ if (len == bytes.length) throw new IllegalStateException("lombok.launch.ShadowClassLoader too large.");
+ }
+ in.close();
+ {
+ Class[] paramTypes = new Class[4];
+ paramTypes[0] = "".getClass();
+ paramTypes[1] = new byte[0].getClass();
+ paramTypes[2] = Integer.TYPE;
+ paramTypes[3] = paramTypes[2];
+ Method defineClassMethod = classLoaderClass.getDeclaredMethod("defineClass", paramTypes);
+ defineClassMethod.setAccessible(true);
+ shadowClassLoaderClass = (Class) defineClassMethod.invoke(original, new Object[] {"lombok.launch.ShadowClassLoader", bytes, new Integer(0), new Integer(len)});
+ original.getClass().getField("lombok$shadowLoaderClass").set(null, shadowClassLoaderClass);
+ }
+ } finally {
+ if (in != null) in.close();
+ jf.close();
+ }
+ }
+ Class[] paramTypes = new Class[5];
+ paramTypes[0] = classLoaderClass;
+ paramTypes[1] = "".getClass();
+ paramTypes[2] = paramTypes[1];
+ paramTypes[3] = Class.forName("java.util.List");
+ paramTypes[4] = paramTypes[3];
+ Constructor constructor = shadowClassLoaderClass.getDeclaredConstructor(paramTypes);
+ constructor.setAccessible(true);
+ shadowLoader = (ClassLoader) constructor.newInstance(new Object[] {original, "lombok", jarLoc, Arrays.asList(new Object[] {"lombok."}), Arrays.asList(new Object[] {"lombok.patcher.Symbols"})});
+ shadowLoaderField.set(original, shadowLoader);
}
- in.close();
- Class classLoaderClass = Class.forName("java.lang.ClassLoader");
- Class shadowClassLoaderClass; {
- Class[] paramTypes = new Class[4];
- paramTypes[0] = "".getClass();
- paramTypes[1] = new byte[0].getClass();
- paramTypes[2] = Integer.TYPE;
- paramTypes[3] = paramTypes[2];
- Method defineClassMethod = classLoaderClass.getDeclaredMethod("defineClass", paramTypes);
- defineClassMethod.setAccessible(true);
- shadowClassLoaderClass = (Class) defineClassMethod.invoke(original, new Object[] {"lombok.launch.ShadowClassLoader", bytes, new Integer(0), new Integer(len)});
- }
- Class[] paramTypes = new Class[4];
- paramTypes[0] = classLoaderClass;
- paramTypes[1] = "".getClass();
- paramTypes[2] = paramTypes[1];
- paramTypes[3] = new String[0].getClass();
- Constructor constructor = shadowClassLoaderClass.getDeclaredConstructor(paramTypes);
- constructor.setAccessible(true);
- shadowLoader = (ClassLoader) constructor.newInstance(new Object[] {original, "lombok", jarLoc, new String[] {"lombok."}});
- shadowLoaderField.set(original, shadowLoader);
- } finally {
- if (in != null) in.close();
- jf.close();
}
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
index b8e3a955..7c538b6f 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
@@ -34,6 +34,7 @@ import lombok.patcher.MethodTarget;
import lombok.patcher.ScriptManager;
import lombok.patcher.StackRequest;
import lombok.patcher.TargetMatcher;
+import lombok.patcher.TransplantMapper;
import lombok.patcher.scripts.ScriptBuilder;
/**
@@ -73,6 +74,14 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable {
private static void registerPatchScripts(Instrumentation instrumentation, boolean reloadExistingClasses, boolean ecjOnly, Class<?> launchingContext) {
ScriptManager sm = new ScriptManager();
sm.registerTransformer(instrumentation);
+ final boolean forceBaseResourceNames = !"".equals(System.getProperty("shadow.override.lombok", ""));
+ sm.setTransplantMapper(new TransplantMapper() {
+ public String mapResourceName(int classFileFormatVersion, String resourceName) {
+ if (classFileFormatVersion < 50 || forceBaseResourceNames) return resourceName;
+ return "Class50/" + resourceName;
+ }
+ });
+
if (!ecjOnly) {
EclipseLoaderPatcher.patchEquinoxLoaders(sm, launchingContext);
patchCatchReparse(sm);
@@ -305,6 +314,13 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable {
.callToWrap(new Hook("org.eclipse.jdt.internal.corext.util.CodeFormatterUtil", "reformat", "org.eclipse.text.edits.TextEdit",
"int", "java.lang.String", "int", "int", "int", "java.lang.String", "java.util.Map"))
.symbol("lombok.disable").build());
+
+ sm.addScript(ScriptBuilder.setSymbolDuringMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.internal.corext.fix.CodeFormatFix", "createCleanUp", "org.eclipse.jdt.ui.cleanup.ICleanUpFix",
+ "org.eclipse.jdt.core.ICompilationUnit", "org.eclipse.jface.text.IRegion[]", "boolean", "boolean", "boolean", "boolean"))
+ .callToWrap(new Hook("org.eclipse.jdt.internal.corext.util.CodeFormatterUtil", "reformat", "org.eclipse.text.edits.TextEdit",
+ "int", "java.lang.String", "int", "java.lang.String", "java.util.Map"))
+ .symbol("lombok.disable").build());
}
private static void patchRefactorScripts(ScriptManager sm) {
@@ -353,11 +369,35 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable {
}
private static void patchRetrieveRightBraceOrSemiColonPosition(ScriptManager sm) {
- sm.addScript(ScriptBuilder.wrapReturnValue()
- .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition"))
- .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace"))
- .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "int"))
- .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
+ sm.addScript(ScriptBuilder.wrapMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
+ .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
+ .requestExtra(StackRequest.PARAM2)
+ .transplant()
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "boolean", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
+ .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration"))
+ .requestExtra(StackRequest.PARAM2)
+ .transplant()
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapMethodCall()
+ .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "convert", "org.eclipse.jdt.core.dom.ASTNode", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration"))
+ .methodToWrap(new Hook("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace", "int", "int", "int"))
+ .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "org.eclipse.jdt.internal.compiler.ast.FieldDeclaration"))
+ .requestExtra(StackRequest.PARAM1)
+ .transplant()
+ .build());
+
+// sm.addScript(ScriptBuilder.wrapReturnValue()
+// .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBraceOrSemiColonPosition"))
+// .target(new MethodTarget("org.eclipse.jdt.core.dom.ASTConverter", "retrieveRightBrace"))
+// .wrapMethod(new Hook("lombok.launch.PatchFixesHider$PatchFixes", "fixRetrieveRightBraceOrSemiColonPosition", "int", "int", "int"))
+// .transplant().request(StackRequest.RETURN_VALUE, StackRequest.PARAM2).build());
}
private static void patchSetGeneratedFlag(ScriptManager sm) {
@@ -655,6 +695,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable {
final String PATCH_EXTENSIONMETHOD_COMPLETIONPROPOSAL_PORTAL = "lombok.eclipse.agent.PatchExtensionMethodCompletionProposalPortal";
final String MESSAGE_SEND_SIG = "org.eclipse.jdt.internal.compiler.ast.MessageSend";
final String TYPE_BINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
+ final String SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.Scope";
final String BLOCK_SCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
final String TYPE_BINDINGS_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding[]";
final String PROBLEM_REPORTER_SIG = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter";
@@ -682,6 +723,13 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable {
.replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG))
.build());
+ // Since eclipse mars; they added a param.
+ sm.addScript(replaceMethodCall()
+ .target(new MethodTarget(MESSAGE_SEND_SIG, "resolveType", TYPE_BINDING_SIG, BLOCK_SCOPE_SIG))
+ .methodToReplace(new Hook(PROBLEM_REPORTER_SIG, "invalidMethod", "void", MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG))
+ .replacementMethod(new Hook(PATCH_EXTENSIONMETHOD, "invalidMethod", "void", PROBLEM_REPORTER_SIG, MESSAGE_SEND_SIG, METHOD_BINDING_SIG, SCOPE_SIG))
+ .build());
+
if (!ecj) {
sm.addScript(wrapReturnValue()
.target(new MethodTarget(COMPLETION_PROPOSAL_COLLECTOR_SIG, "getJavaCompletionProposals", I_JAVA_COMPLETION_PROPOSAL_SIG))
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
index ca0933fb..5d586dff 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethod.java
@@ -24,6 +24,8 @@ package lombok.eclipse.agent;
import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation;
import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -56,6 +58,7 @@ import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
@@ -89,16 +92,47 @@ public class PatchExtensionMethod {
private final ProblemReporter problemReporter;
private final WeakReference<MessageSend> messageSendRef;
private final MethodBinding method;
+ private final Scope scope;
- PostponedInvalidMethodError(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
+ private static final Method shortMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class);
+ private static final Method longMethod = getMethod("invalidMethod", MessageSend.class, MethodBinding.class, Scope.class);
+
+ private static Method getMethod(String name, Class<?>... types) {
+ try {
+ Method m = ProblemReporter.class.getMethod(name, types);
+ m.setAccessible(true);
+ return m;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ PostponedInvalidMethodError(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) {
this.problemReporter = problemReporter;
this.messageSendRef = new WeakReference<MessageSend>(messageSend);
this.method = method;
+ this.scope = scope;
+ }
+
+ static void invoke(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) {
+ if (messageSend != null) {
+ try {
+ if (shortMethod != null) shortMethod.invoke(problemReporter, messageSend, method);
+ else if (longMethod != null) longMethod.invoke(problemReporter, messageSend, method, scope);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getCause();
+ if (t instanceof Error) throw (Error) t;
+ if (t instanceof RuntimeException) throw (RuntimeException) t;
+ throw new RuntimeException(t);
+ }
+ }
}
public void fire() {
MessageSend messageSend = messageSendRef.get();
- if (messageSend != null) problemReporter.invalidMethod(messageSend, method);
+ invoke(problemReporter, messageSend, method, scope);
}
}
@@ -185,7 +219,11 @@ public class PatchExtensionMethod {
}
public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
- MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method));
+ MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method, null));
+ }
+
+ public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) {
+ MessageSend_postponedErrors.set(messageSend, new PostponedInvalidMethodError(problemReporter, messageSend, method, scope));
}
public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) {
@@ -233,7 +271,7 @@ public class PatchExtensionMethod {
if (fixedBinding instanceof ProblemMethodBinding) {
methodCall.arguments = originalArgs;
if (fixedBinding.declaringClass != null) {
- scope.problemReporter().invalidMethod(methodCall, fixedBinding);
+ PostponedInvalidMethodError.invoke(scope.problemReporter(), methodCall, fixedBinding, scope);
}
} else {
for (int i = 0, iend = arguments.size(); i < iend; i++) {
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
index 97ca5a7e..c11a49cd 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposal.java
@@ -60,8 +60,6 @@ import org.eclipse.jdt.ui.text.java.CompletionProposalCollector;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
public class PatchExtensionMethodCompletionProposal {
-
-
public static IJavaCompletionProposal[] getJavaCompletionProposals(IJavaCompletionProposal[] javaCompletionProposals,
CompletionProposalCollector completionProposalCollector) {
@@ -178,7 +176,7 @@ public class PatchExtensionMethodCompletionProposal {
return !proposals.isEmpty() && Reflection.isComplete();
}
- private static int getReplacementOffset(IJavaCompletionProposal proposal) {
+ private static int getReplacementOffset(Object proposal) {
try {
return Reflection.replacementOffsetField.getInt(proposal);
} catch (Exception ignore) {
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java
index 6dca1901..19e1952e 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchExtensionMethodCompletionProposalPortal.java
@@ -33,7 +33,6 @@ public class PatchExtensionMethodCompletionProposalPortal {
private static final String COMPLETION_PROPOSAL_COLLECTOR = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector";
private static final String I_JAVA_COMPLETION_PROPOSAL_ARRAY = "[Lorg.eclipse.jdt.ui.text.java.IJavaCompletionProposal;";
-
public static IJavaCompletionProposal[] getJavaCompletionProposals(Object[] javaCompletionProposals, Object completionProposalCollector) {
try {
return (IJavaCompletionProposal[]) ReflectionForUi.getJavaCompletionProposals.invoke(null, javaCompletionProposals, completionProposalCollector);
@@ -52,7 +51,7 @@ public class PatchExtensionMethodCompletionProposalPortal {
}
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
//do anything useful here.
- return (IJavaCompletionProposal[])javaCompletionProposals;
+ return (IJavaCompletionProposal[]) javaCompletionProposals;
}
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
index 6685b6bb..52f63765 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixesShadowLoaded.java
@@ -32,7 +32,7 @@ import lombok.core.Version;
public class PatchFixesShadowLoaded {
public static String addLombokNotesToEclipseAboutDialog(String origReturnValue, String key) {
if ("aboutText".equals(key)) {
- return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. http://projectlombok.org/";
+ return origReturnValue + "\n\nLombok " + Version.getFullVersion() + " is installed. https://projectlombok.org/";
}
return origReturnValue;
}
diff --git a/src/eclipseAgent/lombok/launch/PatchFixesHider.java b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
index 2472ca3c..fae06900 100644
--- a/src/eclipseAgent/lombok/launch/PatchFixesHider.java
+++ b/src/eclipseAgent/lombok/launch/PatchFixesHider.java
@@ -41,14 +41,17 @@ import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
@@ -258,13 +261,14 @@ final class PatchFixesHider {
public static final class ExtensionMethod {
private static final Method RESOLVE_TYPE;
private static final Method ERROR_NO_METHOD_FOR;
- private static final Method INVALID_METHOD;
+ private static final Method INVALID_METHOD, INVALID_METHOD2;
static {
Class<?> shadowed = Util.shadowLoadClass("lombok.eclipse.agent.PatchExtensionMethod");
RESOLVE_TYPE = Util.findMethod(shadowed, "resolveType", TypeBinding.class, MessageSend.class, BlockScope.class);
ERROR_NO_METHOD_FOR = Util.findMethod(shadowed, "errorNoMethodFor", ProblemReporter.class, MessageSend.class, TypeBinding.class, TypeBinding[].class);
INVALID_METHOD = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class);
+ INVALID_METHOD2 = Util.findMethod(shadowed, "invalidMethod", ProblemReporter.class, MessageSend.class, MethodBinding.class, Scope.class);
}
public static TypeBinding resolveType(TypeBinding resolvedType, MessageSend methodCall, BlockScope scope) {
@@ -278,6 +282,10 @@ final class PatchFixesHider {
public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method) {
Util.invokeMethod(INVALID_METHOD, problemReporter, messageSend, method);
}
+
+ public static void invalidMethod(ProblemReporter problemReporter, MessageSend messageSend, MethodBinding method, Scope scope) {
+ Util.invokeMethod(INVALID_METHOD2, problemReporter, messageSend, method, scope);
+ }
}
/**
@@ -469,7 +477,24 @@ final class PatchFixesHider {
}
public static int fixRetrieveRightBraceOrSemiColonPosition(int original, int end) {
- return original == -1 ? end : original; // Need to fix: see issue 325.
+// if (original == -1) {
+// Thread.dumpStack();
+// }
+ return original == -1 ? end : original;
+ }
+
+ public static int fixRetrieveRightBraceOrSemiColonPosition(int retVal, AbstractMethodDeclaration amd) {
+ if (retVal != -1 || amd == null) return retVal;
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(amd) != null;
+ if (isGenerated) return amd.declarationSourceEnd;
+ return -1;
+ }
+
+ public static int fixRetrieveRightBraceOrSemiColonPosition(int retVal, FieldDeclaration fd) {
+ if (retVal != -1 || fd == null) return retVal;
+ boolean isGenerated = EclipseAugments.ASTNode_generatedBy.get(fd) != null;
+ if (isGenerated) return fd.declarationSourceEnd;
+ return -1;
}
public static final int ALREADY_PROCESSED_FLAG = 0x800000; //Bit 24
diff --git a/src/installer/lombok/installer/Installer.java b/src/installer/lombok/installer/Installer.java
index b9faeebd..7ae01d7a 100644
--- a/src/installer/lombok/installer/Installer.java
+++ b/src/installer/lombok/installer/Installer.java
@@ -56,7 +56,7 @@ import com.zwitserloot.cmdreader.Shorthand;
* and looks in some common places on Mac OS X, Linux and Windows.
*/
public class Installer {
- static final URI ABOUT_LOMBOK_URL = URI.create("http://projectlombok.org");
+ static final URI ABOUT_LOMBOK_URL = URI.create("https://projectlombok.org");
static final List<IdeLocationProvider> locationProviders;
static {
diff --git a/src/installer/lombok/installer/eclipse/EclipseLocation.java b/src/installer/lombok/installer/eclipse/EclipseLocation.java
index 0fe60c05..6c63c48d 100644
--- a/src/installer/lombok/installer/eclipse/EclipseLocation.java
+++ b/src/installer/lombok/installer/eclipse/EclipseLocation.java
@@ -49,6 +49,7 @@ import lombok.installer.UninstallException;
public class EclipseLocation extends IdeLocation {
private final String name;
private final File eclipseIniPath;
+ private final String pathToLombokJarPrefix;
private volatile boolean hasLombok;
private static final String OS_NEWLINE = IdeFinder.getOS().getLineEnding();
@@ -64,6 +65,15 @@ public class EclipseLocation extends IdeLocation {
EclipseLocation(String nameOfLocation, File pathToEclipseIni) throws CorruptedIdeLocationException {
this.name = nameOfLocation;
this.eclipseIniPath = pathToEclipseIni;
+ File p1 = pathToEclipseIni.getParentFile();
+ File p2 = p1 == null ? null : p1.getParentFile();
+ File p3 = p2 == null ? null : p2.getParentFile();
+ if (p1 != null && p1.getName().equals("Eclipse") && p2 != null && p2.getName().equals("Contents") && p3 != null && p3.getName().endsWith(".app")) {
+ this.pathToLombokJarPrefix = "../Eclipse/";
+ } else {
+ this.pathToLombokJarPrefix = "";
+ }
+
try {
this.hasLombok = checkForLombok(eclipseIniPath);
} catch (IOException e) {
@@ -333,10 +343,15 @@ public class EclipseLocation extends IdeLocation {
fis.close();
}
- String fullPathToLombok = fullPathRequired ? (lombokJar.getParentFile().getCanonicalPath() + File.separator) : "";
+ String pathPrefix;
+ if (fullPathRequired) {
+ pathPrefix = lombokJar.getParentFile().getCanonicalPath() + File.separator;
+ } else {
+ pathPrefix = pathToLombokJarPrefix;
+ }
newContents.append(String.format(
- "-javaagent:%s", escapePath(fullPathToLombok + "lombok.jar"))).append(OS_NEWLINE);
+ "-javaagent:%s", escapePath(pathPrefix + "lombok.jar"))).append(OS_NEWLINE);
FileOutputStream fos = new FileOutputStream(eclipseIniPath);
try {
diff --git a/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java b/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java
index 914de588..29716a1f 100644
--- a/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java
+++ b/src/installer/lombok/installer/eclipse/EclipseLocationProvider.java
@@ -104,11 +104,16 @@ public class EclipseLocationProvider implements IdeLocationProvider {
if (ini.isFile()) return makeLocation(canonical(exePath), ini);
}
- /* Try looking for Eclipse/app/Contents/MacOS/eclipse.ini as sibling to executable; this works on Mac OS X. */ {
+ /* Try looking for Eclipse.app/Contents/MacOS/eclipse.ini as sibling to executable; this works on Mac OS X. */ {
File ini = new File(exePath.getParentFile(), getMacAppName() + "/Contents/MacOS/" + getIniName());
if (ini.isFile()) return makeLocation(canonical(exePath), ini);
}
+ /* Starting with Eclipse Mars (with the oomph installer), the structure has changed, and it's now at Eclipse.app/Contents/Eclipse/eclipse.ini*/ {
+ File ini = new File(exePath.getParentFile(), getMacAppName() + "/Contents/Eclipse/" + getIniName());
+ if (ini.isFile()) return makeLocation(canonical(exePath), ini);
+ }
+
/* If executable is a soft link, follow it and retry. */ {
if (loopCounter < 50) {
try {
diff --git a/src/launch/lombok/launch/Main.java b/src/launch/lombok/launch/Main.java
index 63d97d48..b81b6268 100644
--- a/src/launch/lombok/launch/Main.java
+++ b/src/launch/lombok/launch/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Project Lombok Authors.
+ * Copyright (C) 2014-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,11 @@
package lombok.launch;
import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
class Main {
static ClassLoader createShadowClassLoader() {
- return new ShadowClassLoader(Main.class.getClassLoader(), "lombok");
+ return new ShadowClassLoader(Main.class.getClassLoader(), "lombok", null, Arrays.<String>asList(), Arrays.asList("lombok.patcher.Symbols"));
}
public static void main(String[] args) throws Throwable {
diff --git a/src/launch/lombok/launch/ShadowClassLoader.java b/src/launch/lombok/launch/ShadowClassLoader.java
index f8f969ef..37c479ee 100644
--- a/src/launch/lombok/launch/ShadowClassLoader.java
+++ b/src/launch/lombok/launch/ShadowClassLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Project Lombok Authors.
+ * Copyright (C) 2014-2015 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -33,10 +33,15 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.Vector;
import java.util.WeakHashMap;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -79,6 +84,8 @@ import java.util.jar.JarFile;
*/
class ShadowClassLoader extends ClassLoader {
private static final String SELF_NAME = "lombok/launch/ShadowClassLoader.class";
+ private static final ConcurrentMap<String, Class<?>> highlanderMap = new ConcurrentHashMap<String, Class<?>>();
+
private final String SELF_BASE;
private final File SELF_BASE_FILE;
private final int SELF_BASE_LENGTH;
@@ -86,21 +93,16 @@ class ShadowClassLoader extends ClassLoader {
private final List<File> override = new ArrayList<File>();
private final String sclSuffix;
private final List<String> parentExclusion = new ArrayList<String>();
-
- /**
- * Calls the {@link ShadowClassLoader(ClassLoader, String, String, String[]) constructor with no exclusions and the source of this class as base.
- */
- ShadowClassLoader(ClassLoader source, String sclSuffix) {
- this(source, sclSuffix, null);
- }
+ private final List<String> highlanders = new ArrayList<String>();
/**
* @param source The 'parent' classloader.
* @param sclSuffix The suffix of the shadowed class files in our own jar. For example, if this is {@code lombok}, then the class files in your jar should be {@code foo/Bar.SCL.lombok} and not {@code foo/Bar.class}.
* @param selfBase The (preferably absolute) path to our own jar. This jar will be searched for class/SCL.sclSuffix files.
* @param parentExclusion For example {@code "lombok."}; upon invocation of loadClass of this loader, the parent loader ({@code source}) will NOT be invoked if the class to be loaded begins with anything in the parent exclusion list. No exclusion is applied for getResource(s).
+ * @param highlanders SCL will put in extra effort to ensure that these classes (in simple class spec, so {@code foo.bar.baz.ClassName}) are only loaded once as a class, even if many different classloaders try to load classes, such as equinox/OSGi.
*/
- ShadowClassLoader(ClassLoader source, String sclSuffix, String selfBase, String... parentExclusion) {
+ ShadowClassLoader(ClassLoader source, String sclSuffix, String selfBase, List<String> parentExclusion, List<String> highlanders) {
super(source);
this.sclSuffix = sclSuffix;
if (parentExclusion != null) for (String pe : parentExclusion) {
@@ -108,6 +110,9 @@ class ShadowClassLoader extends ClassLoader {
if (!pe.endsWith("/")) pe = pe + "/";
this.parentExclusion.add(pe);
}
+ if (highlanders != null) for (String hl : highlanders) {
+ this.highlanders.add(hl);
+ }
if (selfBase != null) {
SELF_BASE = selfBase;
@@ -139,62 +144,100 @@ class ShadowClassLoader extends ClassLoader {
}
}
}
-
- private static final String EMPTY_MARKER = new String("--EMPTY JAR--");
- private Map<String, Object> jarContentsCacheTrackers = new HashMap<String, Object>();
- private static WeakHashMap<Object, String> trackerCache = new WeakHashMap<Object, String>();
- private static WeakHashMap<Object, List<String>> jarContentsCache = new WeakHashMap<Object, List<String>>();
-
+
+ private final Map<String, Object> mapJarPathToTracker = new HashMap<String, Object>();
+ private static final Map<Object, String> mapTrackerToJarPath = new WeakHashMap<Object, String>();
+ private static final Map<Object, Set<String>> mapTrackerToJarContents = new WeakHashMap<Object, Set<String>>();
+
/**
* This cache ensures that any given jar file is only opened once in order to determine the full contents of it.
* We use 'trackers' to make sure that the bulk of the memory taken up by this cache (the list of strings representing the content of a jar file)
* gets garbage collected if all ShadowClassLoaders that ever tried to request a listing of this jar file, are garbage collected.
*/
- private List<String> getOrMakeJarListing(String absolutePathToJar) {
- List<String> list = retrieveFromCache(absolutePathToJar);
- synchronized (list) {
- if (list.isEmpty()) {
- try {
- JarFile jf = new JarFile(absolutePathToJar);
- try {
- Enumeration<JarEntry> entries = jf.entries();
- while (entries.hasMoreElements()) {
- JarEntry jarEntry = entries.nextElement();
- if (!jarEntry.isDirectory()) list.add(jarEntry.getName());
- }
- } finally {
- jf.close();
- }
- } catch (Exception ignore) {}
- if (list.isEmpty()) list.add(EMPTY_MARKER);
+ private Set<String> getOrMakeJarListing(final String absolutePathToJar) {
+ synchronized (mapTrackerToJarPath) {
+ /*
+ * 1) Check our private instance JarPath-to-Tracker Mappings:
+ */
+ Object ourTracker = mapJarPathToTracker.get(absolutePathToJar);
+ if (ourTracker != null) {
+ /*
+ * Yes, we are already tracking this Jar. Just return its contents...
+ */
+ return mapTrackerToJarContents.get(ourTracker);
+ }
+
+ /*
+ * 2) Not tracked by us as yet. Check statically whether others have tracked this JarPath:
+ */
+ for (Entry<Object, String> entry : mapTrackerToJarPath.entrySet()) {
+ if (entry.getValue().equals(absolutePathToJar)) {
+ /*
+ * Yes, 3rd party is tracking this jar. We must track too, then return its contents.
+ */
+ Object otherTracker = entry.getKey();
+ mapJarPathToTracker.put(absolutePathToJar, otherTracker);
+ return mapTrackerToJarContents.get(otherTracker);
+ }
}
+
+ /*
+ * 3) Not tracked by anyone so far. Build, publish, track & return Jar contents...
+ */
+ Object newTracker = new Object();
+ Set<String> jarMembers = getJarMemberSet(absolutePathToJar);
+
+ mapTrackerToJarContents.put(newTracker, jarMembers);
+ mapTrackerToJarPath.put(newTracker, absolutePathToJar);
+ mapJarPathToTracker.put(absolutePathToJar, newTracker);
+
+ return jarMembers;
}
-
- if (list.size() == 1 && list.get(0) == EMPTY_MARKER) return Collections.emptyList();
- return list;
}
- private List<String> retrieveFromCache(String absolutePathToJar) {
- synchronized (trackerCache) {
- Object tracker = jarContentsCacheTrackers.get(absolutePathToJar);
- if (tracker != null) return jarContentsCache.get(tracker);
+ /**
+ * Return a {@link Set} of members in the Jar identified by {@code absolutePathToJar}.
+ *
+ * @param absolutePathToJar Cache key
+ * @return a Set with the Jar member-names
+ */
+ private Set<String> getJarMemberSet(String absolutePathToJar) {
+ /*
+ * Note:
+ * Our implementation returns a HashSet. initialCapacity and loadFactor are carefully tweaked for speed and RAM optimization purposes.
+ *
+ * Benchmark:
+ * The HashSet implementation is about 10% slower to build (only happens once) than the ArrayList.
+ * The HashSet with shiftBits = 1 was about 33 times(!) faster than the ArrayList for retrievals.
+ */
+ try {
+ int shiftBits = 1; // (fast, but big) 0 <= shiftBits <= 5, say (slower & compact)
+ JarFile jar = new JarFile(absolutePathToJar);
- for (Map.Entry<Object, String> entry : trackerCache.entrySet()) {
- if (entry.getValue().equals(absolutePathToJar)) {
- tracker = entry.getKey();
- break;
+ /*
+ * Find the first power of 2 >= JarSize (as calculated in HashSet constructor)
+ */
+ int jarSizePower2 = Integer.highestOneBit(jar.size());
+ if (jarSizePower2 != jar.size()) jarSizePower2 <<= 1;
+ if (jarSizePower2 == 0) jarSizePower2 = 1;
+
+ Set<String> jarMembers = new HashSet<String>(jarSizePower2 >> shiftBits, 1 << shiftBits);
+ try {
+ Enumeration<JarEntry> entries = jar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry = entries.nextElement();
+ if (jarEntry.isDirectory()) continue;
+ jarMembers.add(jarEntry.getName());
}
+ } catch (Exception ignore) {
+ // ignored; if the jar can't be read, treating it as if the jar contains no classes is just what we want.
+ } finally {
+ jar.close();
}
- List<String> result = null;
- if (tracker != null) result = jarContentsCache.get(tracker);
- if (result != null) return result;
-
- tracker = new Object();
- List<String> list = new ArrayList<String>();
- jarContentsCache.put(tracker, list);
- trackerCache.put(tracker, absolutePathToJar);
- jarContentsCacheTrackers.put(absolutePathToJar, tracker);
- return list;
+ return jarMembers;
+ }
+ catch (Exception newJarFileException) {
+ return Collections.emptySet();
}
}
@@ -226,7 +269,7 @@ class ShadowClassLoader extends ClassLoader {
absoluteFile = location.getAbsoluteFile();
}
}
- List<String> jarContents = getOrMakeJarListing(absoluteFile.getAbsolutePath());
+ Set<String> jarContents = getOrMakeJarListing(absoluteFile.getAbsolutePath());
String absoluteUri = absoluteFile.toURI().toString();
@@ -234,13 +277,17 @@ class ShadowClassLoader extends ClassLoader {
if (jarContents.contains(altName)) {
return new URI("jar:" + absoluteUri + "!/" + altName).toURL();
}
- } catch (Exception e) {}
+ } catch (Exception ignore) {
+ // intentional fallthrough
+ }
try {
if (jarContents.contains(name)) {
return new URI("jar:" + absoluteUri + "!/" + name).toURL();
}
- } catch(Exception e) {}
+ } catch(Exception ignore) {
+ // intentional fallthrough
+ }
return null;
}
@@ -360,7 +407,12 @@ class ShadowClassLoader extends ClassLoader {
if (alreadyLoaded != null) return alreadyLoaded;
}
- String fileNameOfClass = name.replace(".", "/") + ".class";
+ if (highlanders.contains(name)) {
+ Class<?> c = highlanderMap.get(name);
+ if (c != null) return c;
+ }
+
+ String fileNameOfClass = name.replace(".", "/") + ".class";
URL res = getResource_(fileNameOfClass, true);
if (res == null) {
if (!exclusionListMatch(fileNameOfClass)) return super.loadClass(name, resolve);
@@ -391,7 +443,22 @@ class ShadowClassLoader extends ClassLoader {
throw new ClassNotFoundException("I/O exception reading class " + name, e);
}
- Class<?> c = defineClass(name, b, 0, p);
+ Class<?> c;
+ try {
+ c = defineClass(name, b, 0, p);
+ } catch (LinkageError e) {
+ if (highlanders.contains(name)) {
+ Class<?> alreadyDefined = highlanderMap.get(name);
+ if (alreadyDefined != null) return alreadyDefined;
+ }
+ throw e;
+ }
+
+ if (highlanders.contains(name)) {
+ Class<?> alreadyDefined = highlanderMap.putIfAbsent(name, c);
+ if (alreadyDefined != null) c = alreadyDefined;
+ }
+
if (resolve) resolveClass(c);
return c;
}
diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java
index c2a863d5..18b22256 100644
--- a/src/utils/lombok/eclipse/Eclipse.java
+++ b/src/utils/lombok/eclipse/Eclipse.java
@@ -98,15 +98,20 @@ public class Eclipse {
* string containing the same fully qualified name with dots in the string.
*/
public static boolean nameEquals(char[][] typeName, String string) {
- StringBuilder sb = new StringBuilder();
- boolean first = true;
- for (char[] elem : typeName) {
- if (first) first = false;
- else sb.append('.');
- sb.append(elem);
+ int pos = 0, len = string.length();
+ for (int i = 0; i < typeName.length; i++) {
+ char[] t = typeName[i];
+ if (i > 0) {
+ if (pos == len) return false;
+ if (string.charAt(pos++) != '.') return false;
+ }
+ for (int j = 0; j < t.length; j++) {
+ if (pos == len) return false;
+ if (string.charAt(pos++) != t[j]) return false;
+ }
}
- return string.contentEquals(sb);
+ return true;
}
public static boolean hasClinit(TypeDeclaration parent) {