diff options
Diffstat (limited to 'src')
68 files changed, 1541 insertions, 426 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 7a965486..af3c3c06 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/experimental/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,6 +126,8 @@ public @interface Builder { * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}. * <p> * Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}. + * + * @return Name of the builder class that will be generated (or if it already exists, will be filled with builder elements). */ String builderClassName() default ""; @@ -126,6 +135,8 @@ public @interface Builder { * If true, generate an instance method to obtain a builder that is initialized with the values of this instance. * Legal only if {@code @Builder} is used on a constructor, on the type itself, or on a static method that returns * an instance of the declaring type. + * + * @return Whether to generate a {@code toBuilder()} method. */ boolean toBuilder() default false; @@ -142,13 +153,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..528855cd 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> @@ -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..90621027 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -261,7 +261,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 +460,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..310b57e3 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> @@ -59,14 +58,13 @@ public class Lombok { } /** - * 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 +72,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..18c3d9e3 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 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; } 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/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..18c78b04 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.19"; 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/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..f49fff7f 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -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; @@ -443,6 +444,24 @@ public class EclipseHandlerUtil { } } + 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. * diff --git a/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java b/src/core/lombok/eclipse/handlers/EclipseSingularsRecipes.java index 4cb41d4f..bc779dab 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 @@ -132,6 +132,10 @@ public class EclipseSingularsRecipes { } } + public ASTNode getSource() { + return source; + } + public EclipseNode getAnnotation() { return annotation; } @@ -211,7 +215,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); public boolean requiresCleaning() { diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 8c200a94..5f2e8fe3 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-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,6 +38,7 @@ import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FalseLiteral; @@ -102,9 +103,12 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { TypeReference type; char[] rawName; char[] name; + char[] nameOfDefaultProvider; + char[] nameOfSetFlag; SingularData singularData; ObtainVia obtainVia; EclipseNode obtainViaNode; + EclipseNode originalFieldNode; List<EclipseNode> createdFields = new ArrayList<EclipseNode>(); } @@ -127,6 +131,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; @@ -173,17 +187,40 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { 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)) { + for (EclipseNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); - // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes - // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves. - // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. - if (fd.initialization != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; + EclipseNode isDefault = findAnnotation(Builder.Default.class, fieldNode); + boolean isFinal = ((fd.modifiers & ClassFileConstants.AccFinal) != 0) || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); + BuilderFieldData bfd = new BuilderFieldData(); bfd.rawName = fieldNode.getName().toCharArray(); bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.type; bfd.singularData = getSingularData(fieldNode, ast); + bfd.originalFieldNode = fieldNode; + + if (bfd.singularData != null && isDefault != null) { + isDefault.addError("@Builder.Default and @Singular cannot be mixed."); + isDefault = null; + } + + if (fd.initialization == null && isDefault != null) { + isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); + isDefault = null; + } + + if (fd.initialization != null && isDefault == null) { + if (isFinal) continue; + fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + + if (isDefault != null) { + bfd.nameOfDefaultProvider = prefixWith(DEFAULT_PREFIX, bfd.name); + bfd.nameOfSetFlag = prefixWith(bfd.name, SET_PREFIX); + + MethodDeclaration md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode, ast); + if (md != null) injectMethod(tdParent, md); + } addObtainVia(bfd, fieldNode); builderFields.add(bfd); allFields.add(fieldNode); @@ -334,6 +371,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); } @@ -405,7 +443,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - MethodDeclaration md = generateBuildMethod(isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast); + MethodDeclaration md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfStaticBuilderMethod, returnType, builderFields, builderType, thrownExceptions, addCleaning, ast); if (md != null) injectMethod(builderType, md); } @@ -514,7 +552,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return decl; } - public MethodDeclaration generateBuildMethod(boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) { + public MethodDeclaration generateBuildMethod(EclipseNode tdParent, boolean isStatic, String name, char[] staticName, TypeReference returnType, List<BuilderFieldData> builderFields, EclipseNode type, TypeReference[] thrownExceptions, boolean addCleaning, ASTNode source) { MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) type.top().get()).compilationResult); out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; List<Statement> statements = new ArrayList<Statement>(); @@ -536,7 +574,20 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { List<Expression> args = new ArrayList<Expression>(); for (BuilderFieldData bfd : builderFields) { - args.add(new SingleNameReference(bfd.name, 0L)); + if (bfd.nameOfSetFlag != null) { + MessageSend inv = new MessageSend(); + inv.sourceStart = source.sourceStart; + inv.sourceEnd = source.sourceEnd; + inv.receiver = new SingleNameReference(((TypeDeclaration) tdParent.get()).name, 0L); + inv.selector = bfd.nameOfDefaultProvider; + + 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) { @@ -583,6 +634,22 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { return out; } + public MethodDeclaration generateDefaultProvider(char[] methodName, EclipseNode fieldNode, ASTNode source) { + int pS = source.sourceStart, pE = source.sourceEnd; + + MethodDeclaration out = new MethodDeclaration(((CompilationUnitDeclaration) fieldNode.top().get()).compilationResult); + out.selector = methodName; + out.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic; + out.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG; + FieldDeclaration fd = (FieldDeclaration) fieldNode.get(); + out.returnType = copyType(fd.type, source); + out.statements = new Statement[] {new ReturnStatement(fd.initialization, pS, pE)}; + fd.initialization = null; + + out.traverse(new SetGeneratedByVisitor(source), ((TypeDeclaration) fieldNode.up().get()).scope); + return out; + } + public MethodDeclaration generateBuilderMethod(boolean isStatic, String builderMethodName, String builderClassName, EclipseNode type, TypeParameter[] typeParams, ASTNode source) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long) pS << 32 | pE; @@ -608,25 +675,34 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { if (child.getKind() == Kind.FIELD) existing.add(child); } - top: for (BuilderFieldData bfd : builderFields) { if (bfd.singularData != null && bfd.singularData.getSingularizer() != null) { bfd.createdFields.addAll(bfd.singularData.getSingularizer().generateFields(bfd.singularData, builderType)); } else { + EclipseNode field = null, setFlag = null; for (EclipseNode exists : existing) { char[] n = ((FieldDeclaration) exists.get()).name; - if (Arrays.equals(n, bfd.name)) { - bfd.createdFields.add(exists); - continue top; - } + if (Arrays.equals(n, bfd.name)) field = exists; + if (bfd.nameOfSetFlag != null && Arrays.equals(n, bfd.nameOfSetFlag)) setFlag = exists; } - FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0); - fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - fd.modifiers = ClassFileConstants.AccPrivate; - fd.type = copyType(bfd.type); - fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); - bfd.createdFields.add(injectFieldAndMarkGenerated(builderType, fd)); + if (field == null) { + FieldDeclaration fd = new FieldDeclaration(bfd.name, 0, 0); + fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fd.modifiers = ClassFileConstants.AccPrivate; + fd.type = copyType(bfd.type); + fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); + field = injectFieldAndMarkGenerated(builderType, fd); + } + if (setFlag == null && bfd.nameOfSetFlag != null) { + FieldDeclaration fd = new FieldDeclaration(bfd.nameOfSetFlag, 0, 0); + fd.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fd.modifiers = ClassFileConstants.AccPrivate; + fd.type = TypeReference.baseTypeReference(TypeIds.T_boolean, 0); + fd.traverse(new SetGeneratedByVisitor(source), (MethodScope) null); + injectFieldAndMarkGenerated(builderType, fd); + } + bfd.createdFields.add(field); } } } @@ -634,14 +710,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; @@ -657,7 +734,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); } diff --git a/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java new file mode 100644 index 00000000..da184d0b --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleBuilderDefault.java @@ -0,0 +1,25 @@ +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)) { + 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 072b6dd5..3469e6bf 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 @@ -144,14 +144,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); } @@ -361,7 +364,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 && !suppressConstructorProperties && !isLocalType(type)) { constructorProperties = createConstructorProperties(source, fields); } diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index ceef3d3c..37fa78ab 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) { 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/singulars/EclipseGuavaSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseGuavaSingularizer.java index 242bde1f..8e925b3f 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 ef9e2a76..9aac32a3 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 index 4d5e0f67..7b364bff 100644 --- a/src/core/lombok/experimental/Builder.java +++ b/src/core/lombok/experimental/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: * @@ -110,15 +110,20 @@ import java.lang.annotation.Target; @Retention(SOURCE) @Deprecated public @interface Builder { - /** Name of the method that creates a new builder instance. Default: {@code builder}. */ + /** @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"; - /** Name of the builder class. + /** + * Name of the builder class. + * * Default for {@code @Builder} on types and constructors: {@code (TypeName)Builder}. + * <p> * Default for {@code @Builder} on methods: {@code (ReturnTypeName)Builder}. + * + * @return Name of the builder class that will be generated (or if it already exists, will be filled with builder elements). */ String builderClassName() default ""; @@ -127,6 +132,8 @@ public @interface Builder { * to {@code false} to name the setter method for field {@code someField}: {@code setSomeField}. * <p> * <strong>Default: true</strong> + * + * @return Whether to generate fluent methods (just {@code fieldName()} instead of {@code setFieldName()}). */ boolean fluent() default true; @@ -135,6 +142,8 @@ public @interface Builder { * calls to set methods. Set this to {@code false} to have these 'set' methods return {@code void} instead. * <p> * <strong>Default: true</strong> + * + * @return Whether to generate chaining methods (each build call returns itself instead of returning {@code void}). */ 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 index 46ec7a05..06dbee13 100644 --- a/src/core/lombok/experimental/Value.java +++ b/src/core/lombok/experimental/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) @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>. + * Complete documentation is found at <a href="https://projectlombok.org/features/Value">the project lombok features page for @Value</a>. * * @see lombok.Getter * @see Wither @@ -55,6 +55,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/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/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/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/JavacAST.java b/src/core/lombok/javac/JavacAST.java index c99ae5c9..91ed325f 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -65,12 +65,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 +84,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); @@ -422,22 +422,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 +474,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. } - } 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; + } + } + } + + 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(multiple, pos, "proc.messager", message); + } catch (Throwable t) {} } } } diff --git a/src/core/lombok/javac/apt/LombokFileObjects.java b/src/core/lombok/javac/apt/LombokFileObjects.java index 7e818cab..cb2036e9 100644 --- a/src/core/lombok/javac/apt/LombokFileObjects.java +++ b/src/core/lombok/javac/apt/LombokFileObjects.java @@ -22,8 +22,10 @@ package lombok.javac.apt; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URI; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.atomic.AtomicBoolean; @@ -31,10 +33,10 @@ 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 { @@ -98,7 +100,9 @@ final class LombokFileObjects { String jfmClassName = jfm != null ? jfm.getClass().getName() : "null"; if (jfmClassName.equals("com.sun.tools.javac.util.DefaultFileManager")) return Compiler.JAVAC6; if (jfmClassName.equals("com.sun.tools.javac.util.JavacFileManager")) return Compiler.JAVAC6; - if (jfmClassName.equals("com.sun.tools.javac.file.JavacFileManager")) { + if (jfmClassName.equals("com.sun.tools.javac.file.JavacFileManager") || + jfmClassName.equals("com.google.errorprone.MaskedClassLoader$MaskedFileManager") || + jfmClassName.equals("com.google.devtools.build.buildjar.javac.BlazeJavacMain$ClassloaderMaskingFileManager")) { try { Class<?> superType = Class.forName("com.sun.tools.javac.file.BaseFileManager"); if (superType.isInstance(jfm)) { @@ -108,6 +112,18 @@ final class LombokFileObjects { catch (Exception e) {} return Compiler.JAVAC7; } + if (jfmClassName.equals("com.sun.tools.javac.api.ClientCodeWrapper$WrappedStandardJavaFileManager")) { + try { + Field wrappedField = Class.forName("com.sun.tools.javac.api.ClientCodeWrapper$WrappedJavaFileManager").getDeclaredField("clientJavaFileManager"); + wrappedField.setAccessible(true); + JavaFileManager wrappedManager = (JavaFileManager)wrappedField.get(jfm); + Class<?> superType = Class.forName("com.sun.tools.javac.file.BaseFileManager"); + if (superType.isInstance(wrappedManager)) { + return new Java9Compiler(wrappedManager); + } + } + catch (Exception e) {} + } try { if (Class.forName("com.sun.tools.javac.file.BaseFileObject") == null) throw new NullPointerException(); return Compiler.JAVAC7; @@ -116,7 +132,15 @@ final class LombokFileObjects { if (Class.forName("com.sun.tools.javac.util.BaseFileObject") == null) throw new NullPointerException(); return Compiler.JAVAC6; } catch (Exception e) {} - return null; + + 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) { @@ -137,9 +161,15 @@ final class LombokFileObjects { @Override public JavaFileObject wrap(LombokFileObject fileObject) { URI uri = fileObject.toUri(); if (uri.getScheme() == null) { - uri = URI.create("file://" + uri); + uri = URI.create("file:///" + uri); + } + Path path; + try { + path = Paths.get(uri); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Problems in URI '" + uri + "' (" + fileObject.toUri() + ")", e); } - return new Javac9BaseFileObjectWrapper(fileManager, Paths.get(uri), fileObject); + return new Javac9BaseFileObjectWrapper(fileManager, path, fileObject); } @Override public Method getDecoderMethod() { diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 4c670433..157828a0 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-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 @@ -85,9 +85,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>(); } @@ -139,17 +142,39 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { 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)) { + for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent, true)) { JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); - // final fields with an initializer cannot be written to, so they can't be 'builderized'. Unfortunately presence of @Value makes - // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this math ourselves. - // Value will only skip making a field final if it has an explicit @NonFinal annotation, so we check for that. - if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; + JavacNode isDefault = findAnnotation(Builder.Default.class, fieldNode, true); + boolean isFinal = (fd.mods.flags & Flags.FINAL) != 0 || (valuePresent && !hasAnnotation(NonFinal.class, fieldNode)); BuilderFieldData bfd = new BuilderFieldData(); bfd.rawName = fd.name; bfd.name = removePrefixFromField(fieldNode); bfd.type = fd.vartype; bfd.singularData = getSingularData(fieldNode); + bfd.originalFieldNode = fieldNode; + + if (bfd.singularData != null && isDefault != null) { + isDefault.addError("@Builder.Default and @Singular cannot be mixed."); + isDefault = null; + } + + if (fd.init == null && isDefault != null) { + isDefault.addWarning("@Builder.Default requires an initializing expression (' = something;')."); + isDefault = null; + } + + if (fd.init != null && isDefault == null) { + if (isFinal) continue; + fieldNode.addWarning("@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default. If it is not supposed to be settable during building, make the field final."); + } + + if (isDefault != null) { + bfd.nameOfDefaultProvider = parent.toName("$default$" + bfd.name); + bfd.nameOfSetFlag = parent.toName(bfd.name + "$set"); + JCMethodDecl md = generateDefaultProvider(bfd.nameOfDefaultProvider, fieldNode); + recursiveSetGeneratedBy(md, ast, annotationNode.getContext()); + if (md != null) injectMethod(tdParent, md); + } addObtainVia(bfd, fieldNode); builderFields.add(bfd); allFields.append(fieldNode); @@ -290,6 +315,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); } @@ -357,7 +383,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { - JCMethodDecl md = generateBuildMethod(isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning); + JCMethodDecl md = generateBuildMethod(tdParent, isStatic, buildMethodName, nameOfBuilderMethod, returnType, builderFields, builderType, thrownExceptions, ast, addCleaning); if (md != null) injectMethod(builderType, md); } @@ -477,7 +503,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); /* @@ -494,7 +520,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { */ } - private JCMethodDecl generateBuildMethod(boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) { + private JCMethodDecl generateBuildMethod(JavacNode tdParent, boolean isStatic, String buildName, Name builderName, JCExpression returnType, java.util.List<BuilderFieldData> builderFields, JavacNode type, List<JCExpression> thrownExceptions, JCTree source, boolean addCleaning) { JavacTreeMaker maker = type.getTreeMaker(); JCExpression call; @@ -515,25 +541,28 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { ListBuffer<JCExpression> args = new ListBuffer<JCExpression>(); for (BuilderFieldData bfd : builderFields) { - args.append(maker.Ident(bfd.name)); + if (bfd.nameOfSetFlag != null) { + args.append(maker.Conditional(maker.Ident(bfd.nameOfSetFlag), maker.Ident(bfd.name), + maker.Apply(List.<JCExpression>nil(), maker.Select(maker.Ident(((JCClassDecl) tdParent.get()).name), bfd.nameOfDefaultProvider), List.<JCExpression>nil()))); + } else { + args.append(maker.Ident(bfd.name)); + } } if (addCleaning) { - statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, true)))); + statements.append(maker.Exec(maker.Assign(maker.Select(maker.Ident(type.toName("this")), type.toName("$lombokUnclean")), maker.Literal(CTC_BOOLEAN, 1)))); } if (builderName == null) { call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null); statements.append(maker.Return(call)); } else { - ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>(); for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) { typeParams.append(maker.Ident(tp.name)); } JCExpression callee = maker.Ident(((JCClassDecl) type.up().get()).name); - if (!isStatic) - callee = maker.Select(callee, type.up().toName("this")); + if (!isStatic) callee = maker.Select(callee, type.up().toName("this")); JCExpression fn = maker.Select(callee, builderName); call = maker.Apply(typeParams.toList(), fn, args.toList()); if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) { @@ -548,6 +577,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) { + 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()), List.<JCTypeParameter>nil(), 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(); @@ -572,36 +613,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()) { @@ -614,7 +662,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); } diff --git a/src/core/lombok/javac/handlers/HandleBuilderDefault.java b/src/core/lombok/javac/handlers/HandleBuilderDefault.java new file mode 100644 index 00000000..733aea5a --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleBuilderDefault.java @@ -0,0 +1,29 @@ +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> { + @SuppressWarnings("deprecation") + @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.class, 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 25e96d75..929de3cd 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -151,6 +151,10 @@ public class HandleConstructor { } 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 +166,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(); } @@ -295,7 +299,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 && !suppressConstructorProperties && !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/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index 6df56ed6..4bd1ce4b 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, 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/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index 68d8061c..b4dcb097 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -191,6 +191,27 @@ public class JavacHandlerUtil { } } + 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. * @@ -222,6 +243,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; diff --git a/src/core/lombok/javac/handlers/JavacSingularsRecipes.java b/src/core/lombok/javac/handlers/JavacSingularsRecipes.java index c6d601bd..6a76e1dd 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); public boolean requiresCleaning() { diff --git a/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacGuavaSingularizer.java index fba4c80e..0ab7da54 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 93b120e1..fd699275 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 25669721..0589ac34 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, "this", data.getPluralName() + "$key", "get"), List.<JCExpression>of(maker.Ident(ivar))); JCExpression arg2 = maker.Apply(jceBlank, chainDots(builderType, "this", data.getPluralName() + "$value", "get"), List.<JCExpression>of(maker.Ident(ivar))); + // [jdk9] We add an unneccessary (V) cast here. Not doing so gives an error in javac (build 9-ea+156-jigsaw-nightly-h6072-20170212): + // error: method put in interface Map<K#2,V#2> cannot be applied to given types; + arg2 = maker.TypeCast(createTypeArgs(2, false, builderType, data.getTypeArgs(), source).get(1), arg2); JCStatement putStatement = maker.Exec(maker.Apply(jceBlank, pluralnameDotPut, List.of(arg1, arg2))); JCStatement forInit = maker.VarDef(maker.Modifiers(0), ivar, maker.TypeIdent(CTC_INT), maker.Literal(CTC_INT, 0)); JCExpression checkExpr = maker.Binary(CTC_LESS_THAN, maker.Ident(ivar), getSize(maker, builderType, keyVarName, nullGuard, true)); 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/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/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/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..b1ef53c0 --- /dev/null +++ b/src/website/lombok/website/CompileChangelog.java @@ -0,0 +1,114 @@ +package lombok.website; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +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(markdown); + } + + 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); + } + + 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 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/WebsiteMaker.java b/src/website/lombok/website/WebsiteMaker.java new file mode 100644 index 00000000..6e773346 --- /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.getHtml(baseDir.getParentFile())); + 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(); + } + } +} |