diff options
Diffstat (limited to 'src')
131 files changed, 3218 insertions, 1028 deletions
diff --git a/src/core/lombok/AllArgsConstructor.java b/src/core/lombok/AllArgsConstructor.java index 75f87211..c059c65d 100644 --- a/src/core/lombok/AllArgsConstructor.java +++ b/src/core/lombok/AllArgsConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,7 @@ import java.lang.annotation.Target; * Generates an all-args constructor. * An all-args constructor requires one argument for every field in the class. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor.html">the project lombok features page for @Constructor</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for @Constructor</a>. * <p> * Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details. * @@ -45,26 +45,33 @@ public @interface AllArgsConstructor { * is generated with the same argument list that wraps the real constructor. * * Such a static 'constructor' is primarily useful as it infers type arguments. + * + * @return Name of static 'constructor' method to generate (blank = generate a normal constructor). */ String staticName() default ""; /** * Any annotations listed here are put on the generated constructor. - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @AllArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @AllArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @AllArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}. + * + * @return List of annotations to apply to the generated constructor. */ AnyAnnotation[] onConstructor() default {}; /** * Sets the access level of the constructor. By default, generated constructors are {@code public}. + * + * @return The constructor will be generated with this access modifier. */ AccessLevel access() default lombok.AccessLevel.PUBLIC; /** * Placeholder annotation to enable the placement of annotations on the generated code. + * * @deprecated Don't use this annotation, ever - Read the documentation. */ @Deprecated diff --git a/src/core/lombok/Builder.java b/src/core/lombok/Builder.java index 9cf82191..7ae43bfa 100644 --- a/src/core/lombok/Builder.java +++ b/src/core/lombok/Builder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 The Project Lombok Authors. + * Copyright (C) 2013-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -48,8 +48,8 @@ import java.lang.annotation.Target; * as the relevant class, unless a method has been annotated, in which case it'll be equal to the * return type of that method. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Builder.html">the project lombok features page for @Builder</a>. - * <p> + * Complete documentation is found at <a href="https://projectlombok.org/features/Builder">the project lombok features page for @Builder</a>. + * <br> * <p> * Before: * @@ -107,10 +107,17 @@ import java.lang.annotation.Target; @Target({TYPE, METHOD, CONSTRUCTOR}) @Retention(SOURCE) public @interface Builder { - /** Name of the method that creates a new builder instance. Default: {@code builder}. */ + /** + * The field annotated with {@code @Default} must have an initializing expression; that expression is taken as the default to be used if not explicitly set during building. + */ + @Target(FIELD) + @Retention(SOURCE) + public @interface Default {} + + /** @return Name of the method that creates a new builder instance. Default: {@code builder}. */ String builderMethodName() default "builder"; - /** Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ + /** @return Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ String buildMethodName() default "build"; /** @@ -119,38 +126,50 @@ public @interface Builder { * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}. * <p> * Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}. + * + * @return Name of the builder class that will be generated (or if it already exists, will be filled with builder elements). */ String builderClassName() default ""; /** - * If true, the generated builder class will extend the {@code @Builder} of the - * superclass. In this way, the builder will also contain methods for fields - * from the superclass.<br> - * Note that both this builder and the superclass' builder must be a type - * {@code @Builder}; this feature does neither work for constructor nor - * method {@code @Builder}s. The parent {@code @Builder} must be - * {@link #extendable()}. <br> - * Implies {@link #extendable()}. + * Should the generated builder extend the parent class's builder? + * + * If {@code true}, the generated builder class will extend the builder of the + * superclass. This means the builder also contains the methods for fields from + * the superclass. + * <p> + * Note that both this builder and the superclass' builder must be a builder + * for a type (so, not for a static method or a constructor). You must mark + * the parent extendable: {@code @Builder(extensible = true)}. + * <p> + * This builder will also be {@code extensible = true} if you set {@code inherit = true}. + * + * @see #extensible() */ boolean inherit() default false; - + /** - * If true, the generated builder pattern will be extendable by - * {@code @Builder}s of subclasses, using the {@link #inherit()} feature. - * This is achieved by generating a special constructor that takes a builder - * instance as parameter (instead of an {@link AllArgsConstructor}). <br> - * Note that this builder must be a type {@code @Builder}; this feature does - * neither work for constructor nor method {@code @Builder}s.<br> - * If {@link #inherit()} {@code == true}, {@link #extendable()} will - * automatically be {@code true}. + * Should the generated builder be extensible by subclasses? + * + * If {@code true} the generated builder class will be extensible by + * {@code @Builder} classes of this class's subclasses, by marking them + * with {@code @Builder(inherit = true)}. + * <p> + * The {@code @Builder} extensible system only works for {@code @Builder} + * annotations on types (so, not on a static method or a constructor). + * + * @see #inherit() */ - boolean extendable() default false; - + boolean extensible() default false; + /** - * Name of the builder class in the superclass. Only relevant if - * {@code inherit = true} (see {@link #inherit()}). + * Name of the builder class in the superclass. + * + * Only relevant if {@code inherit = true} * - * Default {@code (SuperclassTypeName)Builder}. + * Default: {@code (SuperclassTypeName)Builder}. + * + * @see #inherit() */ String superclassBuilderClassName() default ""; @@ -158,6 +177,8 @@ public @interface Builder { * If true, generate an instance method to obtain a builder that is initialized with the values of this instance. * Legal only if {@code @Builder} is used on a constructor, on the type itself, or on a static method that returns * an instance of the declaring type. + * + * @return Whether to generate a {@code toBuilder()} method. */ boolean toBuilder() default false; @@ -174,13 +195,19 @@ public @interface Builder { @Target({FIELD, PARAMETER}) @Retention(SOURCE) public @interface ObtainVia { - /** Tells lombok to obtain a value with the expression {@code this.value}. */ + /** + * @return Tells lombok to obtain a value with the expression {@code this.value}. + */ String field() default ""; - /** Tells lombok to obtain a value with the expression {@code this.method()}. */ + /** + * @return Tells lombok to obtain a value with the expression {@code this.method()}. + */ String method() default ""; - /** Tells lombok to obtain a value with the expression {@code SelfType.method(this)}; requires {@code method} to be set. */ + /** + * @return Tells lombok to obtain a value with the expression {@code SelfType.method(this)}; requires {@code method} to be set. + */ boolean isStatic() default false; } } diff --git a/src/core/lombok/Cleanup.java b/src/core/lombok/Cleanup.java index c95c03c5..a3a1c198 100644 --- a/src/core/lombok/Cleanup.java +++ b/src/core/lombok/Cleanup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,7 +31,7 @@ import java.lang.annotation.Target; * of what happens. Implemented by wrapping all statements following the local variable declaration to the * end of your scope into a try block that, as a finally action, closes the resource. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Cleanup.html">the project lombok features page for @Cleanup</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Cleanup">the project lombok features page for @Cleanup</a>. * <p> * Example: * <pre> @@ -61,10 +61,10 @@ import java.lang.annotation.Target; * outStream.write(b, 0, r); * } * } finally { - * if (out != null) out.close(); + * if (outStream != null) outStream.close(); * } * } finally { - * if (in != null) in.close(); + * if (inStream != null) inStream.close(); * } * } * </pre> @@ -72,6 +72,6 @@ import java.lang.annotation.Target; @Target(ElementType.LOCAL_VARIABLE) @Retention(RetentionPolicy.SOURCE) public @interface Cleanup { - /** The name of the method that cleans up the resource. By default, 'close'. The method must not have any parameters. */ + /** @return The name of the method that cleans up the resource. By default, 'close'. The method must not have any parameters. */ String value() default "close"; } diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index f0e070e2..29c43d3f 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -41,19 +41,23 @@ public class ConfigurationKeys { /** * lombok configuration: {@code lombok.addGeneratedAnnotation} = {@code true} | {@code false}. * - * If unset or {@code true}, lombok generates {@code @javax.annotation.Generated("lombok")} on all fields, methods, and types that are generated, unless {@code lombok.addJavaxGeneratedAnnotation} is set. + * If {@code true}, lombok generates {@code @javax.annotation.Generated("lombok")} on all fields, methods, and types that are generated, unless {@code lombok.addJavaxGeneratedAnnotation} is set. + * <br> + * <em>BREAKING CHANGE</em>: Starting with lombok v2.0.0, defaults to {@code false} instead of {@code true}, as this annotation is broken in JDK9. * * @see ConfigurationKeys#ADD_JAVAX_GENERATED_ANNOTATIONS * @see ConfigurationKeys#ADD_LOMBOK_GENERATED_ANNOTATIONS * @deprecated Since version 1.16.14, use {@link #ADD_JAVAX_GENERATED_ANNOTATIONS} instead. */ @Deprecated - public static final ConfigurationKey<Boolean> ADD_GENERATED_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.addGeneratedAnnotation", "Generate @javax.annotation.Generated on all generated code (default: true). Deprecated, use 'lombok.addJavaxGeneratedAnnotation' instead.") {}; + public static final ConfigurationKey<Boolean> ADD_GENERATED_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.addGeneratedAnnotation", "Generate @javax.annotation.Generated on all generated code (default: false). Deprecated, use 'lombok.addJavaxGeneratedAnnotation' instead.") {}; /** * lombok configuration: {@code lombok.addJavaxGeneratedAnnotation} = {@code true} | {@code false}. * - * If unset or {@code true}, lombok generates {@code @javax.annotation.Generated("lombok")} on all fields, methods, and types that are generated, unless {@code lombok.addGeneratedAnnotation} is set to {@code false}. + * If {@code true}, lombok generates {@code @javax.annotation.Generated("lombok")} on all fields, methods, and types that are generated. + * <br> + * <em>BREAKING CHANGE</em>: Starting with lombok v2.0.0, defaults to {@code false} instead of {@code true}, as this annotation is broken in JDK9. */ public static final ConfigurationKey<Boolean> ADD_JAVAX_GENERATED_ANNOTATIONS = new ConfigurationKey<Boolean>("lombok.addJavaxGeneratedAnnotation", "Generate @javax.annotation.Generated on all generated code (default: follow lombok.addGeneratedAnnotation).") {}; @@ -89,10 +93,25 @@ public class ConfigurationKeys { * To suppress the generation of it, set this configuration to {@code true}. * * NB: GWT projects, and probably android projects, should explicitly set this key to {@code true} for the entire project. + * + * <br> + * <em>BREAKING CHANGE</em>: Starting with lombok v2.0.0, defaults to {@code false} instead of {@code true}, as {@code @ConstructorProperties} requires extra modules in JDK9. + * + * @see ConfigurationKeys#ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES + * @deprecated Since version 2.0, use {@link #ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES} instead. */ + @Deprecated public static final ConfigurationKey<Boolean> ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES = new ConfigurationKey<Boolean>("lombok.anyConstructor.suppressConstructorProperties", "Suppress the generation of @ConstructorProperties for generated constructors (default: false).") {}; /** + * lombok configuration: {@code lombok.anyConstructor.addConstructorProperties} = {@code true} | {@code false}. + * + * If {@code true}, all generated constructors with at least 1 argument get a {@code @ConstructorProperties}. + * + */ + public static final ConfigurationKey<Boolean> ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES = new ConfigurationKey<Boolean>("lombok.anyConstructor.addConstructorProperties", "Generate @ConstructorProperties for generated constructors (default: false).") {}; + + /** * lombok configuration: {@code lombok.allArgsConstructor.flagUsage} = {@code WARNING} | {@code ERROR}. * * If set, <em>any</em> usage of {@code @AllArgsConstructor} results in a warning / error. @@ -261,7 +280,7 @@ public class ConfigurationKeys { // ----- NonNull ----- /** - * lombok configuration: {@code lombok.nonNull.exceptionType} = <String: <em>a java exception type; either [{@code IllegalArgumentException} or: {@code NullPointerException}]. + * lombok configuration: {@code lombok.nonNull.exceptionType} = <String: <em>a java exception type</em>; either [{@code IllegalArgumentException} or: {@code NullPointerException}]. * * Sets the exception to throw if {@code @NonNull} is applied to a method parameter, and a caller passes in {@code null}. */ @@ -460,6 +479,17 @@ public class ConfigurationKeys { */ public static final ConfigurationKey<FlagUsageType> HELPER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.helper.flagUsage", "Emit a warning or error if @Helper is used.") {}; + // ----- onX ----- + + /** + * lombok configuration: {@code lombok.onX.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code onX} results in a warning / error. + * <br> + * Specifically, this flags usage of {@code @Getter(onMethod=...)}, {@code @Setter(onParam=...)}, {@code @Setter(onMethod=...)}, {@code @XArgsConstructor(onConstructor=...)}. + */ + public static final ConfigurationKey<FlagUsageType> ON_X_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.onX.flagUsage", "Emit a warning or error if onX is used.") {}; + // ----- UtilityClass ----- /** diff --git a/src/core/lombok/Data.java b/src/core/lombok/Data.java index 4c6c2e5d..ffa968c1 100644 --- a/src/core/lombok/Data.java +++ b/src/core/lombok/Data.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,7 +32,7 @@ import java.lang.annotation.Target; * <p> * Equivalent to {@code @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode}. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Data.html">the project lombok features page for @Data</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Data">the project lombok features page for @Data</a>. * * @see Getter * @see Setter @@ -54,6 +54,8 @@ public @interface Data { * </pre> * * Default: No static constructor, instead the normal constructor is public. + * + * @return Name of static 'constructor' method to generate (blank = generate a normal constructor). */ String staticConstructor() default ""; } diff --git a/src/core/lombok/Delegate.java b/src/core/lombok/Delegate.java index d5b4b48c..0c5a4c2c 100644 --- a/src/core/lombok/Delegate.java +++ b/src/core/lombok/Delegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -40,6 +40,8 @@ public @interface Delegate { * type listed here is used only to determine which delegate methods to generate. * * NB: All methods in {@code Object}, as well as {@code canEqual(Object other)} will never be delegated. + * + * @return For each method (not already in {@code java.lang.Object}) in these types, generate a delegate method. */ Class<?>[] types() default {}; @@ -47,6 +49,8 @@ public @interface Delegate { * Each method in any of the types listed here (include supertypes) will <em>not</em> be delegated. * * NB: All methods in {@code Object}, as well as {@code canEqual(Object other)} will never be delegated. + * + * @return For each method (not already in {@code java.lang.Object}) in these types, skip generating a delegate method (overrides {@code types()}). */ Class<?>[] excludes() default {}; } diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java index 34f4ffc6..2f88ac50 100644 --- a/src/core/lombok/EqualsAndHashCode.java +++ b/src/core/lombok/EqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,15 +29,16 @@ import java.lang.annotation.Target; /** * Generates implementations for the {@code equals} and {@code hashCode} methods inherited by all objects, based on relevant fields. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/EqualsAndHashCode.html">the project lombok features page for @EqualsAndHashCode</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/EqualsAndHashCode">the project lombok features page for @EqualsAndHashCode</a>. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface EqualsAndHashCode { /** - * Any fields listed here will not be taken into account in the generated - * {@code equals} and {@code hashCode} implementations. + * Any fields listed here will not be taken into account in the generated {@code equals} and {@code hashCode} implementations. * Mutually exclusive with {@link #of()}. + * + * @return A list of fields to exclude. */ String[] exclude() default {}; @@ -46,30 +47,37 @@ public @interface EqualsAndHashCode { * Normally, all non-static, non-transient fields are used for identity. * <p> * Mutually exclusive with {@link #exclude()}. + * + * @return A list of fields to use (<em>default</em>: all of them). */ String[] of() default {}; /** - * Call on the superclass's implementations of {@code equals} and {@code hashCode} before calculating - * for the fields in this class. + * Call on the superclass's implementations of {@code equals} and {@code hashCode} before calculating for the fields in this class. * <strong>default: false</strong> + * + * @return Whether to call the superclass's {@code equals} implementation as part of the generated equals algorithm. */ boolean callSuper() default false; /** * Normally, if getters are available, those are called. To suppress this and let the generated code use the fields directly, set this to {@code true}. * <strong>default: false</strong> + * + * @return If {@code true}, always use direct field access instead of calling the getter method. */ boolean doNotUseGetters() default false; /** * Any annotations listed here are put on the generated parameter of {@code equals} and {@code canEqual}. - * This is useful to add for example a {@code Nullable} annotation.<br /> - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @EqualsAndHashCode(onParam=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * This is useful to add for example a {@code Nullable} annotation.<br> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @EqualsAndHashCode(onParam=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @EqualsAndHashCode(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}. + * + * @return List of annotations to apply to the generated parameter in the {@code equals()} method. */ AnyAnnotation[] onParam() default {}; diff --git a/src/core/lombok/Getter.java b/src/core/lombok/Getter.java index 5a4056fa..5a23fe30 100644 --- a/src/core/lombok/Getter.java +++ b/src/core/lombok/Getter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Put on any field to make lombok build a standard getter. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/GetterSetter.html">the project lombok features page for @Getter and @Setter</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/GetterSetter">the project lombok features page for @Getter and @Setter</a>. * <p> * Even though it is not listed, this annotation also has the {@code onMethod} parameter. See the full documentation for more details. * <p> @@ -54,25 +54,29 @@ import java.lang.annotation.Target; public @interface Getter { /** * If you want your getter to be non-public, you can specify an alternate access level here. + * + * @return The getter method will be generated with this access modifier. */ lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC; /** * Any annotations listed here are put on the generated method. - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @Getter(onMethod=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @Getter(onMethod=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @Getter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}. + * + * @return List of annotations to apply to the generated getter method. */ AnyAnnotation[] onMethod() default {}; boolean lazy() default false; /** - * Placeholder annotation to enable the placement of annotations on the generated code. - * @deprecated Don't use this annotation, ever - Read the documentation. - */ + * Placeholder annotation to enable the placement of annotations on the generated code. + * @deprecated Don't use this annotation, ever - Read the documentation. + */ @Deprecated @Retention(RetentionPolicy.SOURCE) @Target({}) diff --git a/src/core/lombok/Lombok.java b/src/core/lombok/Lombok.java index 164c47a8..d86b6d1c 100644 --- a/src/core/lombok/Lombok.java +++ b/src/core/lombok/Lombok.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,6 @@ public class Lombok { * The exception is still thrown - javac will just stop whining about it. * <p> * Example usage: - * <p> * <pre>public void run() { * throw sneakyThrow(new IOException("You don't need to catch me!")); * }</pre> @@ -49,24 +48,22 @@ public class Lombok { */ public static RuntimeException sneakyThrow(Throwable t) { if (t == null) throw new NullPointerException("t"); - Lombok.<RuntimeException>sneakyThrow0(t); - return null; + return Lombok.<RuntimeException>sneakyThrow0(t); } @SuppressWarnings("unchecked") - private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T { + private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T { throw (T)t; } /** - * Returns the parameter directly. <br /> + * Returns the parameter directly. * - * This method can be used to prevent a static analyzer to determine - * the nullness of the passed parameter. + * This method can be used to prevent a static analyzer to determine the nullness of the passed parameter. * - * @param <T> the type of the parameter - * @param value the value to return - * @return value + * @param <T> the type of the parameter. + * @param value the value to return. + * @return value (this method just returns the parameter). */ public static <T> T preventNullAnalysis(T value) { return value; @@ -74,10 +71,12 @@ public class Lombok { /** * Ensures that the {@code value} is not {@code null}. - * @param value the value to test for null - * @param message the message of the {@link NullPointerException} - * @return the value if it is not null - * @throws NullPointerException with the {@code message} if the value is null + * + * @param <T> Type of the parameter. + * @param value the value to test for null. + * @param message the message of the {@link NullPointerException}. + * @return the value if it is not null. + * @throws NullPointerException with the {@code message} if the value is null. */ public static <T> T checkNotNull(T value, String message) { if (value == null) throw new NullPointerException(message); diff --git a/src/core/lombok/NoArgsConstructor.java b/src/core/lombok/NoArgsConstructor.java index 5f2268a8..672cd1c2 100644 --- a/src/core/lombok/NoArgsConstructor.java +++ b/src/core/lombok/NoArgsConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2015 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,7 @@ import java.lang.annotation.Target; * Generates a no-args constructor. * Will generate an error message if such a constructor cannot be written due to the existence of final fields. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor.html">the project lombok features page for @Constructor</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for @Constructor</a>. * <p> * Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details. * <p> @@ -47,27 +47,35 @@ public @interface NoArgsConstructor { * is generated with the same argument list that wraps the real constructor. * * Such a static 'constructor' is primarily useful as it infers type arguments. + * + * @return Name of static 'constructor' method to generate (blank = generate a normal constructor). */ String staticName() default ""; /** * Any annotations listed here are put on the generated constructor. - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @NoArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @NoArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @NoArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}. + * + * @return List of annotations to apply to the generated constructor. */ AnyAnnotation[] onConstructor() default {}; /** * Sets the access level of the constructor. By default, generated constructors are {@code public}. + * + * @return The constructor will be generated with this access modifier. */ AccessLevel access() default lombok.AccessLevel.PUBLIC; /** * If {@code true}, initializes all final fields to 0 / null / false. * Otherwise, a compile time error occurs. + * + * @return Return {@code} true to force generation of a no-args constructor, picking defaults if necessary to assign required fields. */ boolean force() default false; diff --git a/src/core/lombok/RequiredArgsConstructor.java b/src/core/lombok/RequiredArgsConstructor.java index 2f4d0aaf..f21bd647 100644 --- a/src/core/lombok/RequiredArgsConstructor.java +++ b/src/core/lombok/RequiredArgsConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,7 +30,7 @@ import java.lang.annotation.Target; * Generates a constructor with required arguments. * Required arguments are final fields and fields with constraints such as {@code @NonNull}. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor.html">the project lombok features page for @Constructor</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Constructor">the project lombok features page for @Constructor</a>. * <p> * Even though it is not listed, this annotation also has the {@code onConstructor} parameter. See the full documentation for more details. * @@ -45,21 +45,27 @@ public @interface RequiredArgsConstructor { * is generated with the same argument list that wraps the real constructor. * * Such a static 'constructor' is primarily useful as it infers type arguments. + * + * @return Name of static 'constructor' method to generate (blank = generate a normal constructor). */ String staticName() default ""; /** * Any annotations listed here are put on the generated constructor. - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @RequiredArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @RequiredArgsConstructor(onConstructor=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @RequiredArgsConstructor(onConstructor_={@AnnotationsGohere})} // note the underscore after {@code onConstructor}. + * + * @return List of annotations to apply to the generated constructor. */ AnyAnnotation[] onConstructor() default {}; /** * Sets the access level of the constructor. By default, generated constructors are {@code public}. + * + * @return The constructor will be generated with this access modifier. */ AccessLevel access() default lombok.AccessLevel.PUBLIC; diff --git a/src/core/lombok/Setter.java b/src/core/lombok/Setter.java index f2ebe5b3..1b70bac9 100644 --- a/src/core/lombok/Setter.java +++ b/src/core/lombok/Setter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Put on any field to make lombok build a standard setter. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/GetterSetter.html">the project lombok features page for @Getter and @Setter</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/GetterSetter">the project lombok features page for @Getter and @Setter</a>. * <p> * Even though it is not listed, this annotation also has the {@code onParam} and {@code onMethod} parameter. See the full documentation for more details. * <p> @@ -55,26 +55,32 @@ import java.lang.annotation.Target; public @interface Setter { /** * If you want your setter to be non-public, you can specify an alternate access level here. + * + * @return The setter method will be generated with this access modifier. */ lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC; /** * Any annotations listed here are put on the generated method. - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @Setter(onMethod=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @Setter(onMethod=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @Setter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}. + * + * @return List of annotations to apply to the generated setter method. */ AnyAnnotation[] onMethod() default {}; /** * Any annotations listed here are put on the generated method's parameter. - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @Setter(onParam=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @Setter(onParam=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @Setter(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}. + * + * @return List of annotations to apply to the generated parameter in the setter method. */ AnyAnnotation[] onParam() default {}; diff --git a/src/core/lombok/Singular.java b/src/core/lombok/Singular.java index 15dec4a5..67edab96 100644 --- a/src/core/lombok/Singular.java +++ b/src/core/lombok/Singular.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,10 +29,10 @@ import java.lang.annotation.Target; /** * The singular annotation is used together with {@code @Builder} to create single element 'add' methods in the builder for collections. - * <p> */ @Target({FIELD, PARAMETER}) @Retention(SOURCE) public @interface Singular { + /** @return The singular name of this field. If it's a normal english plural, lombok will figure it out automatically. Otherwise, this parameter is mandatory. */ String value() default ""; } diff --git a/src/core/lombok/SneakyThrows.java b/src/core/lombok/SneakyThrows.java index 489e13e4..0506d615 100644 --- a/src/core/lombok/SneakyThrows.java +++ b/src/core/lombok/SneakyThrows.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,7 +34,7 @@ import java.lang.annotation.Target; * checked exception types. The JVM does not check for the consistency of the checked exception system; javac does, * and this annotation lets you opt out of its mechanism. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/SneakyThrows.html">the project lombok features page for @SneakyThrows</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/SneakyThrows">the project lombok features page for @SneakyThrows</a>. * <p> * Example: * <pre> @@ -59,8 +59,9 @@ import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.SOURCE) public @interface SneakyThrows { - /** The exception type(s) you want to sneakily throw onward. */ + /** @return The exception type(s) you want to sneakily throw onward. */ Class<? extends Throwable>[] value() default java.lang.Throwable.class; - //The package is mentioned in java.lang due to a bug in javac (presence of an annotation processor throws off the type resolver for some reason). + //The fully qualified name is used for java.lang.Throwable in the parameter only. This works around a bug in javac: + // presence of an annotation processor throws off the type resolver for some reason. } diff --git a/src/core/lombok/Synchronized.java b/src/core/lombok/Synchronized.java index 9a39c212..5dff0fb2 100644 --- a/src/core/lombok/Synchronized.java +++ b/src/core/lombok/Synchronized.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,7 +35,7 @@ import java.lang.annotation.Target; * {@code $LOCK} is used. These will be generated if needed and if they aren't already present. The contents * of the fields will be serializable. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Synchronized.html">the project lombok features page for @Synchronized</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Synchronized">the project lombok features page for @Synchronized</a>. */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) @@ -43,6 +43,8 @@ public @interface Synchronized { /** * Optional: specify the name of a different field to lock on. It is a compile time error if this field * doesn't already exist (the fields are automatically generated only if you don't specify a specific name. + * + * @return Name of the field to lock on (blank = generate one). */ String value() default ""; } diff --git a/src/core/lombok/ToString.java b/src/core/lombok/ToString.java index b9926148..0c43c40b 100644 --- a/src/core/lombok/ToString.java +++ b/src/core/lombok/ToString.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Generates an implementation for the {@code toString} method inherited by all objects, consisting of printing the values of relevant fields. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/ToString.html">the project lombok features page for @ToString</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/ToString">the project lombok features page for @ToString</a>. */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) @@ -37,12 +37,16 @@ public @interface ToString { /** * Include the name of each field when printing it. * <strong>default: true</strong> + * + * @return Whether or not to include the names of fields in the string produced by the generated {@code toString()}. */ boolean includeFieldNames() default true; /** * Any fields listed here will not be printed in the generated {@code toString} implementation. * Mutually exclusive with {@link #of()}. + * + * @return A list of fields to exclude. */ String[] exclude() default {}; @@ -51,18 +55,24 @@ public @interface ToString { * Normally, all non-static fields are printed. * <p> * Mutually exclusive with {@link #exclude()}. + * + * @return A list of fields to use (<em>default</em>: all of them). */ String[] of() default {}; /** * Include the result of the superclass's implementation of {@code toString} in the output. * <strong>default: false</strong> + * + * @return Whether to call the superclass's {@code toString} implementation as part of the generated toString algorithm. */ boolean callSuper() default false; /** * Normally, if getters are available, those are called. To suppress this and let the generated code use the fields directly, set this to {@code true}. * <strong>default: false</strong> + * + * @return If {@code true}, always use direct field access instead of calling the getter method. */ boolean doNotUseGetters() default false; } diff --git a/src/core/lombok/Value.java b/src/core/lombok/Value.java index 39154226..562ba0cc 100644 --- a/src/core/lombok/Value.java +++ b/src/core/lombok/Value.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,7 +31,7 @@ import java.lang.annotation.Target; * <p> * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @AllArgsConstructor @ToString @EqualsAndHashCode}. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Value.html">the project lombok features page for @Value</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Value">the project lombok features page for @Value</a>. * * @see lombok.Getter * @see lombok.experimental.FieldDefaults @@ -53,6 +53,8 @@ public @interface Value { * </pre> * * Default: No static constructor, instead the normal constructor is public. + * + * @return Name of static 'constructor' method to generate (blank = generate a normal constructor). */ String staticConstructor() default ""; } diff --git a/src/core/lombok/bytecode/AsmUtil.java b/src/core/lombok/bytecode/AsmUtil.java index 26e5af1f..e3d33efa 100644 --- a/src/core/lombok/bytecode/AsmUtil.java +++ b/src/core/lombok/bytecode/AsmUtil.java @@ -37,7 +37,7 @@ class AsmUtil { ClassReader reader = new ClassReader(byteCode); ClassWriter writer = new FixedClassWriter(reader, 0); - ClassVisitor visitor = new ClassVisitor(Opcodes.ASM5, writer) { + ClassVisitor visitor = new ClassVisitor(Opcodes.ASM6, writer) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new JSRInlinerAdapter(super.visitMethod(access, name, desc, signature, exceptions), access, name, desc, signature, exceptions); } diff --git a/src/core/lombok/bytecode/ClassFileMetaData.java b/src/core/lombok/bytecode/ClassFileMetaData.java index 68b8bb7d..0dc6a6c8 100644 --- a/src/core/lombok/bytecode/ClassFileMetaData.java +++ b/src/core/lombok/bytecode/ClassFileMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Project Lombok Authors. + * Copyright (C) 2010-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -44,6 +44,9 @@ public class ClassFileMetaData { private static final byte METHOD_HANDLE = 15; private static final byte METHOD_TYPE = 16; private static final byte INVOKE_DYNAMIC = 18; + // New in java9: support for modules + private static final byte MODULE = 19; + private static final byte PACKAGE = 20; private static final int NOT_FOUND = -1; private static final int START_OF_CONSTANT_POOL = 8; @@ -79,6 +82,8 @@ public class ClassFileMetaData { case CLASS: case STRING: case METHOD_TYPE: + case MODULE: + case PACKAGE: position += 2; break; case METHOD_HANDLE: diff --git a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java index 5f2f5f18..c06f2d7c 100644 --- a/src/core/lombok/bytecode/PreventNullAnalysisRemover.java +++ b/src/core/lombok/bytecode/PreventNullAnalysisRemover.java @@ -50,7 +50,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation { class PreventNullAnalysisVisitor extends MethodVisitor { PreventNullAnalysisVisitor(MethodVisitor mv) { - super(Opcodes.ASM5, mv); + super(Opcodes.ASM6, mv); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { @@ -68,7 +68,7 @@ public class PreventNullAnalysisRemover implements PostCompilerTransformation { } } - reader.accept(new ClassVisitor(Opcodes.ASM5, writer) { + reader.accept(new ClassVisitor(Opcodes.ASM6, writer) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new PreventNullAnalysisVisitor(super.visitMethod(access, name, desc, signature, exceptions)); } diff --git a/src/core/lombok/bytecode/SneakyThrowsRemover.java b/src/core/lombok/bytecode/SneakyThrowsRemover.java index 8ef64e59..a2ee59ea 100644 --- a/src/core/lombok/bytecode/SneakyThrowsRemover.java +++ b/src/core/lombok/bytecode/SneakyThrowsRemover.java @@ -47,12 +47,12 @@ public class SneakyThrowsRemover implements PostCompilerTransformation { ClassReader reader = new ClassReader(fixedByteCode); ClassWriter writer = new ClassWriter(reader, 0); - + final AtomicBoolean changesMade = new AtomicBoolean(); class SneakyThrowsRemoverVisitor extends MethodVisitor { SneakyThrowsRemoverVisitor(MethodVisitor mv) { - super(Opcodes.ASM5, mv); + super(Opcodes.ASM6, mv); } private boolean methodInsnQueued = false; diff --git a/src/core/lombok/core/AnnotationProcessor.java b/src/core/lombok/core/AnnotationProcessor.java index 5531ad8e..5b4ef393 100644 --- a/src/core/lombok/core/AnnotationProcessor.java +++ b/src/core/lombok/core/AnnotationProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -40,6 +40,7 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; +import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; @@ -163,7 +164,21 @@ public class AnnotationProcessor extends AbstractProcessor { for (ProcessorDescriptor proc : active) proc.process(annotations, roundEnv); - return false; + boolean onlyLombok = true; + boolean zeroElems = true; + for (TypeElement elem : annotations) { + zeroElems = false; + Name n = elem.getQualifiedName(); + if (n.length() > 7 && n.subSequence(0, 7).toString().equals("lombok.")) continue; + onlyLombok = false; + } + + // Normally we rely on the claiming processor to claim away all lombok annotations. + // One of the many Java9 oversights is that this 'process' API has not been fixed to address the point that 'files I want to look at' and 'annotations I want to claim' must be one and the same, + // and yet in java9 you can no longer have 2 providers for the same service, thus, if you go by module path, lombok no longer loads the ClaimingProcessor. + // This doesn't do as good a job, but it'll have to do. The only way to go from here, I think, is either 2 modules, or use reflection hackery to add ClaimingProcessor during our init. + + return onlyLombok && !zeroElems; } /** diff --git a/src/core/lombok/core/LombokInternalAliasing.java b/src/core/lombok/core/LombokInternalAliasing.java index 08764a5c..3dc1bfa2 100644 --- a/src/core/lombok/core/LombokInternalAliasing.java +++ b/src/core/lombok/core/LombokInternalAliasing.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 The Project Lombok Authors. + * Copyright (C) 2013-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -51,6 +51,7 @@ public class LombokInternalAliasing { Map<String, String> m2 = new HashMap<String, String>(); m2.put("lombok.experimental.Value", "lombok.Value"); m2.put("lombok.experimental.Builder", "lombok.Builder"); + m2.put("lombok.experimental.var", "lombok.var"); m2.put("lombok.Delegate", "lombok.experimental.Delegate"); ALIASES = Collections.unmodifiableMap(m2); } diff --git a/src/core/lombok/core/PublicApiCreatorApp.java b/src/core/lombok/core/PublicApiCreatorApp.java index 178a45e8..c1430c24 100644 --- a/src/core/lombok/core/PublicApiCreatorApp.java +++ b/src/core/lombok/core/PublicApiCreatorApp.java @@ -105,7 +105,7 @@ public class PublicApiCreatorApp extends LombokApp { int firstSlash = subName.indexOf('/'); if (firstSlash == -1) { // direct member of the lombok package. - toCopy.add(name); + if (!subName.startsWith("ConfigurationKeys")) toCopy.add(name); continue; } String topPkg = subName.substring(0, firstSlash); diff --git a/src/core/lombok/core/Version.java b/src/core/lombok/core/Version.java index a50b72d5..98f1e575 100644 --- a/src/core/lombok/core/Version.java +++ b/src/core/lombok/core/Version.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2016 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,9 +30,15 @@ public class Version { // ** CAREFUL ** - this class must always compile with 0 dependencies (it must not refer to any other sources or libraries). // Note: In 'X.Y.Z', if Z is odd, its a snapshot build built from the repository, so many different 0.10.3 versions can exist, for example. // Official builds always end in an even number. (Since 0.10.2). - private static final String VERSION = "1.16.15"; + private static final String VERSION = "1.16.21"; private static final String RELEASE_NAME = "Edgy Guinea Pig"; -// private static final String RELEASE_NAME = "Candid Duck"; +// private static final String RELEASE_NAME = "Dancing Elephant"; + + // Named version history: + // Angry Butterfly + // Branching Cobra + // Candid Duck + // Dancing Elephant private Version() { //Prevent instantiation @@ -43,7 +49,7 @@ public class Version { */ public static void main(String[] args) { if (args.length > 0) { - System.out.printf("Lombok %s\n", getFullVersion()); + System.out.printf("%s\n", getFullVersion()); } else { System.out.println(VERSION); } diff --git a/src/core/lombok/core/configuration/AllowHelper.java b/src/core/lombok/core/configuration/AllowHelper.java index 3873b055..1146ccde 100644 --- a/src/core/lombok/core/configuration/AllowHelper.java +++ b/src/core/lombok/core/configuration/AllowHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Project Lombok Authors. + * Copyright (C) 2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,8 @@ package lombok.core.configuration; import java.util.Collection; import java.util.Collections; -import lombok.ConfigurationKeys; - public final class AllowHelper { - private final static Collection<? extends ConfigurationKey<?>> ALLOWABLE = Collections.singleton(ConfigurationKeys.VAR_FLAG_USAGE); + private final static Collection<? extends ConfigurationKey<?>> ALLOWABLE = Collections.emptySet(); private AllowHelper() { // Prevent instantiation diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 6c3a0b79..6b516904 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 The Project Lombok Authors. + * Copyright (C) 2013-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -113,7 +113,7 @@ public class HandlerUtil { public static boolean shouldAddGenerated(LombokNode<?, ?, ?> node) { Boolean add = node.getAst().readConfiguration(ConfigurationKeys.ADD_JAVAX_GENERATED_ANNOTATIONS); if (add != null) return add; - return !Boolean.FALSE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_GENERATED_ANNOTATIONS)); + return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.ADD_GENERATED_ANNOTATIONS)); } public static void handleExperimentalFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) { @@ -171,11 +171,12 @@ public class HandlerUtil { } @SuppressWarnings({"all", "unchecked", "deprecation"}) - public static final List<Class<? extends java.lang.annotation.Annotation>> INVALID_ON_BUILDERS = Collections.unmodifiableList( - Arrays.<Class<? extends java.lang.annotation.Annotation>>asList( - Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class, - RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class, - Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class)); + public static final List<String> INVALID_ON_BUILDERS = Collections.unmodifiableList( + Arrays.<String>asList( + Getter.class.getName(), Setter.class.getName(), Wither.class.getName(), + ToString.class.getName(), EqualsAndHashCode.class.getName(), + RequiredArgsConstructor.class.getName(), AllArgsConstructor.class.getName(), NoArgsConstructor.class.getName(), + Data.class.getName(), Value.class.getName(), "lombok.experimental.Value", FieldDefaults.class.getName())); /** * Given the name of a field, return the 'base name' of that field. For example, {@code fFoobar} becomes {@code foobar} if {@code f} is in the prefix list. diff --git a/src/core/lombok/core/handlers/singulars.txt b/src/core/lombok/core/handlers/singulars.txt index 9976c76c..d77308cb 100644 --- a/src/core/lombok/core/handlers/singulars.txt +++ b/src/core/lombok/core/handlers/singulars.txt @@ -19,7 +19,7 @@ Mice = mouse Lice = louse News = ! # We could add more detail (axemen, boatsmen, boogymen, cavemen, gentlemen, etc, but (A) there's stuff like 'cerumen', and (B) the 'men' ending is common in singulars and other languages.) -# Therefore, the odds of a mistake are too high, so other than these 2 well known cases, so force the explicit singular. +# Therefore, the odds of a mistake are too high, so other than these 2 well known cases, force the explicit singular. Men = man Women = woman minutiae = minutia @@ -48,7 +48,9 @@ hives = hive -shes = sh -lves = lf -rves = rf --ves = fe +saves = save +Leaves = leaf +-ves = ! -ss = ! -us = ! -s = diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 4726b17e..6617d21a 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -167,6 +167,7 @@ public class EclipseHandlerUtil { } public static boolean isFieldDeprecated(EclipseNode fieldNode) { + if (!(fieldNode.get() instanceof FieldDeclaration)) return false; FieldDeclaration field = (FieldDeclaration) fieldNode.get(); if ((field.modifiers & ClassFileConstants.AccDeprecated) != 0) { return true; @@ -196,17 +197,36 @@ public class EclipseHandlerUtil { TypeResolver resolver = new TypeResolver(node.getImportList()); return resolver.typeMatches(node, type.getName(), typeName); + } + + /** + * Checks if the given TypeReference node is likely to be a reference to the provided class. + * + * @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type. + * @param node A Lombok AST node. Any node in the appropriate compilation unit will do (used to get access to import statements). + * @param typeRef A type reference to check. + */ + public static boolean typeMatches(String type, EclipseNode node, TypeReference typeRef) { + if (typeRef == null || typeRef.getTypeName() == null || typeRef.getTypeName().length == 0) return false; + String lastPartA = new String(typeRef.getTypeName()[typeRef.getTypeName().length -1]); + int lastIndex = type.lastIndexOf('.'); + String lastPartB = lastIndex == -1 ? type : type.substring(lastIndex + 1); + if (!lastPartA.equals(lastPartB)) return false; + String typeName = toQualifiedName(typeRef.getTypeName()); + TypeResolver resolver = new TypeResolver(node.getImportList()); + return resolver.typeMatches(node, type, typeName); } public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(EclipseNode typeNode, EclipseNode errorNode) { List<String> disallowed = null; for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.ANNOTATION) continue; - for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) { + for (String annType : INVALID_ON_BUILDERS) { if (annotationTypeMatches(annType, child)) { if (disallowed == null) disallowed = new ArrayList<String>(); - disallowed.add(annType.getSimpleName()); + int lastIndex = annType.lastIndexOf('.'); + disallowed.add(lastIndex == -1 ? annType : annType.substring(lastIndex + 1)); } } } @@ -443,6 +463,42 @@ public class EclipseHandlerUtil { } } + public static boolean hasAnnotation(String type, EclipseNode node) { + if (node == null) return false; + if (type == null) return false; + switch (node.getKind()) { + case ARGUMENT: + case FIELD: + case LOCAL: + case TYPE: + case METHOD: + for (EclipseNode child : node.down()) { + if (annotationTypeMatches(type, child)) return true; + } + // intentional fallthrough + default: + return false; + } + } + + public static EclipseNode findAnnotation(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) { + if (node == null) return null; + if (type == null) return null; + switch (node.getKind()) { + case ARGUMENT: + case FIELD: + case LOCAL: + case TYPE: + case METHOD: + for (EclipseNode child : node.down()) { + if (annotationTypeMatches(type, child)) return child; + } + // intentional fallthrough + default: + return null; + } + } + /** * Checks if the provided annotation type is likely to be the intended type for the given annotation node. * @@ -453,6 +509,16 @@ public class EclipseHandlerUtil { return typeMatches(type, node, ((Annotation) node.get()).type); } + /** + * Checks if the provided annotation type is likely to be the intended type for the given annotation node. + * + * This is a guess, but a decent one. + */ + public static boolean annotationTypeMatches(String type, EclipseNode node) { + if (node.getKind() != Kind.ANNOTATION) return false; + return typeMatches(type, node, ((Annotation) node.get()).type); + } + public static TypeReference cloneSelfType(EclipseNode context) { return cloneSelfType(context, null); } @@ -831,7 +897,7 @@ public class EclipseHandlerUtil { // Check if the class has a @Getter annotation. - if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) { + if (!hasGetterAnnotation && HandleGetter.fieldQualifiesForGetterGeneration(field)) { //Check if the class has @Getter or @Data annotation. EclipseNode containingType = field.up(); diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 10ed7fbb..2cec2388 100644 --- a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java +++ b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -134,6 +134,10 @@ public class EclipseSingularsRecipes { } } + public ASTNode getSource() { + return source; + } + public EclipseNode getAnnotation() { return annotation; } @@ -213,7 +217,7 @@ public class EclipseSingularsRecipes { } public abstract List<EclipseNode> generateFields(SingularData data, EclipseNode builderType); - public abstract void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain); + public abstract void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain); public abstract void appendBuildCode(SingularData data, EclipseNode builderType, List<Statement> statements, char[] targetVariableName, String builderVariable); public boolean requiresCleaning() { @@ -304,8 +308,7 @@ public class EclipseSingularsRecipes { private static final char[] SIZE_TEXT = new char[] {'s', 'i', 'z', 'e'}; - /** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). - * @param builderVariable */ + /** Generates 'this.<em>name</em>.size()' as an expression; if nullGuard is true, it's this.name == null ? 0 : this.name.size(). */ protected Expression getSize(EclipseNode builderType, char[] name, boolean nullGuard, String builderVariable) { MessageSend invoke = new MessageSend(); Reference thisRef = getBuilderReference(builderVariable); @@ -345,9 +348,7 @@ public class EclipseSingularsRecipes { return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS); } - /** - * @return a {@link SingleNameReference} to the builder in the variable <code>builderVariable</code>. If <code>builderVariable == "this"</code>, a {@link ThisReference} is returned. - */ + /** @return a {@code SingleNameReference} to the builder in the variable <code>builderVariable</code>. If {@ code builderVariable == "this"}, a {@code ThisReference} is returned. */ protected static Reference getBuilderReference(String builderVariable) { if ("this".equals(builderVariable)) { return new ThisReference(0, 0); diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 3795c3de..b5c6e793 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2015 The Project Lombok Authors. + * Copyright (C) 2013-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -39,6 +39,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.ast.Expression; @@ -92,6 +93,8 @@ import lombok.experimental.NonFinal; @ProviderFor(EclipseAnnotationHandler.class) @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleBuilder extends EclipseAnnotationHandler<Builder> { + private HandleConstructor handleConstructor = new HandleConstructor(); + private static final char[] CLEAN_FIELD_NAME = "$lombokUnclean".toCharArray(); private static final char[] CLEAN_METHOD_NAME = "$lombokClean".toCharArray(); @@ -107,9 +110,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { TypeReference type; char[] rawName; char[] name; + char[] nameOfDefaultProvider; + char[] nameOfSetFlag; SingularData singularData; ObtainVia obtainVia; EclipseNode obtainViaNode; + EclipseNode originalFieldNode; List<EclipseNode> createdFields = new ArrayList<EclipseNode>(); } @@ -132,6 +138,16 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return true; } + private static final char[] DEFAULT_PREFIX = {'$', 'd', 'e', 'f', 'a', 'u', 'l', 't', '$'}; + private static final char[] SET_PREFIX = {'$', 's', 'e', 't'}; + + private static final char[] prefixWith(char[] prefix, char[] name) { + char[] out = new char[prefix.length + name.length]; + System.arraycopy(prefix, 0, out, 0, prefix.length); + System.arraycopy(name, 0, out, prefix.length, name.length); + return out; + } + @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) { long p = (long) ast.sourceStart << 32 | ast.sourceEnd; @@ -146,9 +162,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { String builderClassName = builderInstance.builderClassName(); boolean inherit = builderInstance.inherit(); - boolean extendable = inherit || builderInstance.extendable(); // inherit implies extendable - String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); - + boolean extensible = inherit || builderInstance.extensible(); // inherit implies extendable + String toBuilderMethodName = "toBuilder"; boolean toBuilder = builderInstance.toBuilder(); List<char[]> typeArgsForToBuilder = null; @@ -156,9 +171,6 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) builderMethodName = "build"; if (builderClassName == null) builderClassName = ""; - if (superclassBuilderClassName == null) { - superclassBuilderClassName = ""; - } if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; @@ -179,43 +191,61 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { boolean addCleaning = false; boolean isStatic = true; + TypeDeclaration td = null; + if (parent.get() instanceof TypeDeclaration) { tdParent = parent; - TypeDeclaration td = (TypeDeclaration) tdParent.get(); + td = (TypeDeclaration) tdParent.get(); List<EclipseNode> allFields = new ArrayList<EclipseNode>(); - @SuppressWarnings("deprecation") - boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent)); - for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent)) { + boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); + for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); - // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes - // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves. - // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. - if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; + EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode); + boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); + BuilderFieldData bfd = new BuilderFieldData(); bfd.fieldNode = fieldNode; bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.type; bfd.singularData = getSingularData(fieldNode, ast); + bfd.originalFieldNode = fieldNode; + + if (bfd.singularData != null && isDefault != null) { + isDefault.addError("@Builder.Default and @Singular cannot be mixed."); + isDefault = null; + } + + if (fd.initialization == null && isDefault != null) { + isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); + isDefault = null; + } + + if (fd.initialization != null && isDefault == null) { + if (isFinal) continue; + fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + + if (isDefault != null) { + bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name); + bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX); + + MethodDeclaration md = generateDefaultProvider(bfd.nameOfDefaultProvider, td.typeParameters, fieldNode, ast); + if (md != null) injectMethod(tdParent, md); + } addObtainVia(bfd, fieldNode); builderFields.add(bfd); allFields.add(fieldNode); } - if (builderClassName.isEmpty()) { - builderClassName = new String(td.name) + "Builder"; - } - if (superclassBuilderClassName.isEmpty() && td.superclass != null) { - superclassBuilderClassName = new String(td.superclass.getLastToken()) + "Builder"; - } + if (builderClassName.isEmpty()) builderClassName = new String(td.name) + "Builder"; - if (extendable) { + if (extensible) { boolean callBuilderBasedSuperConstructor = td.superclass != null; - generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, - builderClassName, callBuilderBasedSuperConstructor); + generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); } else { - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, + handleConstructor.generateConstructor(tdParent, AccessLevel.PACKAGE, allFields, false, null, SkipIfConstructorExists.I_AM_BUILDER, Collections.<Annotation>emptyList(), annotationNode); } @@ -228,8 +258,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } - if (extendable) { - annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); + if (extensible) { + annotationNode.addError("@Builder(extensible=true) is only supported for type builders."); return; } ConstructorDeclaration cd = (ConstructorDeclaration) parent.get(); @@ -239,7 +269,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } tdParent = parent.up(); - TypeDeclaration td = (TypeDeclaration) tdParent.get(); + td = (TypeDeclaration) tdParent.get(); returnType = namePlusTypeParamsToTypeReference(td.name, td.typeParameters, p); typeParams = td.typeParameters; thrownExceptions = cd.thrownExceptions; @@ -250,7 +280,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } - if (extendable) { + if (extensible) { annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); return; } @@ -376,6 +406,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { bfd.name = arg.name; bfd.type = arg.type; bfd.singularData = getSingularData(param, ast); + bfd.originalFieldNode = param; addObtainVia(bfd, param); builderFields.add(bfd); } @@ -383,7 +414,20 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { EclipseNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast, inherit ? superclassBuilderClassName : null); + String superclassBuilderClassName = null; + if (inherit) { + if (td.superclass == null) { + annotationNode.addError("@Builder(inherit = true) requires that your class has an 'extends' clause."); + return; + } + + superclassBuilderClassName = builderInstance.superclassBuilderClassName(); + if (superclassBuilderClassName == null || superclassBuilderClassName.isEmpty()) { + superclassBuilderClassName = new String(td.superclass.getLastToken()) + "Builder"; + } + } + + builderType = makeBuilderClass(isStatic, tdParent, builderClassName, typeParams, ast, superclassBuilderClassName); } else { TypeDeclaration builderTypeDeclaration = (TypeDeclaration) builderType.get(); if (isStatic && (builderTypeDeclaration.modifiers & ClassFileConstants.AccStatic) == 0) { @@ -447,8 +491,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - boolean useBuilderBasedConstructor = parent.get() instanceof TypeDeclaration && extendable; - MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, useBuilderBasedConstructor); + MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast, extensible); if (md != null) injectMethod(builderType, md); } @@ -496,8 +539,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 = methodName.toCharArray(); out.modifiers = ClassFileConstants.AccPublic; out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; @@ -556,23 +598,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { */ private void generateBuilderBasedConstructor(EclipseNode typeNode, List<BuilderFieldData> builderFields, EclipseNode sourceNode, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { - - if (builderClassnameAsParameter == null || builderClassnameAsParameter.isEmpty()) { - typeNode.addError("A builder-based constructor requires a non-empty 'builderClassnameAsParameter' value."); - return; - } - - AccessLevel level = AccessLevel.PROTECTED; + ASTNode source = sourceNode.get(); - + TypeDeclaration typeDeclaration = ((TypeDeclaration) typeNode.get()); long p = (long) source.sourceStart << 32 | source.sourceEnd; boolean isEnum = (((TypeDeclaration) typeNode.get()).modifiers & ClassFileConstants.AccEnum) != 0; - if (isEnum) { - level = AccessLevel.PRIVATE; - } - + AccessLevel level = isEnum ? AccessLevel.PRIVATE : AccessLevel.PROTECTED; + ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) typeNode.top().get()).compilationResult); constructor.modifiers = toEclipseModifier(level); @@ -601,7 +635,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { int s = (int) (p >> 32); int e = (int) p; thisX.receiver = new ThisReference(s, e); - + Expression assignmentExpr; if (fieldNode.singularData != null && fieldNode.singularData.getSingularizer() != null) { fieldNode.singularData.getSingularizer().appendBuildCode(fieldNode.singularData, typeNode, statements, fieldNode.name, "b"); @@ -628,7 +662,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { constructor.arguments = new Argument[] {new Argument("b".toCharArray(), p, new SingleTypeReference(builderClassnameAsParameter.toCharArray(), p), Modifier.FINAL)}; constructor.traverse(new SetGeneratedByVisitor(source), typeDeclaration.scope); - + injectMethod(typeNode, constructor); } @@ -660,7 +694,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { * that takes the builder instance as parameter (instead of a * constructor with all relevant fields as parameters) */ - public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source, boolean useBuilderBasedConstructor) { + public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source, boolean useBuilderBasedConstructor) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List<Statement> statements = new ArrayList<Statement>(); @@ -676,17 +710,31 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { invokeClean.selector = CLEAN_METHOD_NAME; statements.add(new IfStatement(notClean, invokeClean, 0, 0)); } - + for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, statements, bfd.name, "this"); } } - + for (BuilderFieldData bfd : builderFields) { - args.add(new SingleNameReference(bfd.name, 0L)); + if (bfd.nameOfSetFlag != null) { + MessageSend inv = new MessageSend(); + inv.sourceStart = source.sourceStart; + inv.sourceEnd = source.sourceEnd; + inv.receiver = new SingleNameReference(((TypeDeclaration) tdParent.get()).name, 0L); + inv.selector = bfd.nameOfDefaultProvider; + inv.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); + + args.add(new ConditionalExpression( + new SingleNameReference(bfd.nameOfSetFlag, 0L), + new SingleNameReference(bfd.name, 0L), + inv)); + } else { + args.add(new SingleNameReference(bfd.name, 0L)); + } } - + if (addCleaning) { FieldReference thisUnclean = new FieldReference(CLEAN_FIELD_NAME, 0); thisUnclean.receiver = new ThisReference(0, 0); @@ -718,14 +766,8 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { invoke.receiver = new SingleNameReference(type.up().getName().toCharArray(), 0); else invoke.receiver = new QualifiedThisReference(new SingleTypeReference(type.up().getName().toCharArray(), 0) , 0, 0); - TypeParameter[] tps = ((TypeDeclaration) type.get()).typeParameters; - if (tps != null) { - TypeReference[] trs = new TypeReference[tps.length]; - for (int i = 0; i < trs.length; i++) { - trs[i] = new SingleTypeReference(tps[i].name, 0); - } - invoke.typeArguments = trs; - } + + invoke.typeArguments = typeParameterNames(((TypeDeclaration) type.get()).typeParameters); invoke.arguments = args.isEmpty() ? null : args.toArray(new Expression[args.size()]); if (returnType instanceof SingleTypeReference && Arrays.equals(TypeConstants.VOID, ((SingleTypeReference) returnType).token)) { statements.add(invoke); @@ -738,6 +780,33 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return out; } + private TypeReference[] typeParameterNames(TypeParameter[] typeParameters) { + if (typeParameters == null) return null; + + TypeReference[] trs = new TypeReference[typeParameters.length]; + for (int i = 0; i < trs.length; i++) { + trs[i] = new SingleTypeReference(typeParameters[i].name, 0); + } + return trs; + } + + public MethodDeclaration generateDefaultProvider(char[] methodName, TypeParameter[] typeParameters, EclipseNode fieldNode, ASTNode source) { + int pS = source.sourceStart, pE = source.sourceEnd; + + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); + out.typeParameters = copyTypeParams(typeParameters, source); + out.selector = methodName; + out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic; + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); + out.returnType = copyType(fd.type, source); + out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)}; + fd.initialization = null; + + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope); + return out; + } + public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -763,25 +832,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (child.getKind() == Kind.FIELD) existing.add(child); } - top: for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType)); } else { + EclipseNode field = null, setFlag = null; for (EclipseNode exists : existing) { char[] n = ((FieldDeclaration) exists.get()).name; - if (Arrays.equals(n, bfd.name)) { - bfd.createdFields.add(exists); - continue top; - } + if (Arrays.equals(n, bfd.name)) field = exists; + if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) setFlag = exists; } - FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0); - fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - fd.modifiers = ClassFileConstants.AccPrivate; - fd.type = copyType(bfd.type); - fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); - bfd.createdFields.add(injectFieldAndMarkGenerated(builderType, fd)); + if (field == null) { + FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0); + fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fd.modifiers = ClassFileConstants.AccPrivate; + fd.type = copyType(bfd.type); + fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); + field = injectFieldAndMarkGenerated(builderType, fd); + } + if (setFlag == null && bfd.nameOfSetFlag != null) { + FieldDeclaration fd = new FieldDeclaration(bfd.nameOfSetFlag, 0, 0); + fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fd.modifiers = ClassFileConstants.AccPrivate; + fd.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); + fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); + injectFieldAndMarkGenerated(builderType, fd); + } + bfd.createdFields.add(field); } } } @@ -789,14 +867,15 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { private static final AbstractMethodDeclaration[] EMPTY = {}; public void makeSetterMethodsForBuilder(EclipseNode builderType, BuilderFieldData bfd, EclipseNode sourceNode, boolean fluent, boolean chain) { + boolean deprecate = isFieldDeprecated(bfd.originalFieldNode); if (bfd.singularData == null || bfd.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, bfd.createdFields.get(0), sourceNode, fluent, chain); + makeSimpleSetterMethodForBuilder(builderType, deprecate, bfd.createdFields.get(0), bfd.nameOfSetFlag, sourceNode, fluent, chain); } else { - bfd.singularData.getSingularizer().generateMethods(bfd.singularData, builderType, fluent, chain); + bfd.singularData.getSingularizer().generateMethods(bfd.singularData, deprecate, builderType, fluent, chain); } } - private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, EclipseNode fieldNode, EclipseNode sourceNode, boolean fluent, boolean chain) { + private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean deprecate, EclipseNode fieldNode, char[] nameOfSetFlag, EclipseNode sourceNode, boolean fluent, boolean chain) { TypeDeclaration td = (TypeDeclaration) builderType.get(); AbstractMethodDeclaration[] existing = td.methods; if (existing == null) existing = EMPTY; @@ -812,7 +891,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, + MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, nameOfSetFlag, chain, ClassFileConstants.AccPublic, sourceNode, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList()); injectMethod(builderType, setter); } @@ -835,9 +914,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (isStatic) builder.modifiers |= ClassFileConstants.AccStatic; builder.typeParameters = copyTypeParams(typeParams, source); builder.name = builderClassName.toCharArray(); - if (parentBuilderClassName != null) { - builder.superclass = new SingleTypeReference(parentBuilderClassName.toCharArray(), 0); - } + if (parentBuilderClassName != null) builder.superclass = new SingleTypeReference(parentBuilderClassName.toCharArray(), 0); builder.traverse(new SetGeneratedByVisitor(source), (ClassScope) null); return injectType(tdParent, builder); } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java new file mode 100644 index 00000000..be2b986d --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017-2018 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.eclipse.handlers; + +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.mangosdk.spi.ProviderFor; + +import lombok.Builder; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; + +@ProviderFor(EclipseAnnotationHandler.class) +@HandlerPriority(-1025) //HandleBuilder's level, minus one. +public class HandleBuilderDefault extends EclipseAnnotationHandler<Builder.Default> { + @Override public void handle(AnnotationValues<Builder.Default> annotation, Annotation ast, EclipseNode annotationNode) { + EclipseNode annotatedField = annotationNode.up(); + if (annotatedField.getKind() != Kind.FIELD) return; + EclipseNode classWithAnnotatedField = annotatedField.up(); + if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)) { + annotationNode.addWarning("@Builder.Default requires @Builder on the class for it to mean anything."); + } + } +} diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index b504495d..a2940b88 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2015 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -80,6 +80,8 @@ import org.mangosdk.spi.ProviderFor; public class HandleConstructor { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleNoArgsConstructor extends EclipseAnnotationHandler<NoArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<NoArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.NO_ARGS_CONSTRUCTOR_FLAG_USAGE, "@NoArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -95,12 +97,14 @@ public class HandleConstructor { List<EclipseNode> fields = force ? findFinalFields(typeNode) : Collections.<EclipseNode>emptyList(); List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@NoArgsConstructor(onConstructor", annotationNode); - new HandleConstructor().generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode); + handleConstructor.generateConstructor(typeNode, level, fields, force, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode); } } @ProviderFor(EclipseAnnotationHandler.class) public static class HandleRequiredArgsConstructor extends EclipseAnnotationHandler<RequiredArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE, "@RequiredArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -116,7 +120,7 @@ public class HandleConstructor { List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@RequiredArgsConstructor(onConstructor", annotationNode); - new HandleConstructor().generateConstructor( + handleConstructor.generateConstructor( typeNode, level, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode); } @@ -144,14 +148,17 @@ public class HandleConstructor { } static List<EclipseNode> findAllFields(EclipseNode typeNode) { + return findAllFields(typeNode, false); + } + + static List<EclipseNode> findAllFields(EclipseNode typeNode, boolean evenFinalInitialized) { List<EclipseNode> fields = new ArrayList<EclipseNode>(); for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); if (!filterField(fieldDecl)) continue; - // Skip initialized final fields. - if (((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) continue; + if (!evenFinalInitialized && ((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) continue; fields.add(child); } @@ -160,6 +167,8 @@ public class HandleConstructor { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleAllArgsConstructor extends EclipseAnnotationHandler<AllArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<AllArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.ALL_ARGS_CONSTRUCTOR_FLAG_USAGE, "@AllArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -175,7 +184,7 @@ public class HandleConstructor { List<Annotation> onConstructor = unboxAndRemoveAnnotationParameter(ast, "onConstructor", "@AllArgsConstructor(onConstructor", annotationNode); - new HandleConstructor().generateConstructor( + handleConstructor.generateConstructor( typeNode, level, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, onConstructor, annotationNode); } @@ -290,7 +299,7 @@ public class HandleConstructor { return new Annotation[] { ann }; } - public static ConstructorDeclaration createConstructor( + @SuppressWarnings("deprecation") public static ConstructorDeclaration createConstructor( AccessLevel level, EclipseNode type, Collection<EclipseNode> fields, boolean allToDefault, EclipseNode sourceNode, List<Annotation> onConstructor) { @@ -302,11 +311,13 @@ public class HandleConstructor { if (isEnum) level = AccessLevel.PRIVATE; - boolean suppressConstructorProperties; + boolean addConstructorProperties; if (fields.isEmpty()) { - suppressConstructorProperties = false; + addConstructorProperties = false; } else { - suppressConstructorProperties = Boolean.TRUE.equals(type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + Boolean v = type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES); + addConstructorProperties = v != null ? v.booleanValue() : + Boolean.FALSE.equals(type.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); } ConstructorDeclaration constructor = new ConstructorDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); @@ -347,7 +358,7 @@ public class HandleConstructor { Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN); Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN); if (nonNulls.length != 0) { - Statement nullCheck = generateNullCheck(field, sourceNode); + Statement nullCheck = generateNullCheck(parameter, sourceNode); if (nullCheck != null) nullChecks.add(nullCheck); } parameter.annotations = copyAnnotations(source, nonNulls, nullables); @@ -361,7 +372,7 @@ public class HandleConstructor { /* Generate annotations that must be put on the generated method, and attach them. */ { Annotation[] constructorProperties = null; - if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(type)) { + if (!allToDefault && addConstructorProperties && !isLocalType(type)) { constructorProperties = createConstructorProperties(source, fields); } diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java index 0ff65a47..025ceefd 100644 --- a/src/core/lombok/eclipse/handlers/HandleData.java +++ b/src/core/lombok/eclipse/handlers/HandleData.java @@ -43,6 +43,12 @@ import org.mangosdk.spi.ProviderFor; */ @ProviderFor(EclipseAnnotationHandler.class) public class HandleData extends EclipseAnnotationHandler<Data> { + private HandleGetter handleGetter = new HandleGetter(); + private HandleSetter handleSetter = new HandleSetter(); + private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode(); + private HandleToString handleToString = new HandleToString(); + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<Data> annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data"); @@ -66,11 +72,11 @@ public class HandleData extends EclipseAnnotationHandler<Data> { //for whatever reason, though you can find callers of that one by focusing on the class name itself //and hitting 'find callers'. - new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); - new HandleToString().generateToStringForType(typeNode, annotationNode); - new HandleConstructor().generateRequiredArgsConstructor( + handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleSetter.generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode); + handleToString.generateToStringForType(typeNode, annotationNode); + handleConstructor.generateRequiredArgsConstructor( typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES, Collections.<Annotation>emptyList(), annotationNode); } diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index ceef3d3c..75339f7c 100644 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -118,7 +118,10 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH return; } - generateMethods(typeNode, errorNode, null, null, null, false, FieldAccess.GETTER, new ArrayList<Annotation>()); + Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); + FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; + + generateMethods(typeNode, errorNode, null, null, null, false, access, new ArrayList<Annotation>()); } @Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, EclipseNode annotationNode) { @@ -296,7 +299,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH /* final int PRIME = X; */ { /* Without fields, PRIME isn't used, and that would trigger a 'local variable not used' warning. */ - if (!isEmpty || callSuper) { + if (!isEmpty) { LocalDeclaration primeDecl = new LocalDeclaration(PRIME, pS, pE); setGeneratedBy(primeDecl, source); primeDecl.modifiers |= Modifier.FINAL; @@ -308,26 +311,30 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH } } - /* int result = 1; */ { - LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE); + /*int result = ... */{ + LocalDeclaration resultDecl = new LocalDeclaration(RESULT, pS, pE); setGeneratedBy(resultDecl, source); - resultDecl.initialization = makeIntLiteral("1".toCharArray(), source); + final Expression init; + if (callSuper) { + /* ... super.hashCode(); */ + MessageSend callToSuper = new MessageSend(); + setGeneratedBy(callToSuper, source); + callToSuper.sourceStart = pS; callToSuper.sourceEnd = pE; + callToSuper.receiver = new SuperReference(pS, pE); + setGeneratedBy(callToSuper.receiver, source); + callToSuper.selector = "hashCode".toCharArray(); + init = callToSuper; + } else { + /* ... 1; */ + init = makeIntLiteral("1".toCharArray(), source); + } + resultDecl.initialization = init; resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0); resultDecl.type.sourceStart = pS; resultDecl.type.sourceEnd = pE; setGeneratedBy(resultDecl.type, source); statements.add(resultDecl); } - if (callSuper) { - MessageSend callToSuper = new MessageSend(); - setGeneratedBy(callToSuper, source); - callToSuper.sourceStart = pS; callToSuper.sourceEnd = pE; - callToSuper.receiver = new SuperReference(pS, pE); - setGeneratedBy(callToSuper.receiver, source); - callToSuper.selector = "hashCode".toCharArray(); - statements.add(createResultCalculation(source, callToSuper)); - } - for (EclipseNode field : fields) { TypeReference fType = getFieldType(field, fieldAccess); char[] dollarFieldName = ("$" + field.getName()).toCharArray(); @@ -468,47 +475,62 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH public TypeReference createTypeReference(EclipseNode type, long p, ASTNode source, boolean addWildcards) { int pS = source.sourceStart; int pE = source.sourceEnd; List<String> list = new ArrayList<String>(); + List<Integer> genericsCount = addWildcards ? new ArrayList<Integer>() : null; + list.add(type.getName()); + if (addWildcards) genericsCount.add(arraySizeOf(((TypeDeclaration) type.get()).typeParameters)); + boolean staticContext = (((TypeDeclaration) type.get()).modifiers & Modifier.STATIC) != 0; EclipseNode tNode = type.up(); + while (tNode != null && tNode.getKind() == Kind.TYPE) { list.add(tNode.getName()); + if (addWildcards) genericsCount.add(staticContext ? 0 : arraySizeOf(((TypeDeclaration) tNode.get()).typeParameters)); + if (!staticContext) staticContext = (((TypeDeclaration) tNode.get()).modifiers & Modifier.STATIC) != 0; tNode = tNode.up(); } Collections.reverse(list); - - TypeDeclaration typeDecl = (TypeDeclaration) type.get(); - int typeParamCount = typeDecl.typeParameters == null ? 0 : typeDecl.typeParameters.length; - if (typeParamCount == 0) addWildcards = false; - TypeReference[] typeArgs = null; - if (addWildcards) { - typeArgs = new TypeReference[typeParamCount]; - for (int i = 0; i < typeParamCount; i++) { - typeArgs[i] = new Wildcard(Wildcard.UNBOUND); - typeArgs[i].sourceStart = pS; typeArgs[i].sourceEnd = pE; - setGeneratedBy(typeArgs[i], source); - } - } + if (addWildcards) Collections.reverse(genericsCount); if (list.size() == 1) { - if (addWildcards) { - return new ParameterizedSingleTypeReference(list.get(0).toCharArray(), typeArgs, 0, p); - } else { + if (!addWildcards || genericsCount.get(0) == 0) { return new SingleTypeReference(list.get(0).toCharArray(), p); + } else { + return new ParameterizedSingleTypeReference(list.get(0).toCharArray(), wildcardify(pS, pE, source, genericsCount.get(0)), 0, p); } } + + if (addWildcards) { + addWildcards = false; + for (int i : genericsCount) if (i > 0) addWildcards = true; + } + long[] ps = new long[list.size()]; char[][] tokens = new char[list.size()][]; for (int i = 0; i < list.size(); i++) { ps[i] = p; tokens[i] = list.get(i).toCharArray(); } - if (addWildcards) { - TypeReference[][] typeArgs2 = new TypeReference[tokens.length][]; - typeArgs2[typeArgs2.length - 1] = typeArgs; - return new ParameterizedQualifiedTypeReference(tokens, typeArgs2, 0, ps); - } else { - return new QualifiedTypeReference(tokens, ps); + + if (!addWildcards) return new QualifiedTypeReference(tokens, ps); + TypeReference[][] typeArgs2 = new TypeReference[tokens.length][]; + for (int i = 0; i < tokens.length; i++) typeArgs2[i] = wildcardify(pS, pE, source, genericsCount.get(i)); + return new ParameterizedQualifiedTypeReference(tokens, typeArgs2, 0, ps); + } + + private TypeReference[] wildcardify(int pS, int pE, ASTNode source, int count) { + if (count == 0) return null; + TypeReference[] typeArgs = new TypeReference[count]; + for (int i = 0; i < count; i++) { + typeArgs[i] = new Wildcard(Wildcard.UNBOUND); + typeArgs[i].sourceStart = pS; typeArgs[i].sourceEnd = pE; + setGeneratedBy(typeArgs[i], source); } + + return typeArgs; + } + + private int arraySizeOf(Object[] arr) { + return arr == null ? 0 : arr.length; } public MethodDeclaration createEquals(EclipseNode type, Collection<EclipseNode> fields, boolean callSuper, ASTNode source, FieldAccess fieldAccess, boolean needsCanEqual, List<Annotation> onParam) { @@ -676,7 +698,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH } else /* objects */ { /* final java.lang.Object this$fieldName = this.fieldName; */ /* final java.lang.Object other$fieldName = other.fieldName; */ - /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false;; */ + /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false; */ char[] thisDollarFieldName = ("this$" + field.getName()).toCharArray(); char[] otherDollarFieldName = ("other$" + field.getName()).toCharArray(); diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index c11303f3..f417aca5 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -105,7 +105,7 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { return true; } - public boolean fieldQualifiesForGetterGeneration(EclipseNode field) { + public static boolean fieldQualifiesForGetterGeneration(EclipseNode field) { if (field.getKind() != Kind.FIELD) return false; FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); return filterField(fieldDecl); diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index 7e7ea121..6a9a5123 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -52,6 +52,7 @@ import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.ThisReference; +import org.eclipse.jdt.internal.compiler.ast.TrueLiteral; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; @@ -192,11 +193,11 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { } } - MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), fieldNode, setterName, shouldReturnThis, modifier, sourceNode, onMethod, onParam); + MethodDeclaration method = createSetter((TypeDeclaration) fieldNode.up().get(), false, fieldNode, setterName, null, shouldReturnThis, modifier, sourceNode, onMethod, onParam); injectMethod(fieldNode.up(), method); } - static MethodDeclaration createSetter(TypeDeclaration parent, EclipseNode fieldNode, String name, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) { + static MethodDeclaration createSetter(TypeDeclaration parent, boolean deprecate, EclipseNode fieldNode, String name, char[] booleanFieldToSet, boolean shouldReturnThis, int modifier, EclipseNode sourceNode, List<Annotation> onMethod, List<Annotation> onParam) { FieldDeclaration field = (FieldDeclaration) fieldNode.get(); ASTNode source = sourceNode.get(); int pS = source.sourceStart, pE = source.sourceEnd; @@ -213,7 +214,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { shouldReturnThis = false; } Annotation[] deprecated = null; - if (isFieldDeprecated(fieldNode)) { + if (isFieldDeprecated(fieldNode) || deprecate) { deprecated = new Annotation[] { generateDeprecatedAnnotation(source) }; } method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated); @@ -243,6 +244,10 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { statements.add(assignment); } + if (booleanFieldToSet != null) { + statements.add(new Assignment(new SingleNameReference(booleanFieldToSet, p), new TrueLiteral(pS, pE), pE)); + } + if (shouldReturnThis) { ThisReference thisRef = new ThisReference(pS, pE); ReturnStatement returnThis = new ReturnStatement(thisRef, pS, pE); diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index a4ed254a..d8f4c569 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -94,7 +94,11 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); } catch (Exception ignore) {} - generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER); + + Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); + FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; + + generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, access); } public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) { diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index d8901067..3742ac00 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2016 The Project Lombok Authors. + * Copyright (C) 2010-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,13 +25,14 @@ import static lombok.core.handlers.HandlerUtil.handleFlagUsage; import static lombok.eclipse.handlers.EclipseHandlerUtil.typeMatches; import lombok.ConfigurationKeys; import lombok.val; +import lombok.var; import lombok.core.HandlerPriority; import lombok.eclipse.DeferUntilPostDiet; import lombok.eclipse.EclipseASTAdapter; import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseNode; -import lombok.experimental.var; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; import org.eclipse.jdt.internal.compiler.ast.ForStatement; import org.eclipse.jdt.internal.compiler.ast.ForeachStatement; @@ -74,11 +75,18 @@ public class HandleVal extends EclipseASTAdapter { return; } - if (isVal && localNode.directUp().get() instanceof ForStatement) { + ASTNode parentRaw = localNode.directUp().get(); + + if (isVal && parentRaw instanceof ForStatement) { localNode.addError("'val' is not allowed in old-style for loops"); return; } + if (parentRaw instanceof ForStatement && ((ForStatement) parentRaw).initializations != null && ((ForStatement) parentRaw).initializations.length > 1) { + localNode.addError("'var' is not allowed in old-style for loops if there is more than 1 initializer"); + return; + } + if (local.initialization != null && local.initialization.getClass().getName().equals("org.eclipse.jdt.internal.compiler.ast.LambdaExpression")) { localNode.addError("'" + annotation + "' is not allowed with lambda expressions."); return; diff --git a/src/core/lombok/eclipse/handlers/HandleValue.java b/src/core/lombok/eclipse/handlers/HandleValue.java index 79c11771..a61ca6c3 100644 --- a/src/core/lombok/eclipse/handlers/HandleValue.java +++ b/src/core/lombok/eclipse/handlers/HandleValue.java @@ -47,6 +47,12 @@ import org.mangosdk.spi.ProviderFor; @ProviderFor(EclipseAnnotationHandler.class) @HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier. public class HandleValue extends EclipseAnnotationHandler<Value> { + private HandleFieldDefaults handleFieldDefaults = new HandleFieldDefaults(); + private HandleGetter handleGetter = new HandleGetter(); + private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode(); + private HandleToString handleToString = new HandleToString(); + private HandleConstructor handleConstructor = new HandleConstructor(); + public void handle(AnnotationValues<Value> annotation, Annotation ast, EclipseNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.VALUE_FLAG_USAGE, "@Value"); @@ -72,7 +78,7 @@ public class HandleValue extends EclipseAnnotationHandler<Value> { } } - new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true); + handleFieldDefaults.generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true); //Careful: Generate the public static constructor (if there is one) LAST, so that any attempt to //'find callers' on the annotation node will find callers of the constructor, which is by far the @@ -80,10 +86,10 @@ public class HandleValue extends EclipseAnnotationHandler<Value> { //for whatever reason, though you can find callers of that one by focusing on the class name itself //and hitting 'find callers'. - new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); - new HandleToString().generateToStringForType(typeNode, annotationNode); - new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES, + handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode); + handleToString.generateToStringForType(typeNode, annotationNode); + handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), SkipIfConstructorExists.YES, Collections.<Annotation>emptyList(), annotationNode); } } diff --git a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java index b42761f4..89964914 100644 --- a/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java +++ b/src/core/lombok/eclipse/handlers/SetGeneratedByVisitor.java @@ -369,11 +369,6 @@ public final class SetGeneratedByVisitor extends ASTVisitor { return super.visit(node, scope); } - @Override public boolean visit(ArrayInitializer node, ClassScope scope) { - fixPositions(setGeneratedBy(node, source)); - return super.visit(node, scope); - } - @Override public boolean visit(ArrayQualifiedTypeReference node, BlockScope scope) { fixPositions(setGeneratedBy(node, source)); return super.visit(node, scope); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index d9f4cb73..eb2c9b35 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,6 +35,7 @@ import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.EclipseSingularizer; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; @@ -96,21 +97,21 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) { TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateSingularMethod(returnType, returnStatement, data, builderType, fluent); + generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent); returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generatePluralMethod(returnType, returnStatement, data, builderType, fluent); + generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent); returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateClearMethod(returnType, returnStatement, data, builderType); + generateClearMethod(deprecate, returnType, returnStatement, data, builderType); } - void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { + void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -121,10 +122,12 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { md.selector = HandlerUtil.buildAccessorName("clear", new String(data.getPluralName())).toCharArray(); md.statements = returnStatement != null ? new Statement[] {a, returnStatement} : new Statement[] {a}; md.returnType = returnType; + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + injectMethod(builderType, md); } - void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { + void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { LombokImmutableList<String> suffixes = getArgumentSuffixes(); char[][] names = new char[suffixes.size()][]; for (int i = 0; i < suffixes.size(); i++) { @@ -159,12 +162,13 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { } md.returnType = returnType; md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName(getAddMethodName(), new String(data.getSingularName())).toCharArray(); + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; data.setGeneratedByRecursive(md); injectMethod(builderType, md); } - void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { + void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -190,6 +194,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer { md.arguments = new Argument[] {param}; md.returnType = returnType; md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName(getAddMethodName() + "All", new String(data.getPluralName())).toCharArray(); + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; data.setGeneratedByRecursive(md); injectMethod(builderType, md); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java index 2d8083d3..cfa48eaf 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilListSetSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,6 +32,7 @@ import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseSingularsRecipes.SingularData; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.EqualExpression; @@ -87,26 +88,26 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, builderType, fluent, chain); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, fluent, chain); return; } TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateSingularMethod(returnType, returnStatement, data, builderType, fluent); + generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent); returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generatePluralMethod(returnType, returnStatement, data, builderType, fluent); + generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent); returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateClearMethod(returnType, returnStatement, data, builderType); + generateClearMethod(deprecate, returnType, returnStatement, data, builderType); } - private void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { + private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -122,10 +123,11 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsg, 0, 0); md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement}; md.returnType = returnType; + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; injectMethod(builderType, md); } - void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { + void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -148,12 +150,13 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula md.arguments = new Argument[] {param}; md.returnType = returnType; md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("add", new String(data.getSingularName())).toCharArray(); + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; data.setGeneratedByRecursive(md); injectMethod(builderType, md); } - void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { + void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -178,6 +181,7 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula md.arguments = new Argument[] {param}; md.returnType = returnType; md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("addAll", new String(data.getPluralName())).toCharArray(); + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; data.setGeneratedByRecursive(md); injectMethod(builderType, md); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java index 3d7b05ed..ee0e6409 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilMapSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; @@ -132,26 +133,26 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, EclipseNode builderType, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, EclipseNode builderType, boolean fluent, boolean chain) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, builderType, fluent, chain); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, fluent, chain); return; } TypeReference returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); Statement returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateSingularMethod(returnType, returnStatement, data, builderType, fluent); + generateSingularMethod(deprecate, returnType, returnStatement, data, builderType, fluent); returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generatePluralMethod(returnType, returnStatement, data, builderType, fluent); + generatePluralMethod(deprecate, returnType, returnStatement, data, builderType, fluent); returnType = chain ? cloneSelfType(builderType) : TypeReference.baseTypeReference(TypeIds.T_void, 0); returnStatement = chain ? new ReturnStatement(new ThisReference(0, 0), 0, 0) : null; - generateClearMethod(returnType, returnStatement, data, builderType); + generateClearMethod(deprecate, returnType, returnStatement, data, builderType); } - private void generateClearMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { + private void generateClearMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -178,10 +179,12 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer Statement clearStatement = new IfStatement(new EqualExpression(thisDotField, new NullLiteral(0, 0), OperatorIds.NOT_EQUAL), clearMsgs, 0, 0); md.statements = returnStatement != null ? new Statement[] {clearStatement, returnStatement} : new Statement[] {clearStatement}; md.returnType = returnType; + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; + injectMethod(builderType, md); } - private void generateSingularMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { + private void generateSingularMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -225,12 +228,13 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer md.arguments = new Argument[] {keyParam, valueParam}; md.returnType = returnType; md.selector = fluent ? data.getSingularName() : HandlerUtil.buildAccessorName("put", new String(data.getSingularName())).toCharArray(); + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; data.setGeneratedByRecursive(md); injectMethod(builderType, md); } - private void generatePluralMethod(TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { + private void generatePluralMethod(boolean deprecate, TypeReference returnType, Statement returnStatement, SingularData data, EclipseNode builderType, boolean fluent) { MethodDeclaration md = new MethodDeclaration(((CompilationUnitDeclaration) builderType.top().get()).compilationResult); md.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; md.modifiers = ClassFileConstants.AccPublic; @@ -288,6 +292,7 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer md.arguments = new Argument[] {param}; md.returnType = returnType; md.selector = fluent ? data.getPluralName() : HandlerUtil.buildAccessorName("putAll", new String(data.getPluralName())).toCharArray(); + md.annotations = deprecate ? new Annotation[] { generateDeprecatedAnnotation(data.getSource()) } : null; data.setGeneratedByRecursive(md); injectMethod(builderType, md); diff --git a/src/core/lombok/experimental/Accessors.java b/src/core/lombok/experimental/Accessors.java index 7d9fdc5c..dc9ae4b0 100644 --- a/src/core/lombok/experimental/Accessors.java +++ b/src/core/lombok/experimental/Accessors.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * A container for settings for the generation of getters and setters. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Accessors.html">the project lombok features page for @Accessors</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Accessors">the project lombok features page for @Accessors</a>. * <p> * Using this annotation does nothing by itself; an annotation that makes lombok generate getters and setters, * such as {@link lombok.Setter} or {@link lombok.Data} is also required. @@ -38,15 +38,19 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) public @interface Accessors { /** - * If true, accessors will be named after the field and not include a <code>get</code> or <code>set</code> - * prefix. If true and <code>chain</code> is omitted, <code>chain</code> defaults to <code>true</code>. + * If true, accessors will be named after the field and not include a {@code get} or {@code set} + * prefix. If true and {@code chain} is omitted, {@code chain} defaults to {@code true}. * <strong>default: false</strong> + * + * @return Whether or not to make fluent methods (named {@code fieldName()}, not for example {@code setFieldName}). */ boolean fluent() default false; /** - * If true, setters return <code>this</code> instead of <code>void</code>. - * <strong>default: false</strong>, unless <code>fluent=true</code>, then <strong>default: true</code> + * If true, setters return {@code this} instead of {@code void}. + * <strong>default: false</strong>, unless {@code fluent=true}, then <strong>default: true</strong> + * + * @return Whether or not setters should return themselves (chaining) or {@code void} (no chaining). */ boolean chain() default false; @@ -55,6 +59,8 @@ public @interface Accessors { * Note that a prefix only counts if the next character is NOT a lowercase character or the last * letter of the prefix is not a letter (for instance an underscore). If multiple fields * all turn into the same name when the prefix is stripped, an error will be generated. + * + * @return If you are in the habit of prefixing your fields (for example, you name them {@code fFieldName}, specify such prefixes here). */ String[] prefix() default {}; } diff --git a/src/core/lombok/experimental/Builder.java b/src/core/lombok/experimental/Builder.java deleted file mode 100644 index 4d5e0f67..00000000 --- a/src/core/lombok/experimental/Builder.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2013-2014 The Project Lombok Authors. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package lombok.experimental; - -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class - * that contains a member which is annotated with {@code @Builder}. - * <p> - * If a member is annotated, it must be either a constructor or a method. If a class is annotated, - * then a private constructor is generated with all fields as arguments - * (as if {@code @AllArgsConstructor(AccessLevel.PRIVATE)} is present - * on the class), and it is as if this constructor has been annotated with {@code @Builder} instead. - * <p> - * The effect of {@code @Builder} is that an inner class is generated named <code><strong>T</strong>Builder</code>, - * with a private constructor. Instances of <code><strong>T</strong>Builder</code> are made with the - * method named {@code builder()} which is also generated for you in the class itself (not in the builder class). - * <p> - * The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated - * constructor / method (each field, when annotating a class), which returns the builder itself. - * The builder also has a <code>build()</code> method which returns a completed instance of the original type, - * created by passing all parameters as set via the various other methods in the builder to the constructor - * or method that was annotated with {@code @Builder}. The return type of this method will be the same - * as the relevant class, unless a method has been annotated, in which case it'll be equal to the - * return type of that method. - * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Builder.html">the project lombok features page for @Builder</a>. - * <p> - * <p> - * Before: - * - * <pre> - * @Builder - * class Example { - * private int foo; - * private final String bar; - * } - * </pre> - * - * After: - * - * <pre> - * class Example<T> { - * private T foo; - * private final String bar; - * - * private Example(T foo, String bar) { - * this.foo = foo; - * this.bar = bar; - * } - * - * public static <T> ExampleBuilder<T> builder() { - * return new ExampleBuilder<T>(); - * } - * - * public static class ExampleBuilder<T> { - * private T foo; - * private String bar; - * - * private ExampleBuilder() {} - * - * public ExampleBuilder foo(T foo) { - * this.foo = foo; - * return this; - * } - * - * public ExampleBuilder bar(String bar) { - * this.bar = bar; - * return this; - * } - * - * @java.lang.Override public String toString() { - * return "ExampleBuilder(foo = " + foo + ", bar = " + bar + ")"; - * } - * - * public Example build() { - * return new Example(foo, bar); - * } - * } - * } - * </pre> - * - * @deprecated {@link lombok.Builder} has been promoted to the main package, so use that one instead. - */ -@Target({TYPE, METHOD, CONSTRUCTOR}) -@Retention(SOURCE) -@Deprecated -public @interface Builder { - /** Name of the method that creates a new builder instance. Default: {@code builder}. */ - String builderMethodName() default "builder"; - - /** Name of the method in the builder class that creates an instance of your {@code @Builder}-annotated class. */ - String buildMethodName() default "build"; - - /** Name of the builder class. - * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}. - * Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}. - */ - String builderClassName() default ""; - - /** - * Normally the builder's 'set' methods are fluent, meaning, they have the same name as the field. Set this - * to {@code false} to name the setter method for field {@code someField}: {@code setSomeField}. - * <p> - * <strong>Default: true</strong> - */ - boolean fluent() default true; - - /** - * Normally the builder's 'set' methods are chaining, meaning, they return the builder so that you can chain - * calls to set methods. Set this to {@code false} to have these 'set' methods return {@code void} instead. - * <p> - * <strong>Default: true</strong> - */ - boolean chain() default true; -} diff --git a/src/core/lombok/experimental/Delegate.java b/src/core/lombok/experimental/Delegate.java index d94389b0..cc844526 100644 --- a/src/core/lombok/experimental/Delegate.java +++ b/src/core/lombok/experimental/Delegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -40,7 +40,7 @@ import java.lang.annotation.Target; * that exist in {@link Object}, the {@code canEqual(Object)} method, and any methods that appear in types * that are listed in the {@code excludes} property. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Delegate.html">the project lombok features page for @Delegate</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Delegate">the project lombok features page for @Delegate</a>. */ @Target({ElementType.FIELD, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) @@ -52,6 +52,8 @@ public @interface Delegate { * type listed here is used only to determine which delegate methods to generate. * * NB: All methods in {@code Object}, as well as {@code canEqual(Object other)} will never be delegated. + * + * @return For each method (not already in {@code java.lang.Object}) in these types, generate a delegate method. */ Class<?>[] types() default {}; @@ -59,6 +61,8 @@ public @interface Delegate { * Each method in any of the types listed here (include supertypes) will <em>not</em> be delegated. * * NB: All methods in {@code Object}, as well as {@code canEqual(Object other)} will never be delegated. + * + * @return For each method (not already in {@code java.lang.Object}) in these types, skip generating a delegate method (overrides {@code types()}). */ Class<?>[] excludes() default {}; } diff --git a/src/core/lombok/experimental/ExtensionMethod.java b/src/core/lombok/experimental/ExtensionMethod.java index 3e64cf78..af38f6ed 100644 --- a/src/core/lombok/experimental/ExtensionMethod.java +++ b/src/core/lombok/experimental/ExtensionMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,8 +31,8 @@ import java.lang.annotation.*; * otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as * if they were instance methods on the extended type. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/ExtensionMethod.html">the project lombok features page for @ExtensionMethod</a>. - * <p> + * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/ExtensionMethod">the project lombok features page for @ExtensionMethod</a>. + * <br> * <p> * Before: * @@ -60,12 +60,14 @@ import java.lang.annotation.*; @Target(TYPE) @Retention(SOURCE) public @interface ExtensionMethod { - /** All types whose static methods will be exposed as extension methods. */ + /** @return All types whose static methods will be exposed as extension methods. */ Class<?>[] value(); /** * If {@code true}, an applicable extension method is used (if found) even if the method call already was compilable (this is the default). * If {@code false}, an extension method is only used if the method call is not also defined by the type itself. + * + * @return Whether or not to override already existing methods with the extension. */ boolean suppressBaseMethods() default true; } diff --git a/src/core/lombok/experimental/FieldDefaults.java b/src/core/lombok/experimental/FieldDefaults.java index 384abda5..5c387bb6 100644 --- a/src/core/lombok/experimental/FieldDefaults.java +++ b/src/core/lombok/experimental/FieldDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,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="https://projectlombok.org/features/experimental/FieldDefaults.html">the project lombok features page for @FieldDefaults</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/FieldDefaults">the project lombok features page for @FieldDefaults</a>. * <p> * If {@code makeFinal} is {@code true}, then each (instance) field that is not annotated with {@code @NonFinal} will have the {@code final} modifier added. * <p> diff --git a/src/core/lombok/experimental/Value.java b/src/core/lombok/experimental/Value.java deleted file mode 100644 index 46ec7a05..00000000 --- a/src/core/lombok/experimental/Value.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2012-2013 The Project Lombok Authors. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package lombok.experimental; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Generates a lot of code which fits with a class that is a representation of an immutable entity. - * <p> - * Equivalent to {@code @Getter @FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) @RequiredArgsConstructor @ToString @EqualsAndHashCode}. - * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Value.html">the project lombok features page for @Value</a>. - * - * @see lombok.Getter - * @see Wither - * @see lombok.RequiredArgsConstructor - * @see lombok.ToString - * @see lombok.EqualsAndHashCode - * @see lombok.Data - * @deprecated {@link lombok.Value} has been promoted to the main package, so use that one instead. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.SOURCE) -@Deprecated -public @interface Value { - /** - * If you specify a static constructor name, then the generated constructor will be private, and - * instead a static factory method is created that other classes can use to create instances. - * We suggest the name: "of", like so: - * - * <pre> - * public @Data(staticConstructor = "of") class Point { final int x, y; } - * </pre> - * - * Default: No static constructor, instead the normal constructor is public. - */ - String staticConstructor() default ""; -} diff --git a/src/core/lombok/experimental/Wither.java b/src/core/lombok/experimental/Wither.java index 3fb767d1..3df546d0 100644 --- a/src/core/lombok/experimental/Wither.java +++ b/src/core/lombok/experimental/Wither.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,7 +31,7 @@ import lombok.AccessLevel; /** * Put on any field to make lombok build a 'wither' - a withX method which produces a clone of this object (except for 1 field which gets a new value). * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Wither.html">the project lombok features page for @Wither</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/experimental/Wither">the project lombok features page for @Wither</a>. * <p> * Even though it is not listed, this annotation also has the {@code onParam} and {@code onMethod} parameter. See the full documentation for more details. * <p> @@ -56,26 +56,32 @@ import lombok.AccessLevel; public @interface Wither { /** * If you want your wither to be non-public, you can specify an alternate access level here. + * + * @return The method will be generated with this access modifier. */ AccessLevel value() default AccessLevel.PUBLIC; /** * Any annotations listed here are put on the generated method. - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @Wither(onMethod=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @Wither(onMethod=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @Wither(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}. + * + * @return List of annotations to apply to the generated method. */ AnyAnnotation[] onMethod() default {}; /** * Any annotations listed here are put on the generated method's parameter. - * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br /> - * up to JDK7:<br /> - * {@code @Wither(onParam=@__({@AnnotationsGoHere}))}<br /> - * from JDK8:<br /> + * The syntax for this feature depends on JDK version (nothing we can do about that; it's to work around javac bugs).<br> + * up to JDK7:<br> + * {@code @Wither(onParam=@__({@AnnotationsGoHere}))}<br> + * from JDK8:<br> * {@code @Wither(onParam_={@AnnotationsGohere})} // note the underscore after {@code onParam}. + * + * @return List of annotations to apply to the generated parameter in the method. */ AnyAnnotation[] onParam() default {}; diff --git a/src/core/lombok/experimental/package-info.java b/src/core/lombok/experimental/package-info.java index d85e2969..22b23a52 100644 --- a/src/core/lombok/experimental/package-info.java +++ b/src/core/lombok/experimental/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,6 +28,6 @@ * to the official feature documentation. * * @see lombok - * @see <a href="https://projectlombok.org/features/experimental/index.html">Lombok features (experimental)</a> + * @see <a href="https://projectlombok.org/features/experimental/all">Lombok features (experimental)</a> */ package lombok.experimental; diff --git a/src/core/lombok/experimental/var.java b/src/core/lombok/experimental/var.java index d8de8b19..71cc141a 100644 --- a/src/core/lombok/experimental/var.java +++ b/src/core/lombok/experimental/var.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,9 @@ package lombok.experimental; /** * like val but not final + * + * @deprecated {@code var} has been promoted to the main package; use {@link lombok.var} instead. */ +@Deprecated public @interface var { } diff --git a/src/core/lombok/extern/apachecommons/CommonsLog.java b/src/core/lombok/extern/apachecommons/CommonsLog.java index 777f4b35..04d5ef93 100644 --- a/src/core/lombok/extern/apachecommons/CommonsLog.java +++ b/src/core/lombok/extern/apachecommons/CommonsLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Causes lombok to generate a logger field. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>. * <p> * Example: * <pre> @@ -46,10 +46,10 @@ import java.lang.annotation.Target; * } * </pre> * - * This annotation is valid for classes and enumerations.<br /> + * This annotation is valid for classes and enumerations.<br> * - * @see org.apache.commons.logging.Log org.apache.commons.logging.Log - * @see org.apache.commons.logging.LogFactory#getLog(java.lang.Class) org.apache.commons.logging.LogFactory.getLog(Class target) + * @see <a href="https://commons.apache.org/proper/commons-logging/apidocs/org/apache/commons/logging/Log.html">org.apache.commons.logging.Log</a> + * @see <a href="https://commons.apache.org/proper/commons-logging/apidocs/org/apache/commons/logging/LogFactory.html#getLog(java.lang.Class)">org.apache.commons.logging.LogFactory#getLog(java.lang.Class)</a> * @see lombok.extern.java.Log @Log * @see lombok.extern.log4j.Log4j @Log4j * @see lombok.extern.log4j.Log4j2 @Log4j2 @@ -60,8 +60,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface CommonsLog { - /** - * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. - */ + /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; } diff --git a/src/core/lombok/extern/java/Log.java b/src/core/lombok/extern/java/Log.java index d5515283..553b7c4a 100644 --- a/src/core/lombok/extern/java/Log.java +++ b/src/core/lombok/extern/java/Log.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Causes lombok to generate a logger field. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>. * <p> * Example: * <pre> @@ -46,9 +46,9 @@ import java.lang.annotation.Target; * } * </pre> * - * This annotation is valid for classes and enumerations.<br /> - * @see java.util.logging.Logger java.util.logging.Logger - * @see java.util.logging.Logger#getLogger(java.lang.String) java.util.logging.Logger.getLogger(String name) + * This annotation is valid for classes and enumerations.<br> + * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html">java.util.logging.Logger</a> + * @see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#getLogger-java.lang.String-">java.util.logging.Logger#getLogger(java.lang.String)</a> * @see lombok.extern.apachecommons.CommonsLog @CommonsLog * @see lombok.extern.log4j.Log4j @Log4j * @see lombok.extern.log4j.Log4j2 @Log4j2 @@ -59,8 +59,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface Log { - /** - * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. - */ + /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; } diff --git a/src/core/lombok/extern/jbosslog/JBossLog.java b/src/core/lombok/extern/jbosslog/JBossLog.java index 2735290c..ea520aea 100644 --- a/src/core/lombok/extern/jbosslog/JBossLog.java +++ b/src/core/lombok/extern/jbosslog/JBossLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Project Lombok Authors. + * Copyright (C) 2016-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Causes lombok to generate a logger field. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>. * <p> * Example: * <pre> @@ -46,9 +46,9 @@ import java.lang.annotation.Target; * } * </pre> * - * This annotation is valid for classes and enumerations.<br /> - * @see org.jboss.logging.Logger org.jboss.logging.Logger - * @see org.jboss.logging.Logger#getLogger(java.lang.Class) org.jboss.logging.Logger.getLogger(Class target) + * This annotation is valid for classes and enumerations.<br> + * @see <a href="https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html">org.jboss.logging.Logger</a> + * @see <a href="https://docs.jboss.org/jbosslogging/latest/org/jboss/logging/Logger.html#getLogger(java.lang.Class)">org.jboss.logging.Logger#getLogger(java.lang.Class)</a> * @see lombok.extern.apachecommons.CommonsLog @CommonsLog * @see lombok.extern.java.Log @Log * @see lombok.extern.log4j.Log4j @Log4j @@ -59,9 +59,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface JBossLog { - - /** - * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. - */ + /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; } diff --git a/src/core/lombok/extern/log4j/Log4j.java b/src/core/lombok/extern/log4j/Log4j.java index 6c6ef1da..ee719407 100644 --- a/src/core/lombok/extern/log4j/Log4j.java +++ b/src/core/lombok/extern/log4j/Log4j.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Causes lombok to generate a logger field. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>. * <p> * Example: * <pre> @@ -46,10 +46,10 @@ import java.lang.annotation.Target; * } * </pre> * - * This annotation is valid for classes and enumerations.<br /> + * This annotation is valid for classes and enumerations.<br> * - * @see org.apache.log4j.Logger org.apache.log4j.Logger - * @see org.apache.log4j.Logger#getLogger(java.lang.Class) org.apache.log4j.Logger.getLogger(Class target) + * @see <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html">org.apache.log4j.Logger</a> + * @see <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html#getLogger(java.lang.Class)">org.apache.log4j.Logger#getLogger(java.lang.Class)</a> * @see lombok.extern.log4j.Log4j2 @Log4j2 * @see lombok.extern.apachecommons.CommonsLog @CommonsLog * @see lombok.extern.java.Log @Log @@ -60,8 +60,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface Log4j { - /** - * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. - */ + /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; } diff --git a/src/core/lombok/extern/log4j/Log4j2.java b/src/core/lombok/extern/log4j/Log4j2.java index a1b2e0e7..4a5b166c 100644 --- a/src/core/lombok/extern/log4j/Log4j2.java +++ b/src/core/lombok/extern/log4j/Log4j2.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Project Lombok Authors. + * Copyright (C) 2013-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Causes lombok to generate a logger field. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>. * <p> * Example: * <pre> @@ -46,10 +46,10 @@ import java.lang.annotation.Target; * } * </pre> * - * This annotation is valid for classes and enumerations.<br /> + * This annotation is valid for classes and enumerations.<br> * - * @see org.apache.logging.log4j.Logger org.apache.logging.log4j.Logger - * @see org.apache.logging.log4j.LogManager#getLogger(java.lang.Class) org.apache.logging.log4j.LogManager.getLogger(Class target) + * @see <a href="https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Logger.html">org.apache.logging.log4j.Logger</a> + * @see <a href="https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogManager.html#getLogger-java.lang.Class-">org.apache.logging.log4j.LogManager#getLogger(java.lang.Class)</a> * @see lombok.extern.log4j.Log4j @Log4j * @see lombok.extern.apachecommons.CommonsLog @CommonsLog * @see lombok.extern.java.Log @Log @@ -60,8 +60,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface Log4j2 { - /** - * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. - */ + /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; } diff --git a/src/core/lombok/extern/slf4j/Slf4j.java b/src/core/lombok/extern/slf4j/Slf4j.java index efa6105f..24586d43 100644 --- a/src/core/lombok/extern/slf4j/Slf4j.java +++ b/src/core/lombok/extern/slf4j/Slf4j.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Causes lombok to generate a logger field. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>. * <p> * Example: * <pre> @@ -46,9 +46,9 @@ import java.lang.annotation.Target; * } * </pre> * - * This annotation is valid for classes and enumerations.<br /> - * @see org.slf4j.Logger org.slf4j.Logger - * @see org.slf4j.LoggerFactory#getLogger(java.lang.Class) org.slf4j.LoggerFactory.getLogger(Class target) + * This annotation is valid for classes and enumerations.<br> + * @see <a href="https://www.slf4j.org/api/org/slf4j/Logger.html">org.slf4j.Logger</a> + * @see <a href="https://www.slf4j.org/api/org/slf4j/LoggerFactory.html#getLogger(java.lang.Class)">org.slf4j.LoggerFactory#getLogger(java.lang.Class)</a> * @see lombok.extern.apachecommons.CommonsLog @CommonsLog * @see lombok.extern.java.Log @Log * @see lombok.extern.log4j.Log4j @Log4j @@ -59,9 +59,7 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface Slf4j { - /** - * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. - */ + /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; } diff --git a/src/core/lombok/extern/slf4j/XSlf4j.java b/src/core/lombok/extern/slf4j/XSlf4j.java index ce23ab9e..85a0fdd8 100644 --- a/src/core/lombok/extern/slf4j/XSlf4j.java +++ b/src/core/lombok/extern/slf4j/XSlf4j.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,7 +29,7 @@ import java.lang.annotation.Target; /** * Causes lombok to generate a logger field. * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/Log.html">the project lombok features page for lombok log annotations</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>. * <p> * Example: * <pre> @@ -46,9 +46,9 @@ import java.lang.annotation.Target; * } * </pre> * - * This annotation is valid for classes and enumerations.<br /> - * @see org.slf4j.ext.XLogger org.slf4j.ext.XLogger - * @see org.slf4j.ext.XLoggerFactory#getLogger(java.lang.Class) org.slf4j.ext.XLoggerFactory.getXLogger(Class target) + * This annotation is valid for classes and enumerations.<br> + * @see <a href="https://www.slf4j.org/api/org/slf4j/ext/XLogger.html">org.slf4j.ext.XLogger</a> + * @see <a href="https://www.slf4j.org/api/org/slf4j/ext/XLoggerFactory.html#getXLogger(java.lang.Class)">org.slf4j.ext.XLoggerFactory#getLogger(java.lang.Class)</a> * @see lombok.extern.apachecommons.CommonsLog @CommonsLog * @see lombok.extern.java.Log @Log * @see lombok.extern.log4j.Log4j @Log4j @@ -59,8 +59,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface XSlf4j { - /** - * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. - */ + /** @return The category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; } diff --git a/src/core/lombok/javac/CapturingDiagnosticListener.java b/src/core/lombok/javac/CapturingDiagnosticListener.java index a0ac6adc..0e64ed8d 100644 --- a/src/core/lombok/javac/CapturingDiagnosticListener.java +++ b/src/core/lombok/javac/CapturingDiagnosticListener.java @@ -52,6 +52,10 @@ public class CapturingDiagnosticListener implements DiagnosticListener<JavaFileO "^" + Pattern.quote(file.getAbsolutePath()) + "\\s*:\\s*\\d+\\s*:\\s*(?:warning:\\s*)?(.*)$", Pattern.DOTALL).matcher(msg); if (m.matches()) msg = m.group(1); + if (msg.equals("deprecated item is not annotated with @Deprecated")) { + // This is new in JDK9; prior to that you don't see this. We shall ignore these. + return; + } messages.add(new CompilerMessage(d.getLineNumber(), d.getStartPosition(), d.getKind() == Kind.ERROR, msg)); } diff --git a/src/core/lombok/javac/CompilerMessageSuppressor.java b/src/core/lombok/javac/CompilerMessageSuppressor.java index a17e0c62..391ec64a 100644 --- a/src/core/lombok/javac/CompilerMessageSuppressor.java +++ b/src/core/lombok/javac/CompilerMessageSuppressor.java @@ -26,6 +26,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.lang.reflect.Field; import java.util.LinkedList; +import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -43,38 +44,40 @@ import com.sun.tools.javac.util.Log; * then they will be generated AGAIN, this time with proper names and line numbers, at the end. Therefore, we want to suppress the logger. */ public final class CompilerMessageSuppressor { + private final Log log; - private static final Field errWriterField, warnWriterField, noticeWriterField, dumpOnErrorField, promptOnErrorField, diagnosticListenerField; + private static final WriterField errWriterField, warnWriterField, noticeWriterField; + private static final Field dumpOnErrorField, promptOnErrorField, diagnosticListenerField; private static final Field deferDiagnosticsField, deferredDiagnosticsField, diagnosticHandlerField; private static final ConcurrentMap<Class<?>, Field> handlerDeferredFields = new ConcurrentHashMap<Class<?>, Field>(); private static final Field NULL_FIELD; - private PrintWriter errWriter, warnWriter, noticeWriter; private Boolean dumpOnError, promptOnError; private DiagnosticListener<?> contextDiagnosticListener, logDiagnosticListener; private final Context context; - // If this is true, the fields changed. Better to print weird error messages than to fail outright. - private static final boolean dontBother; - private static final ThreadLocal<Queue<?>> queueCache = new ThreadLocal<Queue<?>>(); + enum Writers { + ERROR("errWriter", "ERROR"), + WARNING("warnWriter", "WARNING"), + NOTICE("noticeWriter", "NOTICE"); + + final String fieldName; + final String keyName; + + Writers(String fieldName, String keyName) { + this.fieldName = fieldName; + this.keyName = keyName; + } + } + static { - errWriterField = getDeclaredField(Log.class, "errWriter"); - warnWriterField = getDeclaredField(Log.class, "warnWriter"); - noticeWriterField = getDeclaredField(Log.class, "noticeWriter"); + errWriterField = createWriterField(Writers.ERROR); + warnWriterField = createWriterField(Writers.WARNING); + noticeWriterField = createWriterField(Writers.NOTICE); dumpOnErrorField = getDeclaredField(Log.class, "dumpOnError"); promptOnErrorField = getDeclaredField(Log.class, "promptOnError"); diagnosticListenerField = getDeclaredField(Log.class, "diagListener"); - - dontBother = - errWriterField == null || - warnWriterField == null || - noticeWriterField == null || - dumpOnErrorField == null || - promptOnErrorField == null || - diagnosticListenerField == null; - - deferDiagnosticsField = getDeclaredField(Log.class, "deferDiagnostics"); deferredDiagnosticsField = getDeclaredField(Log.class, "deferredDiagnostics"); @@ -100,17 +103,13 @@ public final class CompilerMessageSuppressor { this.context = context; } - public boolean disableLoggers() { + public void disableLoggers() { contextDiagnosticListener = context.get(DiagnosticListener.class); context.put(DiagnosticListener.class, (DiagnosticListener<?>) null); - if (dontBother) return false; - boolean dontBotherInstance = false; - - PrintWriter dummyWriter = new PrintWriter(new OutputStream() { - @Override public void write(int b) throws IOException { - // Do nothing on purpose - } - }); + + errWriterField.pauze(log); + warnWriterField.pauze(log); + noticeWriterField.pauze(log); if (deferDiagnosticsField != null) try { if (Boolean.TRUE.equals(deferDiagnosticsField.get(log))) { @@ -130,50 +129,23 @@ public final class CompilerMessageSuppressor { } } catch (Exception e) {} - if (!dontBotherInstance) try { - errWriter = (PrintWriter) errWriterField.get(log); - errWriterField.set(log, dummyWriter); - } catch (Exception e) { - dontBotherInstance = true; - } - - if (!dontBotherInstance) try { - warnWriter = (PrintWriter) warnWriterField.get(log); - warnWriterField.set(log, dummyWriter); - } catch (Exception e) { - dontBotherInstance = true; - } - - if (!dontBotherInstance) try { - noticeWriter = (PrintWriter) noticeWriterField.get(log); - noticeWriterField.set(log, dummyWriter); - } catch (Exception e) { - dontBotherInstance = true; - } - - if (!dontBotherInstance) try { + if (dumpOnErrorField != null) try { dumpOnError = (Boolean) dumpOnErrorField.get(log); dumpOnErrorField.set(log, false); } catch (Exception e) { - dontBotherInstance = true; } - if (!dontBotherInstance) try { + if (promptOnErrorField != null) try { promptOnError = (Boolean) promptOnErrorField.get(log); promptOnErrorField.set(log, false); } catch (Exception e) { - dontBotherInstance = true; } - if (!dontBotherInstance) try { + if (diagnosticListenerField != null) try { logDiagnosticListener = (DiagnosticListener<?>) diagnosticListenerField.get(log); diagnosticListenerField.set(log, null); } catch (Exception e) { - dontBotherInstance = true; } - - if (dontBotherInstance) enableLoggers(); - return !dontBotherInstance; } private static Field getDeferredField(Object handler) { @@ -193,20 +165,9 @@ public final class CompilerMessageSuppressor { contextDiagnosticListener = null; } - if (errWriter != null) try { - errWriterField.set(log, errWriter); - errWriter = null; - } catch (Exception e) {} - - if (warnWriter != null) try { - warnWriterField.set(log, warnWriter); - warnWriter = null; - } catch (Exception e) {} - - if (noticeWriter != null) try { - noticeWriterField.set(log, noticeWriter); - noticeWriter = null; - } catch (Exception e) {} + errWriterField.resume(log); + warnWriterField.resume(log); + noticeWriterField.resume(log); if (dumpOnError != null) try { dumpOnErrorField.set(log, dumpOnError); @@ -283,4 +244,107 @@ public final class CompilerMessageSuppressor { // javac will contain rather a lot of messages, but this is a lot better than just crashing during compilation! } } + + private static WriterField createWriterField(Writers w) { + // jdk9 + try { + Field writers = getDeclaredField(Log.class, "writer"); + if (writers != null) { + Class<?> kindsClass = Class.forName("com.sun.tools.javac.util.Log$WriterKind"); + for (Object enumConstant : kindsClass.getEnumConstants()) { + if (enumConstant.toString().equals(w.keyName)) { + return new Java9WriterField(writers, enumConstant); + } + } + return WriterField.NONE; + } + } catch (Exception e) { + } + + // jdk8 + Field writerField = getDeclaredField(Log.class, w.fieldName); + if (writerField != null) return new Java8WriterField(writerField); + + // other jdk + return WriterField.NONE; + } + + interface WriterField { + final PrintWriter NO_WRITER = new PrintWriter(new OutputStream() { + @Override public void write(int b) throws IOException { + // Do nothing on purpose + } + }); + + final WriterField NONE = new WriterField() { + @Override public void pauze(Log log) { + // do nothing + } + @Override public void resume(Log log) { + // no nothing + } + }; + + void pauze(Log log); + void resume(Log log); + } + + static class Java8WriterField implements WriterField { + private final Field field; + private PrintWriter writer; + + public Java8WriterField(Field field) { + this.field = field; + } + + @Override public void pauze(Log log) { + try { + writer = (PrintWriter) field.get(log); + field.set(log, NO_WRITER); + } catch (Exception e) { + } + } + + @Override public void resume(Log log) { + if (writer != null) { + try { + field.set(log, writer); + } catch (Exception e) { + } + } + writer = null; + } + } + + + static class Java9WriterField implements WriterField { + private final Field field; + private final Object key; + private PrintWriter writer; + + public Java9WriterField(Field field, Object key) { + this.field = field; + this.key = key; + } + + @Override public void pauze(Log log) { + try { + @SuppressWarnings("unchecked") Map<Object,PrintWriter> map = (Map<Object,PrintWriter>)field.get(log); + writer = map.get(key); + map.put(key, NO_WRITER); + } catch (Exception e) { + } + } + + @Override public void resume(Log log) { + if (writer != null) { + try { + @SuppressWarnings("unchecked") Map<Object,PrintWriter> map = (Map<Object,PrintWriter>)field.get(log); + map.put(key, writer); + } catch (Exception e) { + } + } + writer = null; + } + } }
\ No newline at end of file diff --git a/src/core/lombok/javac/Javac8BasedLombokOptions.java b/src/core/lombok/javac/Javac8BasedLombokOptions.java index 3fdea890..9a662490 100644 --- a/src/core/lombok/javac/Javac8BasedLombokOptions.java +++ b/src/core/lombok/javac/Javac8BasedLombokOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Project Lombok Authors. + * Copyright (C) 2013-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,7 @@ import com.sun.tools.javac.util.Options; public class Javac8BasedLombokOptions extends LombokOptions { public static Javac8BasedLombokOptions replaceWithDelombokOptions(Context context) { Options options = Options.instance(context); - context.put(optionsKey, (Options)null); + context.put(optionsKey, (Options) null); Javac8BasedLombokOptions result = new Javac8BasedLombokOptions(context); result.putAll(options); return result; diff --git a/src/core/lombok/javac/Javac9BasedLombokOptions.java b/src/core/lombok/javac/Javac9BasedLombokOptions.java new file mode 100644 index 00000000..e786346d --- /dev/null +++ b/src/core/lombok/javac/Javac9BasedLombokOptions.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac; + +import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Options; + +public class Javac9BasedLombokOptions extends LombokOptions { + public static Javac9BasedLombokOptions replaceWithDelombokOptions(Context context) { + Options options = Options.instance(context); + context.put(optionsKey, (Options) null); + Javac9BasedLombokOptions result = new Javac9BasedLombokOptions(context); + result.putAll(options); + return result; + } + + private Javac9BasedLombokOptions(Context context) { + super(context); + } + + @Override public void putJavacOption(String optionName, String value) { + if (optionName.equals("CLASSPATH")) optionName = "CLASS_PATH"; + if (optionName.equals("SOURCEPATH")) optionName = "SOURCE_PATH"; + if (optionName.equals("BOOTCLASSPATH")) optionName = "BOOT_CLASS_PATH"; + String optionText = Option.valueOf(optionName).primaryName; + put(optionText, value); + } +} diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index c99ae5c9..45679fd3 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -48,8 +48,6 @@ import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCExpression; -import com.sun.tools.javac.tree.JCTree.JCFieldAccess; -import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCTry; @@ -65,12 +63,12 @@ import com.sun.tools.javac.util.Name; * something javac's own AST system does not offer. */ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { - private final Messager messager; private final JavacElements elements; private final JavacTreeMaker treeMaker; private final Symtab symtab; private final JavacTypes javacTypes; private final Log log; + private final ErrorLog errorLogger; private final Context context; /** @@ -84,8 +82,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { super(sourceName(top), PackageName.getPackageName(top), new JavacImportList(top), statementTypes()); setTop(buildCompilationUnit(top)); this.context = context; - this.messager = messager; this.log = Log.instance(context); + this.errorLogger = ErrorLog.create(messager, log); this.elements = JavacElements.instance(context); this.treeMaker = new JavacTreeMaker(TreeMaker.instance(context)); this.symtab = Symtab.instance(context); @@ -106,27 +104,6 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { return cu.sourcefile == null ? null : cu.sourcefile.toString(); } - // jdk9 support, types have changed, names stay the same - static class PackageName { - private static final Method packageNameMethod; - - static { - Method m = null; - try { - m = JCCompilationUnit.class.getDeclaredMethod("getPackageName"); - } catch (Exception e) {} - packageNameMethod = m; - } - - static String getPackageName(JCCompilationUnit cu) { - try { - Object pkg = packageNameMethod.invoke(cu); - return (pkg instanceof JCFieldAccess || pkg instanceof JCIdent) ? pkg.toString() : null; - } catch (Exception e) {} - return null; - } - } - public Context getContext() { return context; } @@ -148,6 +125,8 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { String nm = Source.instance(context).name(); int underscoreIdx = nm.indexOf('_'); if (underscoreIdx > -1) return Integer.parseInt(nm.substring(underscoreIdx + 1)); + // assume java9+ + return Integer.parseInt(nm); } catch (Exception ignore) {} return 6; } @@ -422,22 +401,21 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { try { switch (kind) { case ERROR: - increaseErrorCount(messager); - boolean prev = log.multipleErrors; - log.multipleErrors = true; - try { - log.error(pos, "proc.messager", message); - } finally { - log.multipleErrors = prev; - } + errorLogger.error(pos, message); + break; + case MANDATORY_WARNING: + errorLogger.mandatoryWarning(pos, message); break; - default: case WARNING: - log.warning(pos, "proc.messager", message); + errorLogger.warning(pos, message); + break; + default: + case NOTE: + errorLogger.note(pos, message); break; } } finally { - if (oldSource != null) log.useSource(oldSource); + if (newSource != null) log.useSource(oldSource); } } @@ -475,16 +453,118 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { return oldL; } - private void increaseErrorCount(Messager m) { - try { - Field f = m.getClass().getDeclaredField("errorCount"); - f.setAccessible(true); - if (f.getType() == int.class) { - int val = ((Number)f.get(m)).intValue(); - f.set(m, val +1); + abstract static class ErrorLog { + final Log log; + private final Messager messager; + private final Field errorCount; + private final Field warningCount; + + private ErrorLog(Log log, Messager messager, Field errorCount, Field warningCount) { + this.log = log; + this.messager = messager; + this.errorCount = errorCount; + this.warningCount = warningCount; + } + + final void error(DiagnosticPosition pos, String message) { + increment(errorCount); + error1(pos, message); + } + + final void warning(DiagnosticPosition pos, String message) { + increment(warningCount); + log.warning(pos, "proc.messager", message); + } + + final void mandatoryWarning(DiagnosticPosition pos, String message) { + increment(warningCount); + log.mandatoryWarning(pos, "proc.messager", message); + } + + final void note(DiagnosticPosition pos, String message) { + log.note(pos, "proc.messager", message); + } + + abstract void error1(DiagnosticPosition pos, String message); + + private void increment(Field field) { + if (field == null) return; + try { + int val = ((Number)field.get(messager)).intValue(); + field.set(messager, val +1); + } catch (Throwable t) { + //Very unfortunate, but in most cases it still works fine, so we'll silently swallow it. + } + } + + static ErrorLog create(Messager messager, Log log) { + Field errorCount = null; + try { + Field f = messager.getClass().getDeclaredField("errorCount"); + f.setAccessible(true); + errorCount = f; + } catch (Throwable t) {} + boolean hasMultipleErrors = false; + for (Field field : log.getClass().getFields()) { + if (field.getName().equals("multipleErrors")) { + hasMultipleErrors = true; + break; + } + } + if (hasMultipleErrors) return new JdkBefore9(log, messager, errorCount); + + Field warningCount = null; + try { + Field f = messager.getClass().getDeclaredField("warningCount"); + f.setAccessible(true); + warningCount = f; + } catch (Throwable t) {} + + + Method logMethod = null; + Object multiple = null; + try { + Class<?> df = Class.forName("com.sun.tools.javac.util.JCDiagnostic$DiagnosticFlag"); + for (Object constant : df.getEnumConstants()) { + if (constant.toString().equals("MULTIPLE")) multiple = constant; + } + logMethod = log.getClass().getMethod("error", new Class<?>[] {df, DiagnosticPosition.class, String.class, Object[].class}); + } catch (Throwable t) {} + + return new Jdk9Plus(log, messager, errorCount, warningCount, logMethod, multiple); + } + } + + static class JdkBefore9 extends ErrorLog { + private JdkBefore9(Log log, Messager messager, Field errorCount) { + super(log, messager, errorCount, null); + } + + @Override void error1(DiagnosticPosition pos, String message) { + boolean prev = log.multipleErrors; + log.multipleErrors = true; + try { + log.error(pos, "proc.messager", message); + } finally { + log.multipleErrors = prev; } - } catch (Throwable t) { - //Very unfortunate, but in most cases it still works fine, so we'll silently swallow it. + } + } + + static class Jdk9Plus extends ErrorLog { + private final Object multiple; + private final Method logMethod; + + private Jdk9Plus(Log log, Messager messager, Field errorCount, Field warningCount, Method logMethod, Object multiple) { + super(log, messager, errorCount, warningCount); + this.logMethod = logMethod; + this.multiple = multiple; + } + + @Override void error1(DiagnosticPosition pos, String message) { + try { + logMethod.invoke(log, multiple, pos, "proc.messager", new Object[] { message }); + } catch (Throwable t) {} } } } diff --git a/src/core/lombok/javac/JavacImportList.java b/src/core/lombok/javac/JavacImportList.java index 0f789f45..468d8c7b 100644 --- a/src/core/lombok/javac/JavacImportList.java +++ b/src/core/lombok/javac/JavacImportList.java @@ -26,7 +26,6 @@ import java.util.Collection; import lombok.core.ImportList; import lombok.core.LombokInternalAliasing; -import lombok.javac.JavacAST.PackageName; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; diff --git a/src/core/lombok/javac/JavacResolution.java b/src/core/lombok/javac/JavacResolution.java index 67dbaac6..8cc239e1 100644 --- a/src/core/lombok/javac/JavacResolution.java +++ b/src/core/lombok/javac/JavacResolution.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2015 The Project Lombok Authors. + * Copyright (C) 2011-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,6 +31,7 @@ import java.util.ArrayDeque; import java.util.Map; import javax.lang.model.type.TypeKind; +import javax.tools.JavaFileObject; import lombok.Lombok; import lombok.core.debug.AssertionLogger; @@ -59,6 +60,7 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Log; public class JavacResolution { private final Attr attr; @@ -142,9 +144,14 @@ public class JavacResolution { TreeMirrorMaker mirrorMaker = new TreeMirrorMaker(node.getTreeMaker(), node.getContext()); JCTree copy = mirrorMaker.copy(finder.copyAt()); - - memberEnterAndAttribute(copy, finder.get(), node.getContext()); - return mirrorMaker.getOriginalToCopyMap(); + Log log = Log.instance(node.getContext()); + JavaFileObject oldFileObject = log.useSource(((JCCompilationUnit) node.top().get()).getSourceFile()); + try { + memberEnterAndAttribute(copy, finder.get(), node.getContext()); + return mirrorMaker.getOriginalToCopyMap(); + } finally { + log.useSource(oldFileObject); + } } finally { messageSuppressor.enableLoggers(); } @@ -222,8 +229,13 @@ public class JavacResolution { } private void attrib(JCTree tree, Env<AttrContext> env) { + if (env.enclClass.type == null) try { + env.enclClass.type = Type.noType; + } catch (Throwable ignore) { + // This addresses issue #1553 which involves JDK9; if it doesn't exist, we probably don't need to set it. + } if (tree instanceof JCBlock) attr.attribStat(tree, env); - else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl)tree).body, env); + else if (tree instanceof JCMethodDecl) attr.attribStat(((JCMethodDecl) tree).body, env); else if (tree instanceof JCVariableDecl) attr.attribStat(tree, env); else throw new IllegalStateException("Called with something that isn't a block, method decl, or variable decl"); } @@ -261,6 +273,7 @@ public class JavacResolution { } public static Type ifTypeIsIterableToComponent(Type type, JavacAST ast) { + if (type == null) return null; Types types = Types.instance(ast.getContext()); Symtab syms = Symtab.instance(ast.getContext()); Type boundType = ReflectiveAccess.Types_upperBound(types, type); diff --git a/src/core/lombok/javac/apt/InterceptingJavaFileManager.java b/src/core/lombok/javac/apt/InterceptingJavaFileManager.java index 303bdc2f..9b58d111 100644 --- a/src/core/lombok/javac/apt/InterceptingJavaFileManager.java +++ b/src/core/lombok/javac/apt/InterceptingJavaFileManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 The Project Lombok Authors. + * Copyright (C) 2010-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -42,14 +42,14 @@ final class InterceptingJavaFileManager extends ForwardingJavaFileManager<JavaFi } @Override public JavaFileObject getJavaFileForOutput(Location location, String className, final Kind kind, FileObject sibling) throws IOException { - if (className.startsWith("lombok.dummy.ForceNewRound")) { + if (className.contains("lombok.dummy.ForceNewRound")) { final String name = className.replace(".", "/") + kind.extension; return LombokFileObjects.createEmpty(compiler, name, kind); } + JavaFileObject fileObject = fileManager.getJavaFileForOutput(location, className, kind, sibling); - if (kind != Kind.CLASS) { - return fileObject; - } + if (kind != Kind.CLASS) return fileObject; + return LombokFileObjects.createIntercepting(compiler, fileObject, className, diagnostics); } }
\ No newline at end of file diff --git a/src/core/lombok/javac/apt/LombokFileObjects.java b/src/core/lombok/javac/apt/LombokFileObjects.java index 7e818cab..aba10540 100644 --- a/src/core/lombok/javac/apt/LombokFileObjects.java +++ b/src/core/lombok/javac/apt/LombokFileObjects.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2017 The Project Lombok Authors. + * Copyright (C) 2010-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,19 +22,26 @@ package lombok.javac.apt; +import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; +import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import javax.tools.FileObject; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; -import com.sun.tools.javac.file.BaseFileManager; - import lombok.core.DiagnosticsReceiver; +import com.sun.tools.javac.file.BaseFileManager; + //Can't use SimpleJavaFileObject so we copy/paste most of its content here, because javac doesn't follow the interface, //and casts to its own BaseFileObject type. D'oh! final class LombokFileObjects { @@ -94,6 +101,15 @@ final class LombokFileObjects { private LombokFileObjects() {} + private static final List<String> KNOWN_JAVA9_FILE_MANAGERS = Arrays.asList( + "com.google.errorprone.MaskedClassLoader$MaskedFileManager", + "com.google.devtools.build.buildjar.javac.BlazeJavacMain$ClassloaderMaskingFileManager", + "com.google.devtools.build.java.turbine.javac.JavacTurbineCompiler$ClassloaderMaskingFileManager", + "org.netbeans.modules.java.source.parsing.ProxyFileManager", + "com.sun.tools.javac.api.ClientCodeWrapper$WrappedStandardJavaFileManager", + "com.sun.tools.javac.main.DelegatingJavaFileManager$DelegatingSJFM" // IntelliJ + JDK10 + ); + static Compiler getCompiler(JavaFileManager jfm) { String jfmClassName = jfm != null ? jfm.getClass().getName() : "null"; if (jfmClassName.equals("com.sun.tools.javac.util.DefaultFileManager")) return Compiler.JAVAC6; @@ -105,18 +121,36 @@ final class LombokFileObjects { return new Java9Compiler(jfm); } } - catch (Exception e) {} + catch (Throwable e) {} return Compiler.JAVAC7; } + if (KNOWN_JAVA9_FILE_MANAGERS.contains(jfmClassName)) { + try { + return new Java9Compiler(jfm); + } + catch (Throwable e) {} + } + try { + if (Class.forName("com.sun.tools.javac.file.PathFileObject") == null) throw new NullPointerException(); + return new Java9Compiler(jfm); + } catch (Throwable e) {} try { if (Class.forName("com.sun.tools.javac.file.BaseFileObject") == null) throw new NullPointerException(); return Compiler.JAVAC7; - } catch (Exception e) {} + } catch (Throwable e) {} try { if (Class.forName("com.sun.tools.javac.util.BaseFileObject") == null) throw new NullPointerException(); return Compiler.JAVAC6; - } catch (Exception e) {} - return null; + } catch (Throwable e) {} + + StringBuilder sb = new StringBuilder(jfmClassName); + if (jfm != null) { + sb.append(" extends ").append(jfm.getClass().getSuperclass().getName()); + for (Class<?> cls : jfm.getClass().getInterfaces()) { + sb.append(" implements ").append(cls.getName()); + } + } + throw new IllegalArgumentException(sb.toString()); } static JavaFileObject createEmpty(Compiler compiler, String name, Kind kind) { @@ -131,19 +165,108 @@ final class LombokFileObjects { private final BaseFileManager fileManager; public Java9Compiler(JavaFileManager jfm) { - fileManager = (BaseFileManager) jfm; + fileManager = asBaseFileManager(jfm); } @Override public JavaFileObject wrap(LombokFileObject fileObject) { + return new Javac9BaseFileObjectWrapper(fileManager, toPath(fileObject), fileObject); + } + + @Override public Method getDecoderMethod() { + return null; + } + + private static Path toPath(LombokFileObject fileObject) { URI uri = fileObject.toUri(); if (uri.getScheme() == null) { - uri = URI.create("file://" + uri); + uri = URI.create("file:///" + uri); + } + try { + return Paths.get(uri); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Problems in URI '" + uri + "' (" + fileObject.toUri() + ")", e); } - return new Javac9BaseFileObjectWrapper(fileManager, Paths.get(uri), fileObject); } - @Override public Method getDecoderMethod() { - throw new UnsupportedOperationException(); + private static BaseFileManager asBaseFileManager(JavaFileManager jfm) { + if (jfm instanceof BaseFileManager) { + return (BaseFileManager) jfm; + } + return new FileManagerWrapper(jfm); + } + + static class FileManagerWrapper extends BaseFileManager { + JavaFileManager manager; + + public FileManagerWrapper(JavaFileManager manager) { + super(null); + this.manager = manager; + } + + @Override + public int isSupportedOption(String option) { + return manager.isSupportedOption(option); + } + + @Override + public ClassLoader getClassLoader(Location location) { + return manager.getClassLoader(location); + } + + @Override + public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException { + return manager.list(location, packageName, kinds, recurse); + } + + @Override + public String inferBinaryName(Location location, JavaFileObject file) { + return manager.inferBinaryName(location, file); + } + + @Override + public boolean isSameFile(FileObject a, FileObject b) { + return manager.isSameFile(a, b); + } + + @Override + public boolean handleOption(String current, Iterator<String> remaining) { + return manager.handleOption(current, remaining); + } + + @Override + public boolean hasLocation(Location location) { + return manager.hasLocation(location); + } + + @Override + public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException { + return manager.getJavaFileForInput(location, className, kind); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { + return manager.getJavaFileForOutput(location, className, kind, sibling); + } + + @Override + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { + return manager.getFileForInput(location, packageName, relativeName); + } + + @Override + public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException { + return manager.getFileForOutput(location, packageName, relativeName, sibling); + } + + @Override + public void flush() throws IOException { + manager.flush(); + } + + @Override + public void close() throws IOException { + manager.close(); + } } } } diff --git a/src/core/lombok/javac/apt/LombokProcessor.java b/src/core/lombok/javac/apt/LombokProcessor.java index b962a955..9c0a2dfa 100644 --- a/src/core/lombok/javac/apt/LombokProcessor.java +++ b/src/core/lombok/javac/apt/LombokProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -54,6 +54,8 @@ import lombok.javac.JavacTransformer; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; +import com.sun.tools.javac.jvm.ClassWriter; +import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.processing.JavacFiler; import com.sun.tools.javac.processing.JavacProcessingEnvironment; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; @@ -156,7 +158,6 @@ public class LombokProcessor extends AbstractProcessor { @SuppressWarnings("unchecked") Map<Object,Object> ht = (Map<Object,Object>) htField.get(context); final JavaFileManager originalFiler = (JavaFileManager) ht.get(key); - if (!(originalFiler instanceof InterceptingJavaFileManager)) { final Messager messager = processingEnv.getMessager(); DiagnosticsReceiver receiver = new MessagerDiagnosticsReceiver(messager); @@ -166,11 +167,37 @@ public class LombokProcessor extends AbstractProcessor { Field filerFileManagerField = JavacFiler.class.getDeclaredField("fileManager"); filerFileManagerField.setAccessible(true); filerFileManagerField.set(processingEnv.getFiler(), newFiler); + + replaceFileManagerJdk9(context, newFiler); } } catch (Exception e) { throw Lombok.sneakyThrow(e); } } + + private void replaceFileManagerJdk9(Context context, JavaFileManager newFiler) { + try { + JavaCompiler compiler = (JavaCompiler) JavaCompiler.class.getDeclaredMethod("instance", Context.class).invoke(null, context); + try { + Field fileManagerField = JavaCompiler.class.getDeclaredField("fileManager"); + fileManagerField.setAccessible(true); + fileManagerField.set(compiler, newFiler); + } + catch (Exception e) {} + + try { + Field writerField = JavaCompiler.class.getDeclaredField("writer"); + writerField.setAccessible(true); + ClassWriter writer = (ClassWriter) writerField.get(compiler); + Field fileManagerField = ClassWriter.class.getDeclaredField("fileManager"); + fileManagerField.setAccessible(true); + fileManagerField.set(writer, newFiler); + } + catch (Exception e) {} + } + catch (Exception e) { + } + } private void forceMultipleRoundsInNetBeansEditor() { try { @@ -274,7 +301,10 @@ public class LombokProcessor extends AbstractProcessor { // Step 1: Take all CUs which aren't already in the map. Give them the first priority level. + String randomModuleName = null; + for (Element element : roundEnv.getRootElements()) { + if (randomModuleName == null) randomModuleName = getModuleNameFor(element); JCCompilationUnit unit = toUnit(element); if (unit == null) continue; if (roots.containsKey(unit)) continue; @@ -317,20 +347,22 @@ public class LombokProcessor extends AbstractProcessor { if (newLevels.isEmpty()) return false; newLevels.retainAll(priorityLevelsRequiringResolutionReset); - if (!newLevels.isEmpty()){ + if (!newLevels.isEmpty()) { // Force a new round to reset resolution. The next round will cause this method (process) to be called again. - forceNewRound((JavacFiler) processingEnv.getFiler()); + forceNewRound(randomModuleName, (JavacFiler) processingEnv.getFiler()); return false; } - // None of the new levels need resolution, so just keep going. + // None of the new levels need resolution, so just keep going. } } private int dummyCount = 0; - private void forceNewRound(JavacFiler filer) { + private void forceNewRound(String randomModuleName, JavacFiler filer) { if (!filer.newFiles()) { try { - JavaFileObject dummy = filer.createSourceFile("lombok.dummy.ForceNewRound" + (dummyCount++)); + String name = "lombok.dummy.ForceNewRound" + (dummyCount++); + if (randomModuleName != null) name = randomModuleName + "/" + name; + JavaFileObject dummy = filer.createSourceFile(name); Writer w = dummy.openWriter(); w.close(); } catch (Exception e) { @@ -341,6 +373,19 @@ public class LombokProcessor extends AbstractProcessor { } } + private String getModuleNameFor(Element element) { + while (element != null) { + if (element.getKind().name().equals("MODULE")) { + String n = element.getSimpleName().toString().trim(); + return n.isEmpty() ? null : n; + } + Element n = element.getEnclosingElement(); + if (n == element) return null; + element = n; + } + return null; + } + private JCCompilationUnit toUnit(Element element) { TreePath path = trees == null ? null : trees.getPath(element); if (path == null) return null; diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 9860fb07..4c575d91 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2015 The Project Lombok Authors. + * Copyright (C) 2013-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,7 +21,6 @@ */ package lombok.javac.handlers; -import java.lang.annotation.Annotation; import java.util.ArrayList; import javax.lang.model.element.Modifier; @@ -76,6 +75,8 @@ import static lombok.javac.JavacTreeMaker.TypeTag.*; @ProviderFor(JavacAnnotationHandler.class) @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleBuilder extends JavacAnnotationHandler<Builder> { + private HandleConstructor handleConstructor = new HandleConstructor(); + private static final boolean toBoolean(Object expr, boolean defaultValue) { if (expr == null) return defaultValue; if (expr instanceof JCLiteral) return ((Integer) ((JCLiteral) expr).value) != 0; @@ -87,9 +88,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JCExpression type; Name rawName; Name name; + Name nameOfDefaultProvider; + Name nameOfSetFlag; SingularData singularData; ObtainVia obtainVia; JavacNode obtainViaNode; + JavacNode originalFieldNode; java.util.List<JavacNode> createdFields = new ArrayList<JavacNode>(); } @@ -107,18 +111,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { String toBuilderMethodName = "toBuilder"; boolean inherit = builderInstance.inherit(); - boolean extendable = inherit || builderInstance.extendable(); // inherit implies extendable - String superclassBuilderClassName = builderInstance.superclassBuilderClassName(); - + boolean extensible = inherit || builderInstance.extensible(); // inherit implies extendable + boolean toBuilder = builderInstance.toBuilder(); java.util.List<Name> typeArgsForToBuilder = null; if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; if (builderClassName == null) builderClassName = ""; - if (superclassBuilderClassName == null) { - superclassBuilderClassName = ""; - } if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; @@ -126,9 +126,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (!checkName("builderClassName", builderClassName, annotationNode)) return; } - @SuppressWarnings("deprecation") - Class<? extends Annotation> oldExperimentalBuilder = lombok.experimental.Builder.class; - deleteAnnotationIfNeccessary(annotationNode, Builder.class, oldExperimentalBuilder); + deleteAnnotationIfNeccessary(annotationNode, Builder.class, "lombok.experimental.Builder"); JavacNode parent = annotationNode.up(); @@ -143,43 +141,61 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { boolean addCleaning = false; boolean isStatic = true; + JCClassDecl td = null; + if (parent.get() instanceof JCClassDecl) { tdParent = parent; - JCClassDecl td = (JCClassDecl) tdParent.get(); + td = (JCClassDecl) tdParent.get(); ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>(); - @SuppressWarnings("deprecation") - boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent)); - for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) { + boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation("lombok.experimental.Value", parent)); + for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); - // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes - // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves. - // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. - if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; + JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true); + boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); BuilderFieldData bfd = new BuilderFieldData(); bfd.fieldNode = fieldNode; bfd.rawName = fd.name; bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.vartype; bfd.singularData = getSingularData(fieldNode); + bfd.originalFieldNode = fieldNode; + + if (bfd.singularData != null && isDefault != null) { + isDefault.addError("@Builder.Default and @Singular cannot be mixed."); + isDefault = null; + } + + if (fd.init == null && isDefault != null) { + isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); + isDefault = null; + } + + if (fd.init != null && isDefault == null) { + if (isFinal) continue; + fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + + if (isDefault != null) { + bfd.nameOfDefaultProvider = parent.toName("$default$" + bfd.name); + bfd.nameOfSetFlag = parent.toName(bfd.name + "$set"); + JCMethodDecl md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, td.typarams); + recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); + if (md != null) injectMethod(tdParent, md); + } addObtainVia(bfd, fieldNode); builderFields.add(bfd); allFields.append(fieldNode); } - if (builderClassName.isEmpty()) { - builderClassName = td.name.toString() + "Builder"; - } + if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; JCTree extendsClause = Javac.getExtendsClause(td); - if (superclassBuilderClassName.isEmpty() && extendsClause != null) { - superclassBuilderClassName = extendsClause + "Builder"; - } - if (extendable) { + if (extensible) { boolean callBuilderBasedSuperConstructor = extendsClause != null; generateBuilderBasedConstructor(tdParent, builderFields, annotationNode, builderClassName, callBuilderBasedSuperConstructor); } else { - new HandleConstructor().generateConstructor(tdParent, AccessLevel.PROTECTED, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); + handleConstructor.generateConstructor(tdParent, AccessLevel.PROTECTED, List.<JCAnnotation>nil(), allFields.toList(), false, null, SkipIfConstructorExists.I_AM_BUILDER, annotationNode); } returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); @@ -191,7 +207,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } - if (extendable) { + if (extensible) { annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); return; } @@ -202,7 +218,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } tdParent = parent.up(); - JCClassDecl td = (JCClassDecl) tdParent.get(); + td = (JCClassDecl) tdParent.get(); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = jmd.thrown; @@ -213,12 +229,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { annotationNode.addError("@Builder(inherit=true) is only supported for type builders."); return; } - if (extendable) { + if (extensible) { annotationNode.addError("@Builder(extendable=true) is only supported for type builders."); return; } tdParent = parent.up(); - JCClassDecl td = (JCClassDecl) tdParent.get(); + td = (JCClassDecl) tdParent.get(); JCMethodDecl jmd = (JCMethodDecl) fillParametersFrom.get(); isStatic = (jmd.mods.flags & Flags.STATIC) != 0; JCExpression fullReturnType = jmd.restype; @@ -227,7 +243,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { thrownExceptions = jmd.thrown; nameOfBuilderMethod = jmd.name; if (returnType instanceof JCTypeApply) { - returnType = ((JCTypeApply) returnType).clazz; + returnType = cloneType(tdParent.getTreeMaker(), returnType, ast, annotationNode.getContext()); } if (builderClassName.isEmpty()) { if (returnType instanceof JCFieldAccess) { @@ -247,7 +263,16 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (Character.isLowerCase(builderClassName.charAt(0))) { builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1); } - } else { + } else if (returnType instanceof JCTypeApply) { + JCExpression clazz = ((JCTypeApply) returnType).clazz; + if (clazz instanceof JCFieldAccess) { + builderClassName = ((JCFieldAccess) clazz).name + "Builder"; + } else if (clazz instanceof JCIdent) { + builderClassName = ((JCIdent) clazz).name + "Builder"; + } + } + + if (builderClassName.isEmpty()) { // This shouldn't happen. System.err.println("Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass()); builderClassName = td.name.toString() + "Builder"; @@ -268,11 +293,14 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { tpOnRet = ((JCTypeApply) fullReturnType).arguments; } - if (returnType instanceof JCIdent) { - simpleName = ((JCIdent) returnType).name; + JCExpression namingType = returnType; + if (returnType instanceof JCTypeApply) namingType = ((JCTypeApply) returnType).clazz; + + if (namingType instanceof JCIdent) { + simpleName = ((JCIdent) namingType).name; pkg = null; - } else if (returnType instanceof JCFieldAccess) { - JCFieldAccess jcfa = (JCFieldAccess) returnType; + } else if (namingType instanceof JCFieldAccess) { + JCFieldAccess jcfa = (JCFieldAccess) namingType; simpleName = jcfa.name; pkg = unpack(jcfa.selected); if (pkg.startsWith("ERR:")) { @@ -281,7 +309,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { return; } } else { - annotationNode.addError("Expected a (parameterized) type here instead of a " + returnType.getClass().getName()); + annotationNode.addError("Expected a (parameterized) type here instead of a " + namingType.getClass().getName()); return; } @@ -330,6 +358,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { bfd.rawName = raw.name; bfd.type = raw.vartype; bfd.singularData = getSingularData(param); + bfd.originalFieldNode = param; addObtainVia(bfd, param); builderFields.add(bfd); } @@ -337,7 +366,18 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { - builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast, inherit ? superclassBuilderClassName : null); + String superclassBuilderClassName = null; + if (inherit) { + JCTree extendsClause = Javac.getExtendsClause(td); + if (extendsClause == null) { + annotationNode.addError("@Builder(inherit = true) requires that your class has an 'extends' clause."); + return; + } + + superclassBuilderClassName = builderInstance.superclassBuilderClassName(); + if (superclassBuilderClassName == null || superclassBuilderClassName.isEmpty()) superclassBuilderClassName = extendsClause + "Builder"; + } + builderType = makeBuilderClass(isStatic, annotationNode, tdParent, builderClassName, typeParams, ast, superclassBuilderClassName); } else { JCClassDecl builderTypeDeclaration = (JCClassDecl) builderType.get(); if (isStatic && !builderTypeDeclaration.getModifiers().getFlags().contains(Modifier.STATIC)) { @@ -397,8 +437,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - boolean useBuilderBasedConstructor = parent.get() instanceof JCClassDecl && extendable; - JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, useBuilderBasedConstructor); + JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning, extensible); if (md != null) injectMethod(builderType, md); } @@ -528,17 +567,10 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { * {@code builderClassAsParameter != null}. */ private void generateBuilderBasedConstructor(JavacNode typeNode, java.util.List<BuilderFieldData> builderFields, JavacNode source, String builderClassnameAsParameter, boolean callBuilderBasedSuperConstructor) { - if (builderClassnameAsParameter == null || builderClassnameAsParameter.isEmpty()) { - source.addError("A builder-based constructor requires a non-empty 'builderClassnameAsParameter' value."); - } - JavacTreeMaker maker = typeNode.getTreeMaker(); - AccessLevel level = AccessLevel.PROTECTED; boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; - if (isEnum) { - level = AccessLevel.PRIVATE; - } + AccessLevel level = isEnum ? AccessLevel.PRIVATE : AccessLevel.PROTECTED; ListBuffer<JCStatement> nullChecks = new ListBuffer<JCStatement>(); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); @@ -583,11 +615,11 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { List.<JCExpression>of(maker.Ident(builderVariableName))); statements.prepend(maker.Exec(callToSuperConstructor)); } - + JCMethodDecl constr = recursiveSetGeneratedBy(maker.MethodDef(mods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), params.toList(), List.<JCExpression>nil(), maker.Block(0L, nullChecks.appendList(statements).toList()), null), source.get(), typeNode.getContext()); - + injectMethod(typeNode, constr, null, Javac.createVoidType(typeNode.getSymbolTable(), CTC_VOID)); } @@ -601,7 +633,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } } - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, false)))); + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 0)))); JCBlock body = maker.Block(0, statements.toList()); return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName("$lombokClean"), maker.Type(Javac.createVoidType(type.getSymbolTable(), CTC_VOID)), List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); /* @@ -624,13 +656,13 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { * that takes the builder instance as parameter (instead of a * constructor with all relevant fields as parameters) */ - private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning, boolean useBuilderBasedConstructor) { + private JCMethodDecl generateBuildMethod(JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning, boolean useBuilderBasedConstructor) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); - + // Extendable builders assign their values in the constructor, not in this build() method. if (!useBuilderBasedConstructor) { if (addCleaning) { @@ -645,13 +677,17 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { bfd.singularData.getSingularizer().appendBuildCode(bfd.singularData, type, source, statements, bfd.name, "this"); } } - + for (BuilderFieldData bfd : builderFields) { + if (bfd.nameOfSetFlag != null) { + statements.append(maker.VarDef(maker.Modifiers(0L), bfd.name, cloneType(maker, bfd.type, source, tdParent.getContext()), maker.Select(maker.Ident(type.toName("this")), bfd.name))); + statements.append(maker.If(maker.Unary(CTC_NOT, maker.Ident(bfd.nameOfSetFlag)), maker.Exec(maker.Assign(maker.Ident(bfd.name),maker.Apply(typeParameterNames(maker, ((JCClassDecl) tdParent.get()).typarams), maker.Select(maker.Ident(((JCClassDecl) tdParent.get()).name), bfd.nameOfDefaultProvider), List.<JCExpression>nil()))), null)); + } args.append(maker.Ident(bfd.name)); } if (addCleaning) { - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true)))); + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 1)))); } } @@ -666,14 +702,12 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } statements.append(maker.Return(call)); } else { - ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>(); for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) { typeParams.append(maker.Ident(tp.name)); } JCExpression callee = maker.Ident(((JCClassDecl) type.up().get()).name); - if (!isStatic) - callee = maker.Select(callee, type.up().toName("this")); + if (!isStatic) callee = maker.Select(callee, type.up().toName("this")); JCExpression fn = maker.Select(callee, builderName); call = maker.Apply(typeParams.toList(), fn, args.toList()); if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) { @@ -688,6 +722,18 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { return maker.MethodDef(maker.Modifiers(Flags.PUBLIC), type.toName(buildName), returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), thrownExceptions, body, null); } + public JCMethodDecl generateDefaultProvider(Name methodName, JavacNode fieldNode, List<JCTypeParameter> params) { + JavacTreeMaker maker = fieldNode.getTreeMaker(); + JCVariableDecl field = (JCVariableDecl) fieldNode.get(); + + JCStatement statement = maker.Return(field.init); + field.init = null; + + JCBlock body = maker.Block(0, List.<JCStatement>of(statement)); + int modifiers = Flags.PRIVATE | Flags.STATIC; + return maker.MethodDef(maker.Modifiers(modifiers), methodName, cloneType(maker, field.vartype, field, fieldNode.getContext()), copyTypeParams(fieldNode, params), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), body, null); + } + public JCMethodDecl generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, JavacNode source, JavacNode type, List<JCTypeParameter> typeParams) { JavacTreeMaker maker = type.getTreeMaker(); @@ -712,36 +758,43 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { if (child.getKind() == Kind.FIELD) existing.add(child); } - top: for (int i = len - 1; i >= 0; i--) { BuilderFieldData bfd = builderFields.get(i); if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType, source)); } else { + JavacNode field = null, setFlag = null; for (JavacNode exists : existing) { Name n = ((JCVariableDecl) exists.get()).name; - if (n.equals(bfd.name)) { - bfd.createdFields.add(exists); - continue top; - } + if (n.equals(bfd.name)) field = exists; + if (n.equals(bfd.nameOfSetFlag)) setFlag = exists; } JavacTreeMaker maker = builderType.getTreeMaker(); - JCModifiers mods = maker.Modifiers(Flags.PRIVATE); - JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null); - bfd.createdFields.add(injectFieldAndMarkGenerated(builderType, newField)); + if (field == null) { + JCModifiers mods = maker.Modifiers(Flags.PRIVATE); + JCVariableDecl newField = maker.VarDef(mods, bfd.name, cloneType(maker, bfd.type, source, builderType.getContext()), null); + field = injectFieldAndMarkGenerated(builderType, newField); + } + if (setFlag == null && bfd.nameOfSetFlag != null) { + JCModifiers mods = maker.Modifiers(Flags.PRIVATE); + JCVariableDecl newField = maker.VarDef(mods, bfd.nameOfSetFlag, maker.TypeIdent(CTC_BOOLEAN), null); + injectFieldAndMarkGenerated(builderType, newField); + } + bfd.createdFields.add(field); } } } public void makeSetterMethodsForBuilder(JavacNode builderType, BuilderFieldData fieldNode, JavacNode source, boolean fluent, boolean chain) { + boolean deprecate = isFieldDeprecated(fieldNode.originalFieldNode); if (fieldNode.singularData == null || fieldNode.singularData.getSingularizer() == null) { - makeSimpleSetterMethodForBuilder(builderType, fieldNode.createdFields.get(0), source, fluent, chain); + makeSimpleSetterMethodForBuilder(builderType, deprecate, fieldNode.createdFields.get(0), fieldNode.nameOfSetFlag, source, fluent, chain); } else { - fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, builderType, source.get(), fluent, chain); + fieldNode.singularData.getSingularizer().generateMethods(fieldNode.singularData, deprecate, builderType, source.get(), fluent, chain); } } - private void makeSimpleSetterMethodForBuilder(JavacNode builderType, JavacNode fieldNode, JavacNode source, boolean fluent, boolean chain) { + private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean deprecate, JavacNode fieldNode, Name nameOfSetFlag, JavacNode source, boolean fluent, boolean chain) { Name fieldName = ((JCVariableDecl) fieldNode.get()).name; for (JavacNode child : builderType.down()) { @@ -754,7 +807,9 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { String setterName = fluent ? fieldNode.getName() : HandlerUtil.buildAccessorName("set", fieldNode.getName()); JavacTreeMaker maker = fieldNode.getTreeMaker(); - JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil()); + + JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, nameOfSetFlag, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil()); + injectMethod(builderType, newMethod); } @@ -772,10 +827,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { int modifiers = Flags.PUBLIC; if (isStatic) modifiers |= Flags.STATIC; JCModifiers mods = maker.Modifiers(modifiers); - JCExpression extending = null; - if (parentBuilderClassName != null) { - extending = maker.Ident(tdParent.toName(parentBuilderClassName)); - } + JCExpression extending = parentBuilderClassName == null ? null : maker.Ident(tdParent.toName(parentBuilderClassName)); JCClassDecl builder = maker.ClassDef(mods, tdParent.toName(builderClassName), copyTypeParams(source, typeParams), extending, List.<JCExpression>nil(), List.<JCTree>nil()); return injectType(tdParent, builder); } diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java new file mode 100644 index 00000000..4c4ba0e8 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleBuilderDefault.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017-2018 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.handlers; + +import static lombok.javac.handlers.JavacHandlerUtil.*; + +import org.mangosdk.spi.ProviderFor; + +import com.sun.tools.javac.tree.JCTree.JCAnnotation; + +import lombok.Builder; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; + +@ProviderFor(JavacAnnotationHandler.class) +@HandlerPriority(-1025) //HandleBuilder's level, minus one. +public class HandleBuilderDefault extends JavacAnnotationHandler<Builder.Default> { + @Override public void handle(AnnotationValues<Builder.Default> annotation, JCAnnotation ast, JavacNode annotationNode) { + JavacNode annotatedField = annotationNode.up(); + if (annotatedField.getKind() != Kind.FIELD) return; + JavacNode classWithAnnotatedField = annotatedField.up(); + if (!hasAnnotation(Builder.class, classWithAnnotatedField) && !hasAnnotation("lombok.experimental.Builder", classWithAnnotatedField)) { + annotationNode.addWarning("@Builder.Default requires @Builder on the class for it to mean anything."); + deleteAnnotationIfNeccessary(annotationNode, Builder.Default.class); + } + } +} diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 4e90b639..dca25ee7 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -64,6 +64,8 @@ import com.sun.tools.javac.util.Name; public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleNoArgsConstructor extends JavacAnnotationHandler<NoArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<NoArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.NO_ARGS_CONSTRUCTOR_FLAG_USAGE, "@NoArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -78,12 +80,14 @@ public class HandleConstructor { String staticName = ann.staticName(); boolean force = ann.force(); List<JavacNode> fields = force ? findFinalFields(typeNode) : List.<JavacNode>nil(); - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, annotationNode); + handleConstructor.generateConstructor(typeNode, level, onConstructor, fields, force, staticName, SkipIfConstructorExists.NO, annotationNode); } } @ProviderFor(JavacAnnotationHandler.class) public static class HandleRequiredArgsConstructor extends JavacAnnotationHandler<RequiredArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE, "@RequiredArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -100,7 +104,7 @@ public class HandleConstructor { annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'."); } - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); + handleConstructor.generateConstructor(typeNode, level, onConstructor, findRequiredFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); } } @@ -131,6 +135,8 @@ public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleAllArgsConstructor extends JavacAnnotationHandler<AllArgsConstructor> { + private HandleConstructor handleConstructor = new HandleConstructor(); + @Override public void handle(AnnotationValues<AllArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.ALL_ARGS_CONSTRUCTOR_FLAG_USAGE, "@AllArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); @@ -146,11 +152,15 @@ public class HandleConstructor { if (annotation.isExplicit("suppressConstructorProperties")) { annotationNode.addError("This deprecated feature is no longer supported. Remove it; you can create a lombok.config file with 'lombok.anyConstructor.suppressConstructorProperties = true'."); } - new HandleConstructor().generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); + handleConstructor.generateConstructor(typeNode, level, onConstructor, findAllFields(typeNode), false, staticName, SkipIfConstructorExists.NO, annotationNode); } } public static List<JavacNode> findAllFields(JavacNode typeNode) { + return findAllFields(typeNode, false); + } + + public static List<JavacNode> findAllFields(JavacNode typeNode, boolean evenFinalInitialized) { ListBuffer<JavacNode> fields = new ListBuffer<JavacNode>(); for (JavacNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; @@ -162,7 +172,7 @@ public class HandleConstructor { if ((fieldFlags & Flags.STATIC) != 0) continue; //Skip initialized final fields boolean isFinal = (fieldFlags & Flags.FINAL) != 0; - if (!isFinal || fieldDecl.init == null) fields.append(child); + if (evenFinalInitialized || !isFinal || fieldDecl.init == null) fields.append(child); } return fields.toList(); } @@ -256,18 +266,20 @@ public class HandleConstructor { mods.annotations = mods.annotations.append(annotation); } - public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean allToDefault, JavacNode source) { + @SuppressWarnings("deprecation") public static JCMethodDecl createConstructor(AccessLevel level, List<JCAnnotation> onConstructor, JavacNode typeNode, List<JavacNode> fields, boolean allToDefault, JavacNode source) { JavacTreeMaker maker = typeNode.getTreeMaker(); boolean isEnum = (((JCClassDecl) typeNode.get()).mods.flags & Flags.ENUM) != 0; if (isEnum) level = AccessLevel.PRIVATE; - boolean suppressConstructorProperties; + boolean addConstructorProperties; if (fields.isEmpty()) { - suppressConstructorProperties = false; + addConstructorProperties = false; } else { - suppressConstructorProperties = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); + Boolean v = typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_ADD_CONSTRUCTOR_PROPERTIES); + addConstructorProperties = v != null ? v.booleanValue() : + Boolean.FALSE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.ANY_CONSTRUCTOR_SUPPRESS_CONSTRUCTOR_PROPERTIES)); } ListBuffer<JCStatement> nullChecks = new ListBuffer<JCStatement>(); @@ -285,7 +297,7 @@ public class HandleConstructor { JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null); params.append(param); if (!nonNulls.isEmpty()) { - JCStatement nullCheck = generateNullCheck(maker, fieldNode, source); + JCStatement nullCheck = generateNullCheck(maker, fieldNode, param, source); if (nullCheck != null) nullChecks.append(nullCheck); } } @@ -295,7 +307,7 @@ public class HandleConstructor { } JCModifiers mods = maker.Modifiers(toJavacModifier(level), List.<JCAnnotation>nil()); - if (!allToDefault && !suppressConstructorProperties && level != AccessLevel.PRIVATE && level != AccessLevel.PACKAGE && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) { + if (!allToDefault && addConstructorProperties && !isLocalType(typeNode) && LombokOptionsFactory.getDelombokOptions(typeNode.getContext()).getFormatPreferences().generateConstructorProperties()) { addConstructorProperties(mods, typeNode, fields); } if (onConstructor != null) mods.annotations = mods.annotations.appendList(copyAnnotations(onConstructor)); diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java index 9ecf8754..15f1fd83 100644 --- a/src/core/lombok/javac/handlers/HandleData.java +++ b/src/core/lombok/javac/handlers/HandleData.java @@ -40,6 +40,12 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation; */ @ProviderFor(JavacAnnotationHandler.class) public class HandleData extends JavacAnnotationHandler<Data> { + private HandleConstructor handleConstructor = new HandleConstructor(); + private HandleGetter handleGetter = new HandleGetter(); + private HandleSetter handleSetter = new HandleSetter(); + private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode(); + private HandleToString handleToString = new HandleToString(); + @Override public void handle(AnnotationValues<Data> annotation, JCAnnotation ast, JavacNode annotationNode) { handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data"); @@ -54,11 +60,10 @@ public class HandleData extends JavacAnnotationHandler<Data> { String staticConstructorName = annotation.getInstance().staticConstructor(); - // TODO move this to the end OR move it to the top in eclipse. - new HandleConstructor().generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode); - new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); - new HandleToString().generateToStringForType(typeNode, annotationNode); + handleConstructor.generateRequiredArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode); + handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleSetter.generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode); + handleToString.generateToStringForType(typeNode, annotationNode); } } diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index 6df56ed6..d8bfd154 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -121,7 +121,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas return; } - generateMethods(typeNode, source, null, null, null, false, FieldAccess.GETTER, List.<JCAnnotation>nil()); + Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS); + FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; + + generateMethods(typeNode, source, null, null, null, false, access, List.<JCAnnotation>nil()); } public void generateMethods(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes, @@ -255,20 +258,23 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext()); /* final int PRIME = X; */ { - if (!fields.isEmpty() || callSuper) { + if (!fields.isEmpty()) { statements.append(maker.VarDef(maker.Modifiers(finalFlag), primeName, maker.TypeIdent(CTC_INT), maker.Literal(HandlerUtil.primeForHashcode()))); } } - /* int result = 1; */ { - statements.append(maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(CTC_INT), maker.Literal(1))); - } - - if (callSuper) { - JCMethodInvocation callToSuper = maker.Apply(List.<JCExpression>nil(), - maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), - List.<JCExpression>nil()); - statements.append(createResultCalculation(typeNode, callToSuper)); + /* int result = ... */ { + final JCExpression init; + if (callSuper) { + /* ... super.hashCode(); */ + init = maker.Apply(List.<JCExpression>nil(), + maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")), + List.<JCExpression>nil()); + } else { + /* ... 1; */ + init = maker.Literal(1); + } + statements.append(maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(CTC_INT), init)); } Name dollar = typeNode.toName("$"); @@ -365,31 +371,44 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas public JCExpression createTypeReference(JavacNode type, boolean addWildcards) { java.util.List<String> list = new ArrayList<String>(); + java.util.List<Integer> genericsCount = addWildcards ? new ArrayList<Integer>() : null; + list.add(type.getName()); + if (addWildcards) genericsCount.add(((JCClassDecl) type.get()).typarams.size()); + boolean staticContext = (((JCClassDecl) type.get()).getModifiers().flags & Flags.STATIC) != 0; JavacNode tNode = type.up(); + while (tNode != null && tNode.getKind() == Kind.TYPE) { list.add(tNode.getName()); + if (addWildcards) genericsCount.add(staticContext ? 0 : ((JCClassDecl) tNode.get()).typarams.size()); + if (!staticContext) staticContext = (((JCClassDecl) tNode.get()).getModifiers().flags & Flags.STATIC) != 0; tNode = tNode.up(); } Collections.reverse(list); - JCClassDecl typeDecl = (JCClassDecl) type.get(); + if (addWildcards) Collections.reverse(genericsCount); JavacTreeMaker maker = type.getTreeMaker(); JCExpression chain = maker.Ident(type.toName(list.get(0))); + if (addWildcards) chain = wildcardify(maker, chain, genericsCount.get(0)); for (int i = 1; i < list.size(); i++) { chain = maker.Select(chain, type.toName(list.get(i))); + if (addWildcards) chain = wildcardify(maker, chain, genericsCount.get(i)); } - if (!addWildcards || typeDecl.typarams.length() == 0) return chain; + return chain; + } + + private JCExpression wildcardify(JavacTreeMaker maker, JCExpression expr, int count) { + if (count == 0) return expr; ListBuffer<JCExpression> wildcards = new ListBuffer<JCExpression>(); - for (int i = 0 ; i < typeDecl.typarams.length() ; i++) { + for (int i = 0 ; i < count ; i++) { wildcards.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null)); } - return maker.TypeApply(chain, wildcards.toList()); + return maker.TypeApply(expr, wildcards.toList()); } public JCMethodDecl createEquals(JavacNode typeNode, List<JavacNode> fields, boolean callSuper, FieldAccess fieldAccess, boolean needsCanEqual, JCTree source, List<JCAnnotation> onParam) { diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 5a2e1993..0540465d 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -95,7 +95,7 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { } } - public boolean fieldQualifiesForGetterGeneration(JavacNode field) { + public static boolean fieldQualifiesForGetterGeneration(JavacNode field) { if (field.getKind() != Kind.FIELD) return false; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); //Skip fields that start with $ diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index e766127a..1453aa27 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -206,10 +206,10 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { String setterName = toSetterName(field); boolean returnThis = shouldReturnThis(field); - return createSetter(access, field, treeMaker, setterName, returnThis, source, onMethod, onParam); + return createSetter(access, false, field, treeMaker, setterName, null, returnThis, source, onMethod, onParam); } - public static JCMethodDecl createSetter(long access, JavacNode field, JavacTreeMaker treeMaker, String setterName, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { + public static JCMethodDecl createSetter(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam) { if (setterName == null) return null; JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); @@ -235,6 +235,11 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { statements.append(treeMaker.Exec(assign)); } + if (booleanFieldToSet != null) { + JCAssign setBool = treeMaker.Assign(treeMaker.Ident(booleanFieldToSet), treeMaker.Literal(CTC_BOOLEAN, 1)); + statements.append(treeMaker.Exec(setBool)); + } + JCExpression methodType = null; if (shouldReturnThis) { methodType = cloneSelfType(field); @@ -258,7 +263,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { JCExpression annotationMethodDefaultValue = null; List<JCAnnotation> annsOnMethod = copyAnnotations(onMethod); - if (isFieldDeprecated(field)) { + if (isFieldDeprecated(field) || deprecate) { annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil())); } diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 743e7b26..897d5f2c 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -117,7 +117,11 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); } catch (Exception ignore) {} - generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER); + + Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); + FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD; + + generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, access); } public void generateToString(JavacNode typeNode, JavacNode source, List<String> excludes, List<String> includes, diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 2976eabe..14130bc4 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2016 The Project Lombok Authors. + * Copyright (C) 2010-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ import static lombok.javac.handlers.JavacHandlerUtil.*; import lombok.ConfigurationKeys; import lombok.val; import lombok.core.HandlerPriority; -import lombok.experimental.var; +import lombok.var; import lombok.javac.JavacASTAdapter; import lombok.javac.JavacASTVisitor; import lombok.javac.JavacNode; @@ -54,29 +54,34 @@ import com.sun.tools.javac.util.List; public class HandleVal extends JavacASTAdapter { private static boolean eq(String typeTreeToString, String key) { - return (typeTreeToString.equals(key) || typeTreeToString.equals("lombok." + key)); + return typeTreeToString.equals(key) || typeTreeToString.equals("lombok." + key) || typeTreeToString.equals("lombok.experimental." + key); } - @Override + @SuppressWarnings("deprecation") @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { JCTree typeTree = local.vartype; if (typeTree == null) return; String typeTreeToString = typeTree.toString(); - + if (!(eq(typeTreeToString, "val") || eq(typeTreeToString, "var"))) return; boolean isVal = typeMatches(val.class, localNode, typeTree); boolean isVar = typeMatches(var.class, localNode, typeTree); if (!(isVal || isVar)) return; - + if (isVal) handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); if (isVar) handleFlagUsage(localNode, ConfigurationKeys.VAR_FLAG_USAGE, "var"); - + JCTree parentRaw = localNode.directUp().get(); if (isVal && parentRaw instanceof JCForLoop) { localNode.addError("'val' is not allowed in old-style for loops"); return; } - + + if (parentRaw instanceof JCForLoop && ((JCForLoop) parentRaw).getInitializer().size() > 1) { + localNode.addError("'var' is not allowed in old-style for loops if there is more than 1 initializer"); + return; + } + JCExpression rhsOfEnhancedForLoop = null; if (local.init == null) { if (parentRaw instanceof JCEnhancedForLoop) { @@ -84,26 +89,26 @@ public class HandleVal extends JavacASTAdapter { if (efl.var == local) rhsOfEnhancedForLoop = efl.expr; } } - + final String annotation = typeTreeToString; if (rhsOfEnhancedForLoop == null && local.init == null) { localNode.addError("'" + annotation + "' on a local variable requires an initializer expression"); return; - } - + if (local.init instanceof JCNewArray && ((JCNewArray)local.init).elemtype == null) { localNode.addError("'" + annotation + "' is not compatible with array initializer expressions. Use the full form (new int[] { ... } instead of just { ... })"); return; } - + if (localNode.shouldDeleteLombokAnnotations()) { JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, val.class.getName()); + JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, lombok.experimental.var.class.getName()); JavacHandlerUtil.deleteImportFromCompilationUnit(localNode, var.class.getName()); } - + if (isVal) local.mods.flags |= Flags.FINAL; - + if (!localNode.shouldDeleteLombokAnnotations()) { 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); @@ -126,7 +131,7 @@ public class HandleVal extends JavacASTAdapter { try { type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type; } catch (RuntimeException e) { - System.err.println("Exception while resolving: " + localNode); + System.err.println("Exception while resolving: " + localNode + "(" + localNode.getFileName() + ")"); throw e; } } else { @@ -137,7 +142,7 @@ public class HandleVal extends JavacASTAdapter { local.type = Symtab.instance(localNode.getContext()).unknownType; type = ((JCExpression) resolver.resolveMethodMember(localNode).get(local.init)).type; } catch (RuntimeException e) { - System.err.println("Exception while resolving: " + localNode); + System.err.println("Exception while resolving: " + localNode + "(" + localNode.getFileName() + ")"); throw e; } } diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java index 90f6a98d..d1af4168 100644 --- a/src/core/lombok/javac/handlers/HandleValue.java +++ b/src/core/lombok/javac/handlers/HandleValue.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2012-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,6 @@ package lombok.javac.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; -import java.lang.annotation.Annotation; - import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; @@ -49,13 +47,16 @@ import com.sun.tools.javac.tree.JCTree.JCModifiers; @ProviderFor(JavacAnnotationHandler.class) @HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier. public class HandleValue extends JavacAnnotationHandler<Value> { + private HandleFieldDefaults handleFieldDefaults = new HandleFieldDefaults(); + private HandleConstructor handleConstructor = new HandleConstructor(); + private HandleGetter handleGetter = new HandleGetter(); + private HandleEqualsAndHashCode handleEqualsAndHashCode = new HandleEqualsAndHashCode(); + private HandleToString handleToString = new HandleToString(); + @Override public void handle(AnnotationValues<Value> annotation, JCAnnotation ast, JavacNode annotationNode) { - @SuppressWarnings("deprecation") - Class<? extends Annotation> oldExperimentalValue = lombok.experimental.Value.class; - handleFlagUsage(annotationNode, ConfigurationKeys.VALUE_FLAG_USAGE, "@Value"); - deleteAnnotationIfNeccessary(annotationNode, Value.class, oldExperimentalValue); + deleteAnnotationIfNeccessary(annotationNode, Value.class, "lombok.experimental.Value"); JavacNode typeNode = annotationNode.up(); boolean notAClass = !isClass(typeNode); @@ -73,12 +74,10 @@ public class HandleValue extends JavacAnnotationHandler<Value> { typeNode.rebuild(); } } - new HandleFieldDefaults().generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true); - - // TODO move this to the end OR move it to the top in eclipse. - new HandleConstructor().generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode); - new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); - new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); - new HandleToString().generateToStringForType(typeNode, annotationNode); + handleFieldDefaults.generateFieldDefaultsForType(typeNode, annotationNode, AccessLevel.PRIVATE, true, true); + handleConstructor.generateAllArgsConstructor(typeNode, AccessLevel.PUBLIC, staticConstructorName, SkipIfConstructorExists.YES, annotationNode); + handleGetter.generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); + handleEqualsAndHashCode.generateEqualsAndHashCodeForType(typeNode, annotationNode); + handleToString.generateToStringForType(typeNode, annotationNode); } } diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 56e666f7..65d09a9a 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2017 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -162,6 +162,10 @@ public class JavacHandlerUtil { return node; } + public static boolean hasAnnotation(String type, JavacNode node) { + return hasAnnotation(type, node, false); + } + public static boolean hasAnnotation(Class<? extends Annotation> type, JavacNode node) { return hasAnnotation(type, node, false); } @@ -191,6 +195,48 @@ public class JavacHandlerUtil { } } + private static boolean hasAnnotation(String type, JavacNode node, boolean delete) { + if (node == null) return false; + if (type == null) return false; + switch (node.getKind()) { + case ARGUMENT: + case FIELD: + case LOCAL: + case TYPE: + case METHOD: + for (JavacNode child : node.down()) { + if (annotationTypeMatches(type, child)) { + if (delete) deleteAnnotationIfNeccessary(child, type); + return true; + } + } + // intentional fallthrough + default: + return false; + } + } + + static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node, boolean delete) { + if (node == null) return null; + if (type == null) return null; + switch (node.getKind()) { + case ARGUMENT: + case FIELD: + case LOCAL: + case TYPE: + case METHOD: + for (JavacNode child : node.down()) { + if (annotationTypeMatches(type, child)) { + if (delete) deleteAnnotationIfNeccessary(child, type); + return child; + } + } + // intentional fallthrough + default: + return null; + } + } + /** * Checks if the Annotation AST Node provided is likely to be an instance of the provided annotation type. * @@ -203,6 +249,17 @@ public class JavacHandlerUtil { } /** + * Checks if the Annotation AST Node provided is likely to be an instance of the provided annotation type. + * + * @param type An actual annotation type, such as {@code lombok.Getter.class}. + * @param node A Lombok AST node representing an annotation in source code. + */ + public static boolean annotationTypeMatches(String type, JavacNode node) { + if (node.getKind() != Kind.ANNOTATION) return false; + return typeMatches(type, node, ((JCAnnotation)node.get()).annotationType); + } + + /** * Checks if the given TypeReference node is likely to be a reference to the provided class. * * @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type. @@ -210,10 +267,21 @@ public class JavacHandlerUtil { * @param typeNode A type reference to check. */ public static boolean typeMatches(Class<?> type, JavacNode node, JCTree typeNode) { + return typeMatches(type.getName(), node, typeNode); + } + + /** + * Checks if the given TypeReference node is likely to be a reference to the provided class. + * + * @param type An actual type. This method checks if {@code typeNode} is likely to be a reference to this type. + * @param node A Lombok AST node. Any node in the appropriate compilation unit will do (used to get access to import statements). + * @param typeNode A type reference to check. + */ + public static boolean typeMatches(String type, JavacNode node, JCTree typeNode) { String typeName = typeNode.toString(); TypeResolver resolver = new TypeResolver(node.getImportList()); - return resolver.typeMatches(node, type.getName(), typeName); + return resolver.typeMatches(node, type, typeName); } /** @@ -222,6 +290,7 @@ public class JavacHandlerUtil { * @return {@code true} if a field is marked deprecated, either by {@code @Deprecated} or in javadoc, otherwise {@code false} */ public static boolean isFieldDeprecated(JavacNode field) { + if (!(field.get() instanceof JCVariableDecl)) return false; JCVariableDecl fieldNode = (JCVariableDecl) field.get(); if ((fieldNode.mods.flags & Flags.DEPRECATED) != 0) { return true; @@ -324,8 +393,7 @@ public class JavacHandlerUtil { * then removes any import statement that imports this exact annotation (not star imports). * Only does this if the DeleteLombokAnnotations class is in the context. */ - @SuppressWarnings("unchecked") - public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) { + public static void deleteAnnotationIfNeccessary(JavacNode annotation, String annotationType) { deleteAnnotationIfNeccessary0(annotation, annotationType); } @@ -334,12 +402,29 @@ public class JavacHandlerUtil { * then removes any import statement that imports this exact annotation (not star imports). * Only does this if the DeleteLombokAnnotations class is in the context. */ - @SuppressWarnings("unchecked") + public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType) { + deleteAnnotationIfNeccessary0(annotation, annotationType.getName()); + } + + /** + * Removes the annotation from javac's AST (it remains in lombok's AST), + * then removes any import statement that imports this exact annotation (not star imports). + * Only does this if the DeleteLombokAnnotations class is in the context. + */ public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, Class<? extends Annotation> annotationType2) { - deleteAnnotationIfNeccessary0(annotation, annotationType1, annotationType2); + deleteAnnotationIfNeccessary0(annotation, annotationType1.getName(), annotationType2.getName()); } - private static void deleteAnnotationIfNeccessary0(JavacNode annotation, Class<? extends Annotation>... annotationTypes) { + /** + * Removes the annotation from javac's AST (it remains in lombok's AST), + * then removes any import statement that imports this exact annotation (not star imports). + * Only does this if the DeleteLombokAnnotations class is in the context. + */ + public static void deleteAnnotationIfNeccessary(JavacNode annotation, Class<? extends Annotation> annotationType1, String annotationType2) { + deleteAnnotationIfNeccessary0(annotation, annotationType1.getName(), annotationType2); + } + + private static void deleteAnnotationIfNeccessary0(JavacNode annotation, String... annotationTypes) { if (inNetbeansEditor(annotation)) return; if (!annotation.shouldDeleteLombokAnnotations()) return; JavacNode parentNode = annotation.directUp(); @@ -368,8 +453,8 @@ public class JavacHandlerUtil { } parentNode.getAst().setChanged(); - for (Class<?> annotationType : annotationTypes) { - deleteImportFromCompilationUnit(annotation, annotationType.getName()); + for (String annotationType : annotationTypes) { + deleteImportFromCompilationUnit(annotation, annotationType); } } @@ -733,7 +818,7 @@ public class JavacHandlerUtil { // Check if the class has a @Getter annotation. - if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) { + if (!hasGetterAnnotation && HandleGetter.fieldQualifiesForGetterGeneration(field)) { //Check if the class has @Getter or @Data annotation. JavacNode containingType = field.up(); @@ -1189,16 +1274,25 @@ public class JavacHandlerUtil { } /** - * Generates a new statement that checks if the given variable is null, and if so, throws a specified exception with the + * Generates a new statement that checks if the given variable is null, and if so, throws a configured exception with the * variable name as message. - * - * @param exName The name of the exception to throw; normally {@code java.lang.NullPointerException}. */ public static JCStatement generateNullCheck(JavacTreeMaker maker, JavacNode variable, JavacNode source) { + return generateNullCheck(maker, variable, (JCVariableDecl)variable.get(), source); + } + + /** + * Generates a new statement that checks if the given variable is null, and if so, throws a configured exception with the + * variable name as message. + * + * This is a special case method reserved for use when the provided declaration differs from the + * variable's declaration, i.e. in a constructor or setter where the local parameter is named the same but with the prefix + * stripped as a result of @Accessors.prefix. + */ + public static JCStatement generateNullCheck(JavacTreeMaker maker, JavacNode variable, JCVariableDecl varDecl, JavacNode source) { NullCheckExceptionType exceptionType = source.getAst().readConfiguration(ConfigurationKeys.NON_NULL_EXCEPTION_TYPE); if (exceptionType == null) exceptionType = NullCheckExceptionType.NULL_POINTER_EXCEPTION; - JCVariableDecl varDecl = (JCVariableDecl) variable.get(); if (isPrimitive(varDecl.vartype)) return null; Name fieldName = varDecl.name; JCExpression exType = genTypeRef(variable, exceptionType.getExceptionType()); @@ -1357,25 +1451,27 @@ public class JavacHandlerUtil { } public static JCExpression namePlusTypeParamsToTypeReference(JavacTreeMaker maker, Name typeName, List<JCTypeParameter> params) { + if (params.isEmpty()) { + return maker.Ident(typeName); + } + return maker.TypeApply(maker.Ident(typeName), typeParameterNames(maker, params)); + } + + public static List<JCExpression> typeParameterNames(JavacTreeMaker maker, List<JCTypeParameter> params) { ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>(); - - if (!params.isEmpty()) { - for (JCTypeParameter param : params) { - typeArgs.append(maker.Ident(param.name)); - } - - return maker.TypeApply(maker.Ident(typeName), typeArgs.toList()); + for (JCTypeParameter param : params) { + typeArgs.append(maker.Ident(param.name)); } - - return maker.Ident(typeName); + return typeArgs.toList(); } public static void sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(JavacNode typeNode, JavacNode errorNode) { List<String> disallowed = List.nil(); for (JavacNode child : typeNode.down()) { - for (Class<? extends java.lang.annotation.Annotation> annType : INVALID_ON_BUILDERS) { + for (String annType : INVALID_ON_BUILDERS) { if (annotationTypeMatches(annType, child)) { - disallowed = disallowed.append(annType.getSimpleName()); + int lastIndex = annType.lastIndexOf('.'); + disallowed = disallowed.append(lastIndex == -1 ? annType : annType.substring(lastIndex + 1)); } } } diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index 33501349..4279ec63 100644 --- a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java +++ b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,9 +38,12 @@ import lombok.javac.JavacTreeMaker; import com.sun.source.tree.Tree.Kind; import com.sun.tools.javac.code.BoundKind; +import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWildcard; @@ -141,6 +144,11 @@ public class JavacSingularsRecipes { public static abstract class JavacSingularizer { public abstract LombokImmutableList<String> getSupportedTypes(); + protected JCModifiers makeMods(JavacTreeMaker maker, JavacNode node, boolean deprecate) { + if (deprecate) return maker.Modifiers(Flags.PUBLIC, List.<JCAnnotation>of(maker.Annotation(genJavaLangTypeRef(node, "Deprecated"), List.<JCExpression>nil()))); + return maker.Modifiers(Flags.PUBLIC); + } + /** Checks if any of the to-be-generated nodes (fields, methods) already exist. If so, errors on these (singulars don't support manually writing some of it, and returns true). */ public boolean checkForAlreadyExistingNodesAndGenerateError(JavacNode builderType, SingularData data) { for (JavacNode child : builderType.down()) { @@ -186,7 +194,7 @@ public class JavacSingularsRecipes { } public abstract java.util.List<JavacNode> generateFields(SingularData data, JavacNode builderType, JCTree source); - public abstract void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain); + public abstract void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain); public abstract void appendBuildCode(SingularData data, JavacNode builderType, JCTree source, ListBuffer<JCStatement> statements, Name targetVariableName, String builderVariable); public boolean requiresCleaning() { diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index fb3d2c55..1798d52c 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -70,24 +70,24 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { JavacTreeMaker maker = builderType.getTreeMaker(); Symtab symbolTable = builderType.getSymbolTable(); JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } - private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = makeMods(maker, builderType, deprecate); List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); List<JCVariableDecl> params = List.nil(); @@ -102,7 +102,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { injectMethod(builderType, method); } - void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); @@ -114,7 +114,7 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { names[i] = s.isEmpty() ? n : builderType.toName(s); } - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName()); @@ -141,11 +141,10 @@ abstract class JavacGuavaSingularizer extends JavacSingularizer { injectMethod(builderType, method); } - protected void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + protected void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, source)); JCExpression thisDotFieldDotAddAll = chainDots(builderType, "this", data.getPluralName().toString(), getAddMethodName() + "All"); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java index 7beb29cb..196ce45d 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilListSetSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -76,9 +76,9 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize return Collections.singletonList(injectFieldAndMarkGenerated(builderType, buildField)); } - @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { if (useGuavaInstead(builderType)) { - guavaListSetSingularizer.generateMethods(data, builderType, source, fluent, chain); + guavaListSetSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain); return; } @@ -88,19 +88,19 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); JCStatement returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(thisName)) : null; - generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } - private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = makeMods(maker, builderType, deprecate); List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); List<JCVariableDecl> params = List.nil(); @@ -119,11 +119,10 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize injectMethod(builderType, method); } - void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source)); JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "add"); @@ -140,11 +139,10 @@ abstract class JavacJavaUtilListSetSingularizer extends JavacJavaUtilSingularize injectMethod(builderType, method); } - void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, false, source)); JCExpression thisDotFieldDotAdd = chainDots(builderType, "this", data.getPluralName().toString(), "addAll"); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java index 774b1200..fbcf776e 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilMapSingularizer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Project Lombok Authors. + * Copyright (C) 2015-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -101,9 +101,9 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { return Arrays.asList(keyFieldNode, valueFieldNode); } - @Override public void generateMethods(SingularData data, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { + @Override public void generateMethods(SingularData data, boolean deprecate, JavacNode builderType, JCTree source, boolean fluent, boolean chain) { if (useGuavaInstead(builderType)) { - guavaMapSingularizer.generateMethods(data, builderType, source, fluent, chain); + guavaMapSingularizer.generateMethods(data, deprecate, builderType, source, fluent, chain); return; } @@ -112,19 +112,20 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { JCExpression returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); JCStatement returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateSingularMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generateSingularMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generatePluralMethod(maker, returnType, returnStatement, data, builderType, source, fluent); + generatePluralMethod(deprecate, maker, returnType, returnStatement, data, builderType, source, fluent); returnType = chain ? cloneSelfType(builderType) : maker.Type(createVoidType(symbolTable, CTC_VOID)); returnStatement = chain ? maker.Return(maker.Ident(builderType.toName("this"))) : null; - generateClearMethod(maker, returnType, returnStatement, data, builderType, source); + generateClearMethod(deprecate, maker, returnType, returnStatement, data, builderType, source); } - private void generateClearMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + private void generateClearMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source) { + JCModifiers mods = makeMods(maker, builderType, deprecate); + List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); List<JCVariableDecl> params = List.nil(); @@ -146,10 +147,11 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { injectMethod(builderType, method); } - private void generateSingularMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + private void generateSingularMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrown = List.nil(); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); + ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source)); Name keyName = builderType.toName(data.getSingularName().toString() + "Key"); @@ -178,10 +180,10 @@ public class JavacJavaUtilMapSingularizer extends JavacJavaUtilSingularizer { injectMethod(builderType, method); } - private void generatePluralMethod(JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { + private void generatePluralMethod(boolean deprecate, JavacTreeMaker maker, JCExpression returnType, JCStatement returnStatement, SingularData data, JavacNode builderType, JCTree source, boolean fluent) { List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> jceBlank = List.nil(); - JCModifiers mods = maker.Modifiers(Flags.PUBLIC); + JCModifiers mods = makeMods(maker, builderType, deprecate); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); statements.append(createConstructBuilderVarIfNeeded(maker, data, builderType, true, source)); long paramFlags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, builderType.getContext()); diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java index 12922d3d..df521fd8 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java @@ -165,6 +165,9 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { JCExpression pluralnameDotPut = maker.Select(maker.Ident(data.getPluralName()), builderType.toName("put")); JCExpression arg1 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$key", "get"), List.<JCExpression>of(maker.Ident(ivar))); JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, builderVariable, data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar))); + // [jdk9] We add an unneccessary (V) cast here. Not doing so gives an error in javac (build 9-ea+156-jigsaw-nightly-h6072-20170212): + // error: method put in interface Map<K#2,V#2> cannot be applied to given types; + arg2 = maker.TypeCast(createTypeArgs(2, false, builderType, data.getTypeArgs(), source).get(1), arg2); JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2))); JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0)); JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true, builderVariable)); diff --git a/src/core/lombok/package-info.java b/src/core/lombok/package-info.java index 5e34c914..1c01dd0d 100644 --- a/src/core/lombok/package-info.java +++ b/src/core/lombok/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,6 @@ * <li>{@code lombok.experimental} – This package contains lombok features that are new or likely to change before committing to long-term support. * </ul> * - * @see <a href="https://projectlombok.org/features/index.html">Lombok features</a> + * @see <a href="https://projectlombok.org/features/all">Lombok features</a> */ package lombok; diff --git a/src/core/lombok/val.java b/src/core/lombok/val.java index a398fd34..a69d514c 100644 --- a/src/core/lombok/val.java +++ b/src/core/lombok/val.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2013 The Project Lombok Authors. + * Copyright (C) 2010-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,7 @@ package lombok; * <p> * Note that this is an annotation type because {@code val x = 10;} will be desugared to {@code @val final int x = 10;} * <p> - * Complete documentation is found at <a href="https://projectlombok.org/features/val.html">the project lombok features page for @val</a>. + * Complete documentation is found at <a href="https://projectlombok.org/features/val">the project lombok features page for @val</a>. */ public @interface val { } diff --git a/src/core/lombok/var.java b/src/core/lombok/var.java new file mode 100644 index 00000000..63a70213 --- /dev/null +++ b/src/core/lombok/var.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010-2018 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok; + +/** + * Use {@code var} as the type of any local variable declaration (even in a {@code for} statement), and the type will be inferred from the initializing expression + * (any further assignments to the variable are not involved in this type inference). + * <p> + * For example: {@code var x = 10.0;} will infer {@code double}, and {@code var y = new ArrayList<String>();} will infer {@code ArrayList<String>}. + * <p> + * Note that this is an annotation type because {@code var x = 10;} will be desugared to {@code @var int x = 10;} + * <p> + * Complete documentation is found at <a href="https://projectlombok.org/features/var">the project lombok features page for @var</a>. + */ +public @interface var { +} diff --git a/src/core9/module-info.java b/src/core9/module-info.java new file mode 100644 index 00000000..87f819e2 --- /dev/null +++ b/src/core9/module-info.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +module lombok { + requires java.compiler; + requires java.instrument; + requires jdk.unsupported; + + exports lombok; + exports lombok.experimental; + exports lombok.extern.apachecommons; + exports lombok.extern.java; + exports lombok.extern.jbosslog; + exports lombok.extern.log4j; + exports lombok.extern.slf4j; + + provides javax.annotation.processing.Processor with lombok.launch.AnnotationProcessorHider.AnnotationProcessor; + provides org.mapstruct.ap.spi.AstModifyingAnnotationProcessor with lombok.launch.AnnotationProcessorHider.AstModificationNotifier; +} + diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java index ead4aa60..0d887cb9 100644 --- a/src/delombok/lombok/delombok/Delombok.java +++ b/src/delombok/lombok/delombok/Delombok.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2015 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,12 +30,14 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.Writer; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; @@ -43,15 +45,23 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; import javax.tools.DiagnosticListener; +import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import lombok.Lombok; import lombok.javac.CommentCatcher; +import lombok.javac.Javac; import lombok.javac.LombokOptions; +import lombok.javac.apt.LombokProcessor; +import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.comp.Todo; +import com.sun.tools.javac.file.BaseFileManager; +import com.sun.tools.javac.main.Arguments; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.util.Context; @@ -466,26 +476,83 @@ public class Delombok { return out; } + private static final Field MODULE_FIELD = getModuleField(); + private static Field getModuleField() { + try { + return JCCompilationUnit.class.getField("modle"); + } catch (NoSuchFieldException e) { + return null; + } catch (SecurityException e) { + return null; + } + } + public boolean delombok() throws IOException { LombokOptions options = LombokOptionsFactory.getDelombokOptions(context); options.deleteLombokAnnotations(); options.putJavacOption("ENCODING", charset.name()); - if (classpath != null) options.putJavacOption("CLASSPATH", classpath); + if (classpath != null) options.putJavacOption("CLASSPATH", unpackClasspath(classpath)); if (sourcepath != null) options.putJavacOption("SOURCEPATH", sourcepath); - if (bootclasspath != null) options.putJavacOption("BOOTCLASSPATH", bootclasspath); + if (bootclasspath != null) options.putJavacOption("BOOTCLASSPATH", unpackClasspath(bootclasspath)); options.setFormatPreferences(new FormatPreferences(formatPrefs)); options.put("compilePolicy", "check"); + if (Javac.getJavaCompilerVersion() >= 9) { + Arguments args = Arguments.instance(context); + List<String> argsList = new ArrayList<String>(); + if (classpath != null) { + argsList.add("--class-path"); + argsList.add(options.get("--class-path")); + } + if (sourcepath != null) { + argsList.add("--source-path"); + argsList.add(options.get("--source-path")); + } + if (bootclasspath != null) { + argsList.add("--boot-class-path"); + argsList.add(options.get("--boot-class-path")); + } + if (charset != null) { + argsList.add("-encoding"); + argsList.add(charset.name()); + } + String[] argv = argsList.toArray(new String[0]); + args.init("javac", argv); + } + CommentCatcher catcher = CommentCatcher.create(context); JavaCompiler compiler = catcher.getCompiler(); List<JCCompilationUnit> roots = new ArrayList<JCCompilationUnit>(); Map<JCCompilationUnit, File> baseMap = new IdentityHashMap<JCCompilationUnit, File>(); - compiler.initProcessAnnotations(Collections.singleton(new lombok.javac.apt.LombokProcessor())); + Set<LombokProcessor> processors = Collections.singleton(new lombok.javac.apt.LombokProcessor()); + + if (Javac.getJavaCompilerVersion() >= 9) { + JavaFileManager jfm_ = context.get(JavaFileManager.class); + if (jfm_ instanceof BaseFileManager) { + Arguments args = Arguments.instance(context); + ((BaseFileManager) jfm_).setContext(context); // reinit with options + ((BaseFileManager) jfm_).handleOptions(args.getDeferredFileManagerOptions()); + } + } + + if (Javac.getJavaCompilerVersion() < 9) { + compiler.initProcessAnnotations(processors); + } else { + compiler.initProcessAnnotations(processors, Collections.<JavaFileObject>emptySet(), Collections.<String>emptySet()); + } + + Object unnamedModule = null; + if (Javac.getJavaCompilerVersion() >= 9) unnamedModule = Symtab.instance(context).unnamedModule; for (File fileToParse : filesToParse) { - @SuppressWarnings("deprecation") JCCompilationUnit unit = compiler.parse(fileToParse.getAbsolutePath()); + JCCompilationUnit unit = compiler.parse(fileToParse.getAbsolutePath()); + if (Javac.getJavaCompilerVersion() >= 9) try { + MODULE_FIELD.set(unit, unnamedModule); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } baseMap.put(unit, fileToBase.get(fileToParse)); roots.add(unit); } @@ -498,9 +565,19 @@ public class Delombok { catcher.setComments(unit, new DocCommentIntegrator().integrate(catcher.getComments(unit), unit)); } + if (Javac.getJavaCompilerVersion() >= 9) { + compiler.initModules(com.sun.tools.javac.util.List.from(roots.toArray(new JCCompilationUnit[0]))); + } com.sun.tools.javac.util.List<JCCompilationUnit> trees = compiler.enterTrees(toJavacList(roots)); - JavaCompiler delegate = compiler.processAnnotations(trees); + JavaCompiler delegate; + if (Javac.getJavaCompilerVersion() < 9) { + delegate = compiler.processAnnotations(trees, com.sun.tools.javac.util.List.<String>nil()); + } else { + delegate = compiler; + Collection<String> c = com.sun.tools.javac.util.List.nil(); + compiler.processAnnotations(trees, c); + } Object care = callAttributeMethodOnJavaCompiler(delegate, delegate.todo); @@ -529,6 +606,29 @@ public class Delombok { return true; } + private String unpackClasspath(String cp) { + String[] parts = cp.split(Pattern.quote(File.pathSeparator)); + StringBuilder out = new StringBuilder(); + for (String p : parts) { + if (!p.endsWith("*")) { + if (out.length() > 0) out.append(File.pathSeparator); + out.append(p); + continue; + } + File f = new File(p.substring(0, p.length() - 2)); + File[] files = f.listFiles(); + if (files == null) continue; + for (File file : files) { + if (file.isFile()) { + if (out.length() > 0) out.append(File.pathSeparator); + out.append(p, 0, p.length() - 1); + out.append(file.getName()); + } + } + } + return out.toString(); + } + private static Method attributeMethod; /** Method is needed because the call signature has changed between javac6 and javac7; no matter what we compile against, using delombok in the other means VerifyErrors. */ private static Object callAttributeMethodOnJavaCompiler(JavaCompiler compiler, Todo arg) { diff --git a/src/delombok/lombok/delombok/DocCommentIntegrator.java b/src/delombok/lombok/delombok/DocCommentIntegrator.java index c66ff0ec..bab0abd8 100644 --- a/src/delombok/lombok/delombok/DocCommentIntegrator.java +++ b/src/delombok/lombok/delombok/DocCommentIntegrator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,7 @@ import java.util.regex.Pattern; import lombok.javac.CommentInfo; import lombok.javac.Javac; +import lombok.javac.PackageName; import lombok.javac.handlers.JavacHandlerUtil; import com.sun.tools.javac.parser.Tokens.Comment; @@ -73,7 +74,7 @@ public class DocCommentIntegrator { return out; } - private static final Pattern CONTENT_STRIPPER = Pattern.compile("^(?:\\s*\\*)?[ \\t]*(.*?)$", Pattern.MULTILINE); + private static final Pattern CONTENT_STRIPPER = Pattern.compile("^(?:\\s*\\*)?(.*?)$", Pattern.MULTILINE); @SuppressWarnings("unchecked") private boolean attach(JCCompilationUnit top, final JCTree node, CommentInfo cmt) { String docCommentContent = cmt.content; if (docCommentContent.startsWith("/**")) docCommentContent = docCommentContent.substring(3); @@ -120,7 +121,8 @@ public class DocCommentIntegrator { } private JCTree findJavadocableNodeOnOrAfter(JCCompilationUnit unit, int endPos) { - if (unit.pid != null && endPos <= unit.pid.pos) return null; + JCTree pid = PackageName.getPackageNode(unit); + if (pid != null && endPos <= pid.pos) return null; Iterator<JCTree> it = unit.defs.iterator(); while (it.hasNext()) { diff --git a/src/delombok/lombok/delombok/LombokOptionsFactory.java b/src/delombok/lombok/delombok/LombokOptionsFactory.java index 47921931..62dc953a 100644 --- a/src/delombok/lombok/delombok/LombokOptionsFactory.java +++ b/src/delombok/lombok/delombok/LombokOptionsFactory.java @@ -24,6 +24,7 @@ package lombok.delombok; import lombok.javac.Javac; import lombok.javac.Javac6BasedLombokOptions; import lombok.javac.Javac8BasedLombokOptions; +import lombok.javac.Javac9BasedLombokOptions; import lombok.javac.LombokOptions; import com.sun.tools.javac.util.Context; @@ -41,9 +42,15 @@ public class LombokOptionsFactory { @Override LombokOptions createAndRegisterOptions(Context context) { return Javac8BasedLombokOptions.replaceWithDelombokOptions(context); } + }, + + JDK9 { + @Override LombokOptions createAndRegisterOptions(Context context) { + return Javac9BasedLombokOptions.replaceWithDelombokOptions(context); + } }; - abstract LombokOptions createAndRegisterOptions(Context context); + abstract LombokOptions createAndRegisterOptions(Context context); } public static LombokOptions getDelombokOptions(Context context) { @@ -53,8 +60,10 @@ public class LombokOptionsFactory { LombokOptions options; if (Javac.getJavaCompilerVersion() < 8) { options = LombokOptionCompilerVersion.JDK7_AND_LOWER.createAndRegisterOptions(context); - } else { + } else if (Javac.getJavaCompilerVersion() == 8) { options = LombokOptionCompilerVersion.JDK8.createAndRegisterOptions(context); + } else { + options = LombokOptionCompilerVersion.JDK9.createAndRegisterOptions(context); } return options; } diff --git a/src/delombok/lombok/delombok/PrettyPrinter.java b/src/delombok/lombok/delombok/PrettyPrinter.java index b5064a33..4261a558 100644 --- a/src/delombok/lombok/delombok/PrettyPrinter.java +++ b/src/delombok/lombok/delombok/PrettyPrinter.java @@ -92,6 +92,7 @@ import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Position; import lombok.javac.CommentInfo; +import lombok.javac.PackageName; import lombok.javac.CommentInfo.EndConnection; import lombok.javac.CommentInfo.StartConnection; import lombok.javac.JavacTreeMaker.TreeTag; @@ -457,11 +458,12 @@ public class PrettyPrinter extends JCTree.Visitor { @Override public void visitTopLevel(JCCompilationUnit tree) { printDocComment(tree); - if (tree.pid != null) { + JCTree n = PackageName.getPackageNode(tree); + if (n != null) { consumeComments(tree); aPrint("package "); - print(tree.pid); - println(";", tree.pid); + print(n); + println(";", n); } boolean first = true; @@ -811,6 +813,11 @@ public class PrettyPrinter extends JCTree.Visitor { print(tree.encl); print("."); } + boolean moveFirstParameter = tree.args.nonEmpty() && tree.args.head instanceof JCUnary && tree.args.head.toString().startsWith("<*nullchk*>"); + if (moveFirstParameter) { + print(((JCUnary) tree.args.head).arg); + print("."); + } print("new "); if (!tree.typeargs.isEmpty()) { @@ -820,7 +827,11 @@ public class PrettyPrinter extends JCTree.Visitor { } print(tree.clazz); print("("); - print(tree.args, ", "); + if (moveFirstParameter) { + print(tree.args.tail, ", "); + } else { + print(tree.args, ", "); + } print(")"); if (tree.def != null) { Name previousTypeName = currentTypeName; @@ -1431,22 +1442,19 @@ public class PrettyPrinter extends JCTree.Visitor { 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; + } else if ("JCPackageDecl".equals(simpleName)) { + // Starting with JDK9, this is inside the import list, but we've already printed it. Just ignore it. + } else { + throw new AssertionError("Unhandled tree type: " + tree.getClass() + ": " + tree); } - - throw new AssertionError("Unhandled tree type: " + tree.getClass() + ": " + tree); } private void printMemberReference0(JCTree tree) { diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java index 135b5c77..33d7a496 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java +++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java @@ -23,6 +23,7 @@ package lombok.eclipse.agent; import static lombok.patcher.scripts.ScriptBuilder.*; +import java.io.File; import java.lang.instrument.Instrumentation; import java.net.URLClassLoader; import java.security.ProtectionDomain; @@ -86,7 +87,7 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { } }); - final boolean forceBaseResourceNames = !"".equals(System.getProperty("shadow.override.lombok", "")); + final boolean forceBaseResourceNames = shouldForceBaseResourceNames(); sm.setTransplantMapper(new TransplantMapper() { public String mapResourceName(int classFileFormatVersion, String resourceName) { if (classFileFormatVersion < 50 || forceBaseResourceNames) return resourceName; @@ -125,6 +126,15 @@ public class EclipsePatcher implements AgentLauncher.AgentLaunchable { if (reloadExistingClasses) sm.reloadClasses(instrumentation); } + private static boolean shouldForceBaseResourceNames() { + String shadowOverride = System.getProperty("shadow.override.lombok", ""); + if (shadowOverride == null || shadowOverride.length() == 0) return false; + for (String part : shadowOverride.split("\\s*" + (File.pathSeparatorChar == ';' ? ";" : ":") + "\\s*")) { + if (part.equalsIgnoreCase("lombok.jar")) return false; + } + return true; + } + private static void patchRenameField(ScriptManager sm) { /* RefactoringSearchEngine.search will not return results when renaming field and Data Annotation is present. Return a fake Element to make checks pass */ sm.addScript(ScriptBuilder.wrapMethodCall() diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java index e4dd7b26..3da37869 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 The Project Lombok Authors. + * Copyright (C) 2010-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -212,7 +212,7 @@ public class PatchVal { } private static boolean isVar(LocalDeclaration local, BlockScope scope) { - return is(local.type, scope, "lombok.experimental.var"); + return is(local.type, scope, "lombok.experimental.var") || is(local.type, scope, "lombok.var"); } private static boolean isVal(LocalDeclaration local, BlockScope scope) { diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java index 505eb767..46237dcf 100644 --- a/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java +++ b/src/eclipseAgent/lombok/eclipse/agent/PatchValEclipse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 The Project Lombok Authors. + * Copyright (C) 2010-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -103,7 +103,7 @@ public class PatchValEclipse { } private static boolean couldBeVar(TypeReference type) { - return PatchVal.couldBe("lombok.experimental.var", type); + return PatchVal.couldBe("lombok.experimental.var", type) || PatchVal.couldBe("lombok.var", type); } public static void addFinalAndValAnnotationToSingleVariableDeclaration(Object converter, SingleVariableDeclaration out, LocalDeclaration in) { diff --git a/src/installer/lombok/installer/InstallerGUI.java b/src/installer/lombok/installer/InstallerGUI.java index b96fec9c..231e2d3c 100644 --- a/src/installer/lombok/installer/InstallerGUI.java +++ b/src/installer/lombok/installer/InstallerGUI.java @@ -171,6 +171,7 @@ public class InstallerGUI { container.add(buttonBar, constraints); container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415)); + container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415)); return container; } @@ -195,6 +196,7 @@ public class InstallerGUI { JLabel title; container.add(title = new JLabel(SUCCESS_TITLE), constraints); title.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH - 82, 20)); + title.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH - 82, 20)); constraints.gridy = 1; constraints.insets = new Insets(8, 0, 0, 16); @@ -296,6 +298,7 @@ public class InstallerGUI { container.add(buttonBar, constraints); container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415)); + container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 415)); return container; } @@ -319,6 +322,7 @@ public class InstallerGUI { constraints.gridy = 2; container.add(example, constraints); container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 105)); + container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 105)); return container; } @@ -507,6 +511,7 @@ public class InstallerGUI { container.add(uninstallPlaceholder, constraints); container.setPreferredSize(new Dimension(INSTALLER_WINDOW_WIDTH, 296)); + container.setMinimumSize(new Dimension(INSTALLER_WINDOW_WIDTH, 296)); return container; } diff --git a/src/installer/lombok/installer/eclipse/EclipseProductLocation.java b/src/installer/lombok/installer/eclipse/EclipseProductLocation.java index 886e3e85..aa97a3e5 100644 --- a/src/installer/lombok/installer/eclipse/EclipseProductLocation.java +++ b/src/installer/lombok/installer/eclipse/EclipseProductLocation.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2016 The Project Lombok Authors. + * Copyright (C) 2009-2017 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -256,11 +256,11 @@ public final class EclipseProductLocation extends IdeLocation { */ @Override public String install() throws InstallException { - // For whatever reason, relative paths in your eclipse.ini file don't work on linux, but only for -javaagent. - // If someone knows how to fix this, please do so, as this current hack solution (putting the absolute path - // to the jar files in your eclipse.ini) means you can't move your eclipse around on linux without lombok - // breaking it. NB: rerunning lombok.jar installer and hitting 'update' will fix it if you do that. - boolean fullPathRequired = OsUtils.getOS() == OsUtils.OS.UNIX || System.getProperty("lombok.installer.fullpath") != null; + // On Linux, for whatever reason, relative paths in your eclipse.ini file don't work, but only for -javaagent. + // On Windows, since the Oomph, the generated shortcut starts in the wrong directory. + // So the default is to use absolute paths, breaking lombok when you move the eclipse directory. + // Or not break when you copy your directory, but break later when you remove the original one. + boolean fullPathRequired = !"false".equals(System.getProperty("lombok.installer.fullpath", "true")); boolean installSucceeded = false; StringBuilder newContents = new StringBuilder(); diff --git a/src/installer/lombok/installer/eclipse/STS4LocationProvider.java b/src/installer/lombok/installer/eclipse/STS4LocationProvider.java new file mode 100644 index 00000000..47a07bdd --- /dev/null +++ b/src/installer/lombok/installer/eclipse/STS4LocationProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.installer.eclipse; + +import java.util.Arrays; +import java.util.Collections; + +import lombok.installer.IdeLocationProvider; + +import org.mangosdk.spi.ProviderFor; + +@ProviderFor(IdeLocationProvider.class) +public class STS4LocationProvider extends EclipseProductLocationProvider { + + private static final EclipseProductDescriptor STS4 = new StandardProductDescriptor("Spring Tools Suite 4", + "SpringToolSuite4", + "sts", + STS4LocationProvider.class.getResource("STS.png"), + Collections.unmodifiableList(Arrays.asList("springsource", "spring-tool-suite")) + ); + + public STS4LocationProvider() { + super(STS4); + } +} diff --git a/src/j9stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java b/src/j9stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java new file mode 100644 index 00000000..ffb99030 --- /dev/null +++ b/src/j9stubs/org/mapstruct/ap/spi/AstModifyingAnnotationProcessor.java @@ -0,0 +1,48 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.ap.spi; + +import javax.lang.model.type.TypeMirror; + +/** + * A contract to be implemented by other annotation processors which - against the design philosophy of JSR 269 - alter + * the types under compilation. + * <p> + * This contract will be queried by MapStruct when examining types referenced by mappers to be generated, most notably + * the source and target types of mapping methods. If at least one AST-modifying processor announces further changes to + * such type, the generation of the affected mapper(s) will be deferred to a future round in the annnotation processing + * cycle. + * <p> + * Implementations are discovered via the service loader, i.e. a JAR providing an AST-modifying processor needs to + * declare its implementation in a file {@code META-INF/services/org.mapstruct.ap.spi.AstModifyingAnnotationProcessor}. + * + * @author Gunnar Morling + */ +//@org.mapstruct.util.Experimental +public interface AstModifyingAnnotationProcessor { + + /** + * Whether the specified type has been fully processed by this processor or not (i.e. this processor will amend the + * given type's structure after this invocation). + * + * @param type The type of interest + * @return {@code true} if this processor has fully processed the given type, {@code false} otherwise. + */ + boolean isTypeComplete(TypeMirror type); +}
\ No newline at end of file diff --git a/src/launch/lombok/launch/AnnotationProcessor.java b/src/launch/lombok/launch/AnnotationProcessor.java index 05c900ab..c4f922b9 100644 --- a/src/launch/lombok/launch/AnnotationProcessor.java +++ b/src/launch/lombok/launch/AnnotationProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2017 The Project Lombok Authors. + * Copyright (C) 2014-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,6 +21,7 @@ */ package lombok.launch; +import java.lang.reflect.Field; import java.util.Set; import javax.annotation.processing.AbstractProcessor; @@ -37,6 +38,8 @@ import javax.lang.model.type.TypeMirror; import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; +import sun.misc.Unsafe; + class AnnotationProcessorHider { public static class AstModificationNotifier implements AstModifyingAnnotationProcessor { @Override public boolean isTypeComplete(TypeMirror type) { @@ -65,11 +68,33 @@ class AnnotationProcessorHider { } @Override public void init(ProcessingEnvironment processingEnv) { + disableJava9SillyWarning(); AstModificationNotifierData.lombokInvoked = true; instance.init(processingEnv); super.init(processingEnv); } + // sunapi suppresses javac's warning about using Unsafe; 'all' suppresses eclipse's warning about the unspecified 'sunapi' key. Leave them both. + // Yes, javac's definition of the word 'all' is quite contrary to what the dictionary says it means. 'all' does NOT include 'sunapi' according to javac. + @SuppressWarnings({"sunapi", "all"}) + private void disableJava9SillyWarning() { + // JVM9 complains about using reflection to access packages from a module that aren't exported. This makes no sense; the whole point of reflection + // is to get past such issues. The only comment from the jigsaw team lead on this was some unspecified mumbling about security which makes no sense, + // as the SecurityManager is invoked to check such things. Therefore this warning is a bug, so we shall patch java to fix it. + + try { + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + Unsafe u = (Unsafe) theUnsafe.get(null); + + Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger"); + Field logger = cls.getDeclaredField("logger"); + u.putObjectVolatile(cls, u.staticFieldOffset(logger), null); + } catch (Throwable t) { + // We shall ignore it; the effect of this code failing is that the user gets to see a warning they remove with various --add-opens magic. + } + } + @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { return instance.process(annotations, roundEnv); } @@ -82,7 +107,7 @@ class AnnotationProcessorHider { ClassLoader cl = Main.createShadowClassLoader(); try { Class<?> mc = cl.loadClass("lombok.core.AnnotationProcessor"); - return (AbstractProcessor) mc.newInstance(); + return (AbstractProcessor) mc.getDeclaredConstructor().newInstance(); } catch (Throwable t) { if (t instanceof Error) throw (Error) t; if (t instanceof RuntimeException) throw (RuntimeException) t; diff --git a/src/stubs/com/sun/tools/javac/code/Symbol.java b/src/stubs/com/sun/tools/javac/code/Symbol.java new file mode 100644 index 00000000..4aef63ad --- /dev/null +++ b/src/stubs/com/sun/tools/javac/code/Symbol.java @@ -0,0 +1,84 @@ +/* + * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these. + */ +package com.sun.tools.javac.code; + +import java.lang.annotation.Annotation; +import java.util.Set; + +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ElementVisitor; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; + +import com.sun.tools.javac.util.Name; + +public abstract class Symbol implements Element { + public Type type; + public Name name; + + public long flags() { return 0; } + public boolean isStatic() { return false; } + public boolean isConstructor() { return false; } + public boolean isLocal() { return false; } + public Name flatName() { return null; } + public Name getQualifiedName() { return null; } + public <A extends Annotation> A[] getAnnotationsByType(Class<A> annoType) { return null; } + @Override public java.util.List<Attribute.Compound> getAnnotationMirrors() { return null; } + @Override public TypeMirror asType() { return null; } + public <A extends java.lang.annotation.Annotation> A getAnnotation(Class<A> annoType) { return null; } + @Override public Name getSimpleName() { return null; } + @Override public java.util.List<Symbol> getEnclosedElements() { return null; } + @Override public Element getEnclosingElement() { return null; } + + public static abstract class TypeSymbol extends Symbol {} + + public static class MethodSymbol extends Symbol implements ExecutableElement { + public MethodSymbol(long flags, Name name, Type type, Symbol owner) {} + @Override public ElementKind getKind() { return null; } + @Override public Set<Modifier> getModifiers() { return null; } + @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; } + @Override public java.util.List<? extends TypeParameterElement> getTypeParameters() { return null; } + @Override public TypeMirror getReturnType() { return null; } + @Override public java.util.List<? extends VariableElement> getParameters() { return null; } + @Override public boolean isVarArgs() { return false; } + @Override public java.util.List<? extends TypeMirror> getThrownTypes() { return null; } + @Override public AnnotationValue getDefaultValue() { return null; } + public TypeMirror getReceiverType() { return null; } + public boolean isDefault() { return false; } + public com.sun.tools.javac.util.List<VarSymbol> params() { return null; } + } + + public static class VarSymbol extends Symbol implements VariableElement { + public Type type; + @Override public ElementKind getKind() { return null; } + @Override public Set<Modifier> getModifiers() { return null; } + @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; } + @Override public Object getConstantValue() { return null; } + } + + public static class ClassSymbol extends TypeSymbol implements TypeElement { + @Override public Name getQualifiedName() { return null; } + @Override public java.util.List<? extends TypeMirror> getInterfaces() { return null; } + @Override public TypeMirror getSuperclass() { return null; } + @Override public ElementKind getKind() { return null; } + @Override public Set<Modifier> getModifiers() { return null; } + @Override public NestingKind getNestingKind() { return null; } + @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; } + @Override public java.util.List<? extends TypeParameterElement> getTypeParameters() { return null; } + } + + // JDK9 + public static class ModuleSymbol extends TypeSymbol { + @Override public ElementKind getKind() { return null; } + @Override public Set<Modifier> getModifiers() { return null; } + @Override public <R, P> R accept(ElementVisitor<R, P> v, P p) { return null; } + } +} diff --git a/src/stubs/com/sun/tools/javac/code/Symtab.java b/src/stubs/com/sun/tools/javac/code/Symtab.java new file mode 100644 index 00000000..2b524e4c --- /dev/null +++ b/src/stubs/com/sun/tools/javac/code/Symtab.java @@ -0,0 +1,20 @@ +/* + * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these. + */ +package com.sun.tools.javac.code; + +import com.sun.tools.javac.code.Symbol.ClassSymbol; +import com.sun.tools.javac.code.Symbol.ModuleSymbol; +import com.sun.tools.javac.util.Context; + +public class Symtab { + // Shared by JDK6-9 + public ClassSymbol methodClass; + public Type iterableType; + public Type objectType; + public static Symtab instance(Context context) {return null;} + public Type unknownType; + + // JDK 9 + public ModuleSymbol unnamedModule; +} diff --git a/src/stubs/com/sun/tools/javac/file/BaseFileManager.java b/src/stubs/com/sun/tools/javac/file/BaseFileManager.java index 7a2293d5..a56a2430 100644 --- a/src/stubs/com/sun/tools/javac/file/BaseFileManager.java +++ b/src/stubs/com/sun/tools/javac/file/BaseFileManager.java @@ -5,4 +5,14 @@ package com.sun.tools.javac.file; import javax.tools.JavaFileManager; -public abstract class BaseFileManager implements JavaFileManager{} +import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.util.Context; + +import java.nio.charset.Charset; +import java.util.Map; + +public abstract class BaseFileManager implements JavaFileManager { + protected BaseFileManager(Charset charset) {} + public void setContext(Context context) {} + public boolean handleOptions(Map<Option, String> deferredFileManagerOptions) { return false; } +} diff --git a/src/stubs/com/sun/tools/javac/main/Arguments.java b/src/stubs/com/sun/tools/javac/main/Arguments.java new file mode 100644 index 00000000..ea866b6e --- /dev/null +++ b/src/stubs/com/sun/tools/javac/main/Arguments.java @@ -0,0 +1,13 @@ +package com.sun.tools.javac.main; + +import java.util.Map; + +import com.sun.tools.javac.util.Context; + +public class Arguments { + public static final Context.Key<Arguments> argsKey = new Context.Key<Arguments>(); + public static Arguments instance(Context context) { return null; } + public void init(String ownName, String... argv) {} + public Map<Option, String> getDeferredFileManagerOptions() { return null; } + public boolean validate() { return false; } +} diff --git a/src/stubs/com/sun/tools/javac/main/JavaCompiler.java b/src/stubs/com/sun/tools/javac/main/JavaCompiler.java new file mode 100644 index 00000000..d0e7b38f --- /dev/null +++ b/src/stubs/com/sun/tools/javac/main/JavaCompiler.java @@ -0,0 +1,37 @@ +/* + * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these. + */ +package com.sun.tools.javac.main; + +import java.io.IOException; +import java.util.Collection; +import javax.annotation.processing.Processor; +import javax.tools.JavaFileObject; + +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.comp.Todo; + +public class JavaCompiler { + // Shared by JDK6-9 + public boolean keepComments; + public boolean genEndPos; + public Todo todo; + + public JavaCompiler(Context context) {} + public int errorCount() { return 0; } + public static String version() { return "<stub>"; } + public JCCompilationUnit parse(String fileName) throws IOException { return null; } + public List<JCCompilationUnit> enterTrees(List<JCCompilationUnit> roots) {return null;} + + //JDK up to 8 + public void initProcessAnnotations(Iterable<? extends Processor> processors) throws IOException {} + public JavaCompiler processAnnotations(List<JCCompilationUnit> roots, List<String> classnames) {return this;} + + // JDK 9 + public void initProcessAnnotations(Iterable<? extends Processor> processors, Collection<? extends JavaFileObject> initialFiles, Collection<String> initialClassNames) {} + public void processAnnotations(List<JCCompilationUnit> roots, Collection<String> classnames) {} + public void close() {} + public List<JCCompilationUnit> initModules(List<JCCompilationUnit> roots) { return null; } +} diff --git a/src/stubs/com/sun/tools/javac/main/Option.java b/src/stubs/com/sun/tools/javac/main/Option.java index f3229c78..ae955772 100644 --- a/src/stubs/com/sun/tools/javac/main/Option.java +++ b/src/stubs/com/sun/tools/javac/main/Option.java @@ -7,4 +7,5 @@ package com.sun.tools.javac.main; public enum Option { ; public String text; + public String primaryName; } diff --git a/src/stubs/com/sun/tools/javac/parser/JavacParser.java b/src/stubs/com/sun/tools/javac/parser/JavacParser.java index da42f37a..4f1f3380 100644 --- a/src/stubs/com/sun/tools/javac/parser/JavacParser.java +++ b/src/stubs/com/sun/tools/javac/parser/JavacParser.java @@ -1,3 +1,6 @@ +/* + * These are stub versions of various bits of javac-internal API (for various different versions of javac). Lombok is compiled against these. + */ package com.sun.tools.javac.parser; import com.sun.tools.javac.tree.JCTree; @@ -6,6 +9,9 @@ public class JavacParser { protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions) { } + protected JavacParser(ParserFactory fac, Lexer S, boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions, boolean parseModuleInfo) { + } + public JCTree.JCCompilationUnit parseCompilationUnit() { return null; } diff --git a/src/stubs/com/sun/tools/javac/util/Options.java b/src/stubs/com/sun/tools/javac/util/Options.java new file mode 100644 index 00000000..e7ba8960 --- /dev/null +++ b/src/stubs/com/sun/tools/javac/util/Options.java @@ -0,0 +1,20 @@ +package com.sun.tools.javac.util; + +import java.util.Set; + +import com.sun.tools.javac.main.Option; +import com.sun.tools.javac.main.OptionName; +import com.sun.tools.javac.main.JavacOption; + +public class Options { + public Options(Context context) {} + public static final Context.Key<Options> optionsKey = new Context.Key<Options>(); + public static Options instance(Context context) { return null; } + public String get(String key) { return null; } + public String get(Option opt) { return null; } + public String get(OptionName name) { return null; } + public String get(JavacOption.Option opt) { return null; } + public void putAll(Options o) {} + public void put(String key, String value) {} + public Set<String> keySet() { return null; } +} diff --git a/src/stubsstubs/com/sun/tools/javac/code/Attribute.java b/src/stubsstubs/com/sun/tools/javac/code/Attribute.java new file mode 100644 index 00000000..29bd54a9 --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/code/Attribute.java @@ -0,0 +1,15 @@ +package com.sun.tools.javac.code; + +import java.util.Map; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.DeclaredType; + +public abstract class Attribute { + public static class Compound extends Attribute implements AnnotationMirror { + public DeclaredType getAnnotationType() { return null; } + public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues() { return null; } + } +}
\ No newline at end of file diff --git a/src/stubsstubs/com/sun/tools/javac/code/Type.java b/src/stubsstubs/com/sun/tools/javac/code/Type.java new file mode 100644 index 00000000..c130ae9c --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/code/Type.java @@ -0,0 +1,3 @@ +package com.sun.tools.javac.code; + +public class Type {}
\ No newline at end of file diff --git a/src/stubsstubs/com/sun/tools/javac/comp/Todo.java b/src/stubsstubs/com/sun/tools/javac/comp/Todo.java new file mode 100644 index 00000000..006a7fd3 --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/comp/Todo.java @@ -0,0 +1,3 @@ +package com.sun.tools.javac.comp; + +public class Todo {}
\ No newline at end of file diff --git a/src/stubsstubs/com/sun/tools/javac/main/JavacOption.java b/src/stubsstubs/com/sun/tools/javac/main/JavacOption.java new file mode 100644 index 00000000..8e74d3d3 --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/main/JavacOption.java @@ -0,0 +1,5 @@ +package com.sun.tools.javac.main; + +public class JavacOption { + public static class Option {} +}
\ No newline at end of file diff --git a/src/stubsstubs/com/sun/tools/javac/main/Option.java b/src/stubsstubs/com/sun/tools/javac/main/Option.java new file mode 100644 index 00000000..45988e4c --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/main/Option.java @@ -0,0 +1,3 @@ +package com.sun.tools.javac.main; + +public class Option {}
\ No newline at end of file diff --git a/src/stubsstubs/com/sun/tools/javac/main/OptionName.java b/src/stubsstubs/com/sun/tools/javac/main/OptionName.java new file mode 100644 index 00000000..b1866633 --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/main/OptionName.java @@ -0,0 +1,3 @@ +package com.sun.tools.javac.main; + +public class OptionName {}
\ No newline at end of file diff --git a/src/stubsstubs/com/sun/tools/javac/util/Context.java b/src/stubsstubs/com/sun/tools/javac/util/Context.java new file mode 100644 index 00000000..a090714e --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/util/Context.java @@ -0,0 +1,5 @@ +package com.sun.tools.javac.util; + +public class Context { + public static class Key<T> {} +}
\ No newline at end of file diff --git a/src/stubsstubs/com/sun/tools/javac/util/List.java b/src/stubsstubs/com/sun/tools/javac/util/List.java new file mode 100644 index 00000000..16418a2b --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/util/List.java @@ -0,0 +1,3 @@ +package com.sun.tools.javac.util; + +public class List<T> {}
\ No newline at end of file diff --git a/src/stubsstubs/com/sun/tools/javac/util/Name.java b/src/stubsstubs/com/sun/tools/javac/util/Name.java new file mode 100644 index 00000000..c0e81926 --- /dev/null +++ b/src/stubsstubs/com/sun/tools/javac/util/Name.java @@ -0,0 +1,8 @@ +package com.sun.tools.javac.util; + +public class Name implements javax.lang.model.element.Name { + public boolean contentEquals(CharSequence cs) { return false; } + public int length() { return 0; } + public char charAt(int idx) { return '\0'; } + public CharSequence subSequence(int a, int b) { return null; } +}
\ No newline at end of file diff --git a/src/utils/lombok/javac/CommentCatcher.java b/src/utils/lombok/javac/CommentCatcher.java index c32da68b..afbd7b52 100644 --- a/src/utils/lombok/javac/CommentCatcher.java +++ b/src/utils/lombok/javac/CommentCatcher.java @@ -95,8 +95,10 @@ public class CommentCatcher { parserFactory = Class.forName("lombok.javac.java6.CommentCollectingParserFactory"); } else if (javaCompilerVersion == 7) { parserFactory = Class.forName("lombok.javac.java7.CommentCollectingParserFactory"); - } else { + } else if (javaCompilerVersion == 8) { parserFactory = Class.forName("lombok.javac.java8.CommentCollectingParserFactory"); + } else { + parserFactory = Class.forName("lombok.javac.java9.CommentCollectingParserFactory"); } parserFactory.getMethod("setInCompiler", JavaCompiler.class, Context.class).invoke(null, compiler, context); } catch (InvocationTargetException e) { diff --git a/src/utils/lombok/javac/Javac.java b/src/utils/lombok/javac/Javac.java index 6f99463b..9ff4d22f 100644 --- a/src/utils/lombok/javac/Javac.java +++ b/src/utils/lombok/javac/Javac.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2015 The Project Lombok Authors. + * Copyright (C) 2009-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -63,8 +63,8 @@ public class Javac { /** Matches any of the 8 primitive names, such as {@code boolean}. */ private static final Pattern PRIMITIVE_TYPE_NAME_PATTERN = Pattern.compile("^(boolean|byte|short|int|long|float|double|char)$"); - private static final Pattern VERSION_PARSER = Pattern.compile("^(\\d{1,6})\\.(\\d{1,6}).*$"); - private static final Pattern SOURCE_PARSER = Pattern.compile("^JDK(\\d{1,6})_(\\d{1,6}).*$"); + private static final Pattern VERSION_PARSER = Pattern.compile("^(\\d{1,6})\\.?(\\d{1,6})?.*$"); + private static final Pattern SOURCE_PARSER = Pattern.compile("^JDK(\\d{1,6})_?(\\d{1,6})?.*$"); private static final AtomicInteger compilerVersion = new AtomicInteger(-1); @@ -79,11 +79,11 @@ public class Javac { Matcher m = VERSION_PARSER.matcher(JavaCompiler.version()); if (m.matches()) { int major = Integer.parseInt(m.group(1)); - int minor = Integer.parseInt(m.group(2)); if (major == 1) { - compilerVersion.set(minor); - return minor; + int minor = Integer.parseInt(m.group(2)); + return setVersion(minor); } + if (major >= 9) return setVersion(major); } } @@ -92,16 +92,19 @@ public class Javac { Matcher m = SOURCE_PARSER.matcher(name); if (m.matches()) { int major = Integer.parseInt(m.group(1)); - int minor = Integer.parseInt(m.group(2)); if (major == 1) { - compilerVersion.set(minor); - return minor; + int minor = Integer.parseInt(m.group(2)); + return setVersion(minor); } + if (major >= 9) return setVersion(major); } } - - compilerVersion.set(6); - return 6; + return setVersion(6); + } + + private static int setVersion(int version) { + compilerVersion.set(version); + return version; } private static final Class<?> DOCCOMMENTTABLE_CLASS; diff --git a/src/utils/lombok/javac/JavacTreeMaker.java b/src/utils/lombok/javac/JavacTreeMaker.java index 12baf5af..5f4fb09c 100644 --- a/src/utils/lombok/javac/JavacTreeMaker.java +++ b/src/utils/lombok/javac/JavacTreeMaker.java @@ -226,6 +226,7 @@ public class JavacTreeMaker { } public static TypeTag typeTag(Type t) { + if (t == null) return Javac.CTC_VOID; try { return new TypeTag(getFieldCached(FIELD_CACHE, t, "tag")); } catch (NoSuchFieldException e) { diff --git a/src/utils/lombok/javac/PackageName.java b/src/utils/lombok/javac/PackageName.java new file mode 100644 index 00000000..e4dd6b20 --- /dev/null +++ b/src/utils/lombok/javac/PackageName.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac; + +import java.lang.reflect.Method; + +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCIdent; + +// Supports JDK6-9 +public class PackageName { + private static final Method packageNameMethod = getPackageNameMethod(); + + private static Method getPackageNameMethod() { + try { + return JCCompilationUnit.class.getDeclaredMethod("getPackageName"); + } catch (Exception e) { + return null; + } + } + + public static String getPackageName(JCCompilationUnit cu) { + JCTree t = getPackageNode(cu); + return t != null ? t.toString() : null; + } + + public static JCTree getPackageNode(JCCompilationUnit cu) { + if (packageNameMethod != null) try { + Object pkg = packageNameMethod.invoke(cu); + return (pkg instanceof JCFieldAccess || pkg instanceof JCIdent) ? (JCTree) pkg : null; + } catch (Exception e) {} + return cu.pid instanceof JCFieldAccess || cu.pid instanceof JCIdent ? cu.pid : null; + } +} diff --git a/src/utils/lombok/javac/TreeMirrorMaker.java b/src/utils/lombok/javac/TreeMirrorMaker.java index 918a3242..8cd8cffe 100644 --- a/src/utils/lombok/javac/TreeMirrorMaker.java +++ b/src/utils/lombok/javac/TreeMirrorMaker.java @@ -90,12 +90,13 @@ public class TreeMirrorMaker extends TreeCopier<Void> { return Collections.unmodifiableMap(originalToCopy); } - // Monitor issue 205 and issue 694 when making changes here. + // Monitor the following issues when making changes here. + // - https://github.com/rzwitserloot/lombok/issues/278 + // - https://github.com/rzwitserloot/lombok/issues/729 @Override public JCTree visitVariable(VariableTree node, Void p) { JCVariableDecl original = node instanceof JCVariableDecl ? (JCVariableDecl) node : null; JCVariableDecl copy = (JCVariableDecl) super.visitVariable(node, p); if (original == null) return copy; - copy.sym = original.sym; if (copy.sym != null) copy.type = original.type; if (copy.type != null) { @@ -114,7 +115,7 @@ public class TreeMirrorMaker extends TreeCopier<Void> { return copy; } - // Fix for NPE in HandleVal. See http://code.google.com/p/projectlombok/issues/detail?id=299 + // Fix for NPE in HandleVal. See https://github.com/rzwitserloot/lombok/issues/372 // This and visitVariable is rather hacky but we're working around evident bugs or at least inconsistencies in javac. @Override public JCTree visitLabeledStatement(LabeledStatementTree node, Void p) { return node.getStatement().accept(this, p); diff --git a/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java index 45f865ad..2fdaddfe 100644 --- a/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java +++ b/src/utils/lombok/javac/java8/CommentCollectingParserFactory.java @@ -52,6 +52,16 @@ public class CommentCollectingParserFactory extends ParserFactory { //Either way this will work out. } + public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) { + ScannerFactory scannerFactory = ScannerFactory.instance(context); + Lexer lexer = scannerFactory.newScanner(input, true); + Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, keepEndPos); + return (JavacParser) x; + // CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either + //javac6's EndPosParser which extends Parser, or javac8's JavacParser which implements Parser. + //Either way this will work out. + } + public static void setInCompiler(JavaCompiler compiler, Context context) { context.put(CommentCollectingParserFactory.key(), (ParserFactory) null); Field field; diff --git a/src/utils/lombok/javac/java9/CommentCollectingParser.java b/src/utils/lombok/javac/java9/CommentCollectingParser.java new file mode 100644 index 00000000..307be405 --- /dev/null +++ b/src/utils/lombok/javac/java9/CommentCollectingParser.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013-2017 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.java9; + +import static lombok.javac.CommentCatcher.JCCompilationUnit_comments; + +import java.util.List; + +import lombok.javac.CommentInfo; +import lombok.javac.java8.CommentCollectingScanner; + +import com.sun.tools.javac.parser.JavacParser; +import com.sun.tools.javac.parser.Lexer; +import com.sun.tools.javac.parser.ParserFactory; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; + +class CommentCollectingParser extends JavacParser { + private final Lexer lexer; + + protected CommentCollectingParser(ParserFactory fac, Lexer S, + boolean keepDocComments, boolean keepLineMap, boolean keepEndPositions, boolean parseModuleInfo) { + super(fac, S, keepDocComments, keepLineMap, keepEndPositions, parseModuleInfo); + lexer = S; + } + + public JCCompilationUnit parseCompilationUnit() { + JCCompilationUnit result = super.parseCompilationUnit(); + if (lexer instanceof CommentCollectingScanner) { + List<CommentInfo> comments = ((CommentCollectingScanner)lexer).getComments(); + JCCompilationUnit_comments.set(result, comments); + } + return result; + } +}
\ No newline at end of file diff --git a/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java b/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java new file mode 100644 index 00000000..5af4a419 --- /dev/null +++ b/src/utils/lombok/javac/java9/CommentCollectingParserFactory.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013-2017 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.javac.java9; + +import java.lang.reflect.Field; + +import com.sun.tools.javac.main.JavaCompiler; +import com.sun.tools.javac.parser.JavacParser; +import com.sun.tools.javac.parser.Lexer; +import com.sun.tools.javac.parser.ParserFactory; +import com.sun.tools.javac.parser.ScannerFactory; +import com.sun.tools.javac.util.Context; + +public class CommentCollectingParserFactory extends ParserFactory { + private final Context context; + + static Context.Key<ParserFactory> key() { + return parserFactoryKey; + } + + protected CommentCollectingParserFactory(Context context) { + super(context); + this.context = context; + } + + public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap) { + return newParser(input, keepDocComments, keepEndPos, keepLineMap, false); + } + + public JavacParser newParser(CharSequence input, boolean keepDocComments, boolean keepEndPos, boolean keepLineMap, boolean parseModuleInfo) { + ScannerFactory scannerFactory = ScannerFactory.instance(context); + Lexer lexer = scannerFactory.newScanner(input, true); + Object x = new CommentCollectingParser(this, lexer, true, keepLineMap, keepEndPos, parseModuleInfo); + return (JavacParser) x; + // CCP is based on a stub which extends nothing, but at runtime the stub is replaced with either + //javac6's EndPosParser which extends Parser, or javac-9's JavacParser which implements Parser. + //Either way this will work out. + } + + public static void setInCompiler(JavaCompiler compiler, Context context) { + context.put(CommentCollectingParserFactory.key(), (ParserFactory) null); + Field field; + try { + field = JavaCompiler.class.getDeclaredField("parserFactory"); + field.setAccessible(true); + field.set(compiler, new CommentCollectingParserFactory(context)); + } catch (Exception e) { + throw new IllegalStateException("Could not set comment sensitive parser in the compiler", e); + } + } +}
\ No newline at end of file diff --git a/src/website/log4j.properties b/src/website/log4j.properties new file mode 100644 index 00000000..9cafcc3b --- /dev/null +++ b/src/website/log4j.properties @@ -0,0 +1,6 @@ +log4j.rootLogger=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n diff --git a/src/website/lombok/website/CompileChangelog.java b/src/website/lombok/website/CompileChangelog.java new file mode 100644 index 00000000..8912434e --- /dev/null +++ b/src/website/lombok/website/CompileChangelog.java @@ -0,0 +1,147 @@ +package lombok.website; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.petebevin.markdown.MarkdownProcessor; + +public class CompileChangelog { + public static void main(String[] args) { + String fileIn = args[0]; + String fileOut = args[1]; + boolean edge = args.length > 3 && "-edge".equals(args[2]); + boolean latest = args.length > 3 && "-latest".equals(args[2]); + String version = args.length > 3 ? args[3] : null; + + try { + FileInputStream in = new FileInputStream(fileIn); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + byte[] b = new byte[65536]; + while (true) { + int r = in.read(b); + if ( r == -1 ) break; + out.write(b, 0, r); + } + in.close(); + String markdown = new String(out.toByteArray(), "UTF-8"); + + String result; + if (edge) { + result = buildEdge(sectionByVersion(markdown, version)); + } else if (latest) { + result = buildLatest(sectionByVersion(markdown, version)); + } else { + result = markdownToHtml(sectionStartingAt(markdown, version)); + } + + FileOutputStream file = new FileOutputStream(fileOut); + file.write(result.getBytes("UTF-8")); + file.close(); + System.exit(0); + } catch (Throwable e) { + e.printStackTrace(); + System.exit(1); + } + } + + public static String getHtmlForEdge(File root, String edgeVersion) throws IOException { + File f = new File(root, "doc/changelog.markdown"); + String raw = readFile(f); + return buildEdge(sectionByVersion(raw, edgeVersion)); + } + + public static String getHtmlForLatest(File root, String latestVersion) throws IOException { + File f = new File(root, "doc/changelog.markdown"); + String raw = readFile(f); + return buildLatest(sectionByVersion(raw, latestVersion)); + } + + public static String getHtml(File root) throws IOException { + File f = new File(root, "doc/changelog.markdown"); + String raw = readFile(f); + return markdownToHtml(raw); + } + + public static String getHtmlStartingAtSection(File root, String version) throws IOException { + File f = new File(root, "doc/changelog.markdown"); + String raw = readFile(f); + return markdownToHtml(sectionStartingAt(raw, version)); + } + + private static String readFile(File f) throws IOException { + byte[] b = new byte[65536]; + FileInputStream in = new FileInputStream(f); + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + while (true) { + int r = in.read(b); + if ( r == -1 ) break; + out.write(b, 0, r); + } + in.close(); + return new String(out.toByteArray(), "UTF-8"); + } finally { + in.close(); + } + } + + private static String markdownToHtml(String markdown) { + return new MarkdownProcessor().markdown(markdown); + } + + private static String buildEdge(String section) { + String latest = section != null ? section : "* No changelog records for this edge release."; + return markdownToHtml(latest); + } + + private static String buildLatest(String section) { + String latest = section != null ? section : "* No changelog records for this release."; + String noIssueLinks = latest.replaceAll("\\[[^]]*[Ii]ssue[^]]*\\]\\([^)]*\\)", ""); + String noLinks = noIssueLinks.replaceAll("\\[([^]]*)\\]\\([^)]*\\)", "$1"); + return markdownToHtml(noLinks); + } + + private static String sectionStartingAt(String markdown, String version) { + if (version.toUpperCase().endsWith("-HEAD") || version.toUpperCase().endsWith("-EDGE")) { + version = version.substring(0, version.length() - 5); + } + + Pattern p = Pattern.compile("^.*###\\s*v(.*)$"); + BufferedReader br = new BufferedReader(new StringReader(markdown)); + StringBuilder out = new StringBuilder(); + int state = 0; + try { + for (String line = br.readLine(); line != null; line = br.readLine()) { + if (state < 2) { + Matcher m = p.matcher(line); + if (m.matches()) state = m.group(1).startsWith(version) ? 2 : 1; + } + if (state != 1) { + out.append(line); + out.append("\n"); + } + } + return out.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static String sectionByVersion(String markdown, String version) { + if (version.toUpperCase().endsWith("-HEAD") || version.toUpperCase().endsWith("-EDGE")) { + version = version.substring(0, version.length() - 5); + } + + Pattern p = Pattern.compile("(?is-m)^.*###\\s*v" + version + ".*?\n(.*?)(?:###\\s*v.*)?$"); + Matcher m = p.matcher(markdown); + return m.matches() ? m.group(1) : null; + } +}
\ No newline at end of file diff --git a/src/website/lombok/website/FetchCurrentVersion.java b/src/website/lombok/website/FetchCurrentVersion.java new file mode 100644 index 00000000..6c1ca639 --- /dev/null +++ b/src/website/lombok/website/FetchCurrentVersion.java @@ -0,0 +1,33 @@ +package lombok.website; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FetchCurrentVersion { + private FetchCurrentVersion() {} + + private static final Pattern VERSION_PATTERN = Pattern.compile("^.*<\\s*span\\s+id\\s*=\\s*[\"'](currentVersion|currentVersionFull)[\"'](?:\\s+style\\s*=\\s*[\"']display\\s*:\\s*none;?[\"'])?\\s*>\\s*([^\t<]+)\\s*<\\s*/\\s*span\\s*>.*$"); + + public static void main(String[] args) throws IOException { + System.out.print(fetchVersionFromSite(args.length == 0 || args[0].equals("full"))); + } + + public static String fetchVersionFromSite(boolean fetchFull) throws IOException { + InputStream in = new URL("https://projectlombok.org/download").openStream(); + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); + for (String line = br.readLine(); line != null; line = br.readLine()) { + Matcher m = VERSION_PATTERN.matcher(line); + if (m.matches() && m.group(1).equals("currentVersionFull") == fetchFull) return m.group(2); + } + throw new IOException("Expected a span with id 'currentVersion'"); + } finally { + in.close(); + } + } +} diff --git a/src/website/lombok/website/WebsiteMaker.java b/src/website/lombok/website/WebsiteMaker.java new file mode 100644 index 00000000..88556b97 --- /dev/null +++ b/src/website/lombok/website/WebsiteMaker.java @@ -0,0 +1,403 @@ +package lombok.website; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.lang.reflect.Method; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.java2html.Java2Html; +import freemarker.cache.FileTemplateLoader; +import freemarker.cache.TemplateLoader; +import freemarker.core.HTMLOutputFormat; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateExceptionHandler; + +public class WebsiteMaker { + private final String version, fullVersion; + private final File baseDir, outputDir; + + public WebsiteMaker(String version, String fullVersion, File baseDir, File outputDir) { + this.version = version; + this.fullVersion = fullVersion; + this.baseDir = baseDir; + this.outputDir = outputDir; + } + + private static final class VersionFinder { + public static String getVersion() { + return getVersion0("getVersion"); + } + + public static String getFullVersion() { + return getVersion0("getFullVersion"); + } + + private static String getVersion0(String mName) { + try { + Class<?> c = Class.forName("lombok.core.Version"); + Method m = c.getMethod(mName); + return (String) m.invoke(null); + } catch (ClassNotFoundException e) { + System.err.println("You need to specify the version string, and the full version string, as first 2 arguments."); + System.exit(1); + return null; + } catch (Exception e) { + if (e instanceof RuntimeException) throw (RuntimeException) e; + throw new RuntimeException(e); + } + } + } + + private static void buildAll(String version, String fullVersion, String argIn, String argOut) throws Exception { + File in, out; + if (argIn == null) { + in = new File("."); + if (new File(in, "build.xml").isFile() && new File(in, "website").isDirectory()) in = new File(in, "website"); + } else { + in = new File(argIn); + } + + if (argOut == null) { + if (new File("./build.xml").isFile() && new File("./website").isDirectory() && new File("./build").isDirectory()) { + out = new File("./build/website"); + } else { + out = new File(in, "output"); + } + } else { + out = new File(argOut); + } + WebsiteMaker maker = new WebsiteMaker(version, fullVersion, in, out); + maker.buildWebsite(); + } + + private static void buildChangelog(String version, String fullVersion, String argIn, String argOut) throws Exception { + File in, out; + if (argIn == null) { + in = new File("."); + if (new File(in, "build.xml").isFile() && new File(in, "website").isDirectory()) in = new File(in, "website"); + } else { + in = new File(argIn); + } + + if (argOut == null) { + if (new File("./build.xml").isFile() && new File("./website").isDirectory() && new File("./build").isDirectory()) { + out = new File("./build/website/changelog.html"); + } else { + out = new File(in, "output/changelog.html"); + } + } else { + out = new File(argOut); + } + WebsiteMaker maker = new WebsiteMaker(version, fullVersion, in, out.getParentFile()); + maker.buildChangelog(out); + } + + private static void buildDownloadEdge(String version, String fullVersion, String argIn, String argOut) throws Exception { + File in, out; + if (argIn == null) { + in = new File("."); + if (new File(in, "build.xml").isFile() && new File(in, "website").isDirectory()) in = new File(in, "website"); + } else { + in = new File(argIn); + } + + if (argOut == null) { + if (new File("./build.xml").isFile() && new File("./website").isDirectory() && new File("./build").isDirectory()) { + out = new File("./build/website-edge/download-edge.html"); + } else { + out = new File(in, "output/download-edge.html"); + } + } else { + out = new File(argOut); + } + WebsiteMaker maker = new WebsiteMaker(version, fullVersion, in, out.getParentFile()); + maker.buildDownloadEdge(out); + } + + private static void buildChangelogLatest(String version, String fullVersion, String argIn, String argOut) throws Exception { + File in, out; + if (argIn == null) { + in = new File("."); + if (new File(in, "build.xml").isFile() && new File(in, "website").isDirectory()) in = new File(in, "website"); + } else { + in = new File(argIn); + } + + if (argOut == null) { + if (new File("./build.xml").isFile() && new File("./website").isDirectory() && new File("./build").isDirectory()) { + out = new File("./build/latestchanges.html"); + } else { + out = new File(in, "output/latestchanges.html"); + } + } else { + out = new File(argOut); + } + WebsiteMaker maker = new WebsiteMaker(version, fullVersion, in, out.getParentFile()); + maker.buildChangelogLatest(out); + } + + public static void main(String[] args) throws Exception { + String version, fullVersion; + + if (args.length < 2) { + version = VersionFinder.getVersion(); + fullVersion = VersionFinder.getFullVersion(); + } else { + version = args[0]; + fullVersion = args[1]; + } + + if (args.length < 3 || args[2].equalsIgnoreCase("all")) { + buildAll(version, fullVersion, args.length < 4 ? null : args[3], args.length < 5 ? null : args[4]); + } else if (args[2].equalsIgnoreCase("changelog")) { + buildChangelog(version, fullVersion, args.length < 4 ? null : args[3], args.length < 5 ? null : args[4]); + } else if (args[2].equalsIgnoreCase("download-edge")) { + buildDownloadEdge(version, fullVersion, args.length < 4 ? null : args[3], args.length < 5 ? null : args[4]); + } else if (args[2].equalsIgnoreCase("changelog-latest")) { + buildChangelogLatest(version, fullVersion, args.length < 4 ? null : args[3], args.length < 5 ? null : args[4]); + } else { + throw new IllegalArgumentException("3rd argument must be one of 'all', 'changelog', 'download-edge', 'changelog-latest'"); + } + } + + private Configuration makeFreemarkerConfig() throws IOException { + Configuration freemarkerConfig = new Configuration(Configuration.VERSION_2_3_25); + freemarkerConfig.setEncoding(Locale.ENGLISH, "UTF-8"); + freemarkerConfig.setOutputEncoding("UTF-8"); + freemarkerConfig.setOutputFormat(HTMLOutputFormat.INSTANCE); + freemarkerConfig.setTemplateLoader(createLoader()); + freemarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + return freemarkerConfig; + } + + public void buildChangelog(File out) throws Exception { + Configuration freemarkerConfig = makeFreemarkerConfig(); + outputDir.mkdirs(); + convertChangelog(freemarkerConfig, out); + } + + public void buildChangelogLatest(File out) throws Exception { + outputDir.mkdirs(); + String htmlForLatest = CompileChangelog.getHtmlForLatest(baseDir.getParentFile(), version); + FileOutputStream fos = new FileOutputStream(out); + try { + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8")); + bw.write(htmlForLatest); + bw.close(); + } finally { + fos.close(); + } + } + + public void buildDownloadEdge(File out) throws Exception { + Configuration freemarkerConfig = makeFreemarkerConfig(); + + outputDir.mkdirs(); + convertDownloadEdge(freemarkerConfig, out); + } + + public void buildHtAccess(File out) throws Exception { + Configuration freemarkerConfig = new Configuration(Configuration.VERSION_2_3_25); + freemarkerConfig.setEncoding(Locale.ENGLISH, "UTF-8"); + freemarkerConfig.setOutputEncoding("UTF-8"); + freemarkerConfig.setOutputFormat(HTMLOutputFormat.INSTANCE); + freemarkerConfig.setTemplateLoader(createLoader("extra")); + freemarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + + outputDir.mkdirs(); + convertHtAccess(freemarkerConfig, out); + } + + public void buildWebsite() throws Exception { + Configuration freemarkerConfig = makeFreemarkerConfig(); + + outputDir.mkdirs(); + convertTemplates(freemarkerConfig); + buildHtAccess(new File(outputDir, ".htaccess")); + } + + private TemplateLoader createLoader() throws IOException { + return createLoader("templates"); + } + + private TemplateLoader createLoader(String base) throws IOException { + return new FileTemplateLoader(new File(baseDir, base)); + } + + private void convertHtAccess(Configuration freemarker, File outFile) throws Exception { + Map<String, Object> dataModel = new HashMap<String, Object>(); + dataModel.put("setupPages", listHtmlNames(new File(outputDir, "setup"))); + dataModel.put("featurePages", listHtmlNames(new File(outputDir, "features"))); + dataModel.put("experimentalPages", listHtmlNames(new File(outputDir, "features/experimental"))); + Template template = freemarker.getTemplate("htaccess"); + FileOutputStream fileOut = new FileOutputStream(outFile); + try { + Writer wr = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8")); + template.process(dataModel, wr); + wr.close(); + } finally { + fileOut.close(); + } + } + + private List<String> listHtmlNames(File dir) { + List<String> out = new ArrayList<String>(); + for (String s : dir.list()) { + if (s.endsWith(".html") && !s.equals("index.html")) out.add(s.substring(0, s.length() - 5)); + } + return out; + } + + private void convertChangelog(Configuration freemarker, File outFile) throws Exception { + Map<String, Object> dataModel = createBasicDataModel(); + + Template template = freemarker.getTemplate("changelog.html"); + FileOutputStream fileOut = new FileOutputStream(outFile); + try { + Writer wr = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8")); + template.process(dataModel, wr); + wr.close(); + } finally { + fileOut.close(); + } + } + + private void convertDownloadEdge(Configuration freemarker, File outFile) throws Exception { + Map<String, Object> dataModel = createBasicDataModel(); + + Template template = freemarker.getTemplate("_download-edge.html"); + FileOutputStream fileOut = new FileOutputStream(outFile); + try { + Writer wr = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8")); + template.process(dataModel, wr); + wr.close(); + } finally { + fileOut.close(); + } + } + + private void convertTemplates(Configuration freemarker) throws Exception { + File basePagesLoc = new File(baseDir, "templates"); + Map<String, Object> dataModel = createBasicDataModel(); + dataModel.putAll(createExtendedDataModel()); + convertTemplates_(freemarker, "", basePagesLoc, outputDir, 0, dataModel); + } + + private void convertTemplates_(Configuration freemarker, String prefix, File from, File to, int depth, Map<String, Object> dataModel) throws Exception { + if (depth > 50) throw new IllegalArgumentException("50 levels is too deep: " + from); + + for (File f : from.listFiles()) { + if (f.isDirectory()) convertTemplates_(freemarker, prefix + f.getName() + "/", f, new File(to, f.getName()), depth + 1, dataModel); + if (!f.isFile() || !f.getName().endsWith(".html") || f.getName().startsWith("_")) continue; + to.mkdirs(); + Template template = freemarker.getTemplate(prefix + f.getName()); + FileOutputStream fileOut = new FileOutputStream(new File(to, f.getName())); + try { + Writer wr = new BufferedWriter(new OutputStreamWriter(fileOut, "UTF-8")); + template.process(dataModel, wr); + wr.close(); + } finally { + fileOut.close(); + } + } + } + + private Map<String, Object> createBasicDataModel() throws IOException { + Map<String, Object> data = new HashMap<String, Object>(); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss 'UTC'"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String currentTime = sdf.format(new Date()); + + data.put("version", version); + data.put("fullVersion", fullVersion); + data.put("timestampString", currentTime); + data.put("year", "" + new GregorianCalendar().get(Calendar.YEAR)); + data.put("changelog", CompileChangelog.getHtmlStartingAtSection(baseDir.getParentFile(), version)); + data.put("changelogEdge", CompileChangelog.getHtmlForEdge(baseDir.getParentFile(), version)); + + return data; + } + + private static final Pattern LOMBOK_LINK = Pattern.compile("^.*<a(?: (?:id|class|rel|rev|download|target|type)(?:=\"[^\"]*\")?)* href=\"(downloads/[^\"]+)\"(?: (?:id|class|rel|rev|download|target|type)(?:=\"[^\"]*\")?)*>([^<]+)</a>.*$"); + private Map<String, Object> createExtendedDataModel() throws IOException { + Map<String, Object> data = new HashMap<String, Object>(); + + data.put("usages", new HtmlMaker(new File(baseDir, "usageExamples"))); + InputStream in = new URL("https://projectlombok.org/all-versions.html").openStream(); + ArrayList<List<String>> links = new ArrayList<List<String>>(); + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); + for (String line = br.readLine(); line != null; line = br.readLine()) { + Matcher m = LOMBOK_LINK.matcher(line); + if (m.matches()) links.add(Arrays.asList(m.group(1), m.group(2))); + } + } finally { + in.close(); + } + + data.put("linksToVersions", links); + + return data; + } + + public static class HtmlMaker { + private final File usagesDir; + + HtmlMaker(File usagesDir) { + this.usagesDir = usagesDir; + } + + public String pre(String name) throws IOException { + return convert(new File(usagesDir, name + "Example_pre.jpage")); + } + + public String post(String name) throws IOException { + return convert(new File(usagesDir, name + "Example_post.jpage")); + } + + public String convert(File file) throws IOException { + String rawJava = readFully(file); + return Java2Html.convertToHtml(rawJava); + } + } + + public static String readFully(File file) throws IOException { + FileInputStream fis = new FileInputStream(file); + try { + InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); + StringBuilder out = new StringBuilder(); + char[] b = new char[65536]; + while (true) { + int r = isr.read(b); + if (r == -1) break; + out.append(b, 0, r); + } + return out.toString(); + } finally { + fis.close(); + } + } +} |