diff options
-rw-r--r-- | doc/changelog.markdown | 1 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java | 18 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleConstructor.java | 35 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java | 6 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleGetter.java | 10 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleSetter.java | 6 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleToString.java | 11 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleConstructor.java | 17 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleGetter.java | 4 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleToString.java | 4 | ||||
-rw-r--r-- | website/features/Constructor.html | 3 | ||||
-rw-r--r-- | website/features/GetterSetter.html | 3 | ||||
-rw-r--r-- | website/features/ToString.html | 2 |
13 files changed, 86 insertions, 34 deletions
diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 43a1eb9d..9264adde 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -5,6 +5,7 @@ Lombok Changelog * FEATURE: Added support for several logging frameworks by the `@Log` annotation. * FEATURE: Lombok now supports post-compile transformers. [Issue #144](http://code.google.com/p/projectlombok/issues/detail?id=144) * FEATURE: Using `@SneakyThrows` no longer requires a runtime dependency on lombok.jar. In fact, any call to {@code Lombok.sneakyThrows(ex)} is optimized at the bytecode level and no longer requires you to actually have lombok.jar or lombok-runtime.jar on the classpath. + * FEATURE: @XArgsConstructor, @Getter, and @ToString can now be used on enum declarations. Previously, behaviour of these annotations on enums was undefined. * BUGFIX: `@Setter` and `@Getter` can now be applied to static fields again (was broken in v0.9.3 only). [Issue #136](http://code.google.com/p/projectlombok/issues/detail?id=136) * BUGFIX: delombok added type parameters to constructors that mirror the type's own type parameters. This resulted in delombok turning any generated constructor that takes at least 1 parameter of type 'T' into something that didn't compile, and to boot, a confusing error message ('T is not compatible with T'). This is now fixed. [Issue #140](http://code.google.com/p/projectlombok/issues/detail?id=140) * BUGFIX: Add null check for `@Cleanup` [Issue #154](http://code.google.com/p/projectlombok/issues/detail?id=154) diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 4097362b..293359dc 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -294,6 +294,24 @@ public class EclipseHandlerUtil { } /** + * Checks if the field should be included in operations that work on 'all' fields: + * If the field is static, or starts with a '$', or is actually an enum constant, 'false' is returned, indicating you should skip it. + */ + public static boolean filterField(FieldDeclaration declaration) { + // Skip the fake fields that represent enum constants. + if (declaration.initialization instanceof AllocationExpression && + ((AllocationExpression)declaration.initialization).enumConstant != null) return false; + + // Skip fields that start with $ + if (declaration.name.length > 0 && declaration.name[0] == '$') return false; + + // Skip static fields. + if ((declaration.modifiers & ClassFileConstants.AccStatic) != 0) return false; + + return true; + } + + /** * Checks if there is a field with the provided name. * * @param fieldName the field name to check for. diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index f5025b19..db071329 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -75,6 +75,7 @@ public class HandleConstructor { public static class HandleNoArgsConstructor implements EclipseAnnotationHandler<NoArgsConstructor> { @Override public boolean handle(AnnotationValues<NoArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { EclipseNode typeNode = annotationNode.up(); + if (!checkLegality(typeNode, annotationNode, NoArgsConstructor.class.getSimpleName())) return true; NoArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); String staticName = ann.staticName(); @@ -89,6 +90,7 @@ public class HandleConstructor { public static class HandleRequiredArgsConstructor implements EclipseAnnotationHandler<RequiredArgsConstructor> { @Override public boolean handle(AnnotationValues<RequiredArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { EclipseNode typeNode = annotationNode.up(); + if (!checkLegality(typeNode, annotationNode, RequiredArgsConstructor.class.getSimpleName())) return true; RequiredArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); String staticName = ann.staticName(); @@ -105,10 +107,7 @@ public class HandleConstructor { for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); - //Skip fields that start with $ - if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; - //Skip static fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; + if (!EclipseHandlerUtil.filterField(fieldDecl)) continue; boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; boolean isNonNull = findAnnotations(fieldDecl, TransformationsUtil.NON_NULL_PATTERN).length != 0; if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child); @@ -120,6 +119,7 @@ public class HandleConstructor { public static class HandleAllArgsConstructor implements EclipseAnnotationHandler<AllArgsConstructor> { @Override public boolean handle(AnnotationValues<AllArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { EclipseNode typeNode = annotationNode.up(); + if (!checkLegality(typeNode, annotationNode, AllArgsConstructor.class.getSimpleName())) return true; AllArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); String staticName = ann.staticName(); @@ -130,12 +130,11 @@ public class HandleConstructor { for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); - // Skip fields that start with $ - if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; - // Skip static fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; + if (!EclipseHandlerUtil.filterField(fieldDecl)) continue; + // Skip initialized final fields. - if (((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) continue; + if (((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) && fieldDecl.initialization != null) return false; + fields.add(child); } new HandleConstructor().generateConstructor(typeNode, level, fields, staticName, false, suppressConstructorProperties, ast); @@ -143,6 +142,20 @@ public class HandleConstructor { } } + static boolean checkLegality(EclipseNode typeNode, EclipseNode errorNode, String name) { + TypeDeclaration typeDecl = null; + if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); + int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; + boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; + + if (typeDecl == null || notAClass) { + errorNode.addError(name + " is only supported on a class or an enum."); + return false; + } + + return true; + } + public void generateRequiredArgsConstructor(EclipseNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists, ASTNode source) { generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, false, source); } @@ -208,6 +221,10 @@ public class HandleConstructor { EclipseNode type, Collection<EclipseNode> fields, boolean suppressConstructorProperties, ASTNode source) { long p = (long)source.sourceStart << 32 | source.sourceEnd; + boolean isEnum = (((TypeDeclaration)type.get()).modifiers & ClassFileConstants.AccEnum) != 0; + + if (isEnum) level = AccessLevel.PRIVATE; + ConstructorDeclaration constructor = new ConstructorDeclaration( ((CompilationUnitDeclaration) type.top().get()).compilationResult); Eclipse.setGeneratedBy(constructor, source); diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 895c076e..d906d85d 100644 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -193,14 +193,12 @@ public class HandleEqualsAndHashCode implements EclipseAnnotationHandler<EqualsA for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); - //Skip static fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; + if (!EclipseHandlerUtil.filterField(fieldDecl)) continue; + //Skip transient fields. if ((fieldDecl.modifiers & ClassFileConstants.AccTransient) != 0) continue; //Skip excluded fields. if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue; - //Skip fields that start with $. - if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; nodesForEquality.add(child); } } diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 56d0ba6c..f39e55b5 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -87,10 +87,10 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; + (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; if (typeDecl == null || notAClass) { - pos.addError("@Getter is only supported on a class or a field."); + pos.addError("@Getter is only supported on a class, an enum, or a field."); return false; } @@ -103,11 +103,7 @@ public class HandleGetter implements EclipseAnnotationHandler<Getter> { public boolean fieldQualifiesForGetterGeneration(EclipseNode field) { if (field.getKind() != Kind.FIELD) return false; FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); - //Skip fields that start with $ - if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') return false; - //Skip static fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) return false; - return true; + return EclipseHandlerUtil.filterField(fieldDecl); } /** diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index 2c3ca6ed..14ff0e10 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -85,10 +85,8 @@ public class HandleSetter implements EclipseAnnotationHandler<Setter> { for (EclipseNode field : typeNode.down()) { if (field.getKind() != Kind.FIELD) continue; FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); - //Skip fields that start with $ - if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; - //Skip static fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; + if (!EclipseHandlerUtil.filterField(fieldDecl)) continue; + //Skip final fields. if ((fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0) continue; diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index f39cb129..8d722247 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -128,10 +128,10 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> { if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; boolean notAClass = (modifiers & - (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; + (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; if (typeDecl == null || notAClass) { - errorNode.addError("@ToString is only supported on a class."); + errorNode.addError("@ToString is only supported on a class or enum."); return false; } @@ -152,12 +152,11 @@ public class HandleToString implements EclipseAnnotationHandler<ToString> { for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); - //Skip static fields. - if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; + if (!EclipseHandlerUtil.filterField(fieldDecl)) continue; + //Skip excluded fields. if (excludes != null && excludes.contains(new String(fieldDecl.name))) continue; - //Skip fields that start with $ - if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; + nodesForToString.add(child); } } diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index 7f0c2ba6..3cb36c30 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -62,6 +62,7 @@ public class HandleConstructor { markAnnotationAsProcessed(annotationNode, NoArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); + if (!checkLegality(typeNode, annotationNode, NoArgsConstructor.class.getSimpleName())) return true; NoArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); String staticName = ann.staticName(); @@ -82,6 +83,7 @@ public class HandleConstructor { markAnnotationAsProcessed(annotationNode, RequiredArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); + if (!checkLegality(typeNode, annotationNode, RequiredArgsConstructor.class.getSimpleName())) return true; RequiredArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); String staticName = ann.staticName(); @@ -120,6 +122,7 @@ public class HandleConstructor { markAnnotationAsProcessed(annotationNode, AllArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); + if (!checkLegality(typeNode, annotationNode, AllArgsConstructor.class.getSimpleName())) return true; AllArgsConstructor ann = annotation.getInstance(); AccessLevel level = ann.access(); String staticName = ann.staticName(); @@ -148,6 +151,20 @@ public class HandleConstructor { } } + static boolean checkLegality(JavacNode typeNode, JavacNode errorNode, String name) { + JCClassDecl typeDecl = null; + if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); + long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; + boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; + + if (typeDecl == null || notAClass) { + errorNode.addError(name + " is only supported on a class or an enum."); + return false; + } + + return true; + } + public void generateRequiredArgsConstructor(JavacNode typeNode, AccessLevel level, String staticName, boolean skipIfConstructorExists) { generateConstructor(typeNode, level, findRequiredFields(typeNode), staticName, skipIfConstructorExists, false); } diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index cfe5c98a..8a1f7eed 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -85,10 +85,10 @@ public class HandleGetter implements JavacAnnotationHandler<Getter> { JCClassDecl typeDecl = null; if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); long modifiers = typeDecl == null ? 0 : typeDecl.mods.flags; - boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; + boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; if (typeDecl == null || notAClass) { - errorNode.addError("@Getter is only supported on a class or a field."); + errorNode.addError("@Getter is only supported on a class, an enum, or a field."); return false; } diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index 6f7465f3..8bc8036f 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -117,7 +117,7 @@ public class HandleToString implements JavacAnnotationHandler<ToString> { boolean notAClass = true; if (typeNode.get() instanceof JCClassDecl) { long flags = ((JCClassDecl)typeNode.get()).mods.flags; - notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION | Flags.ENUM)) != 0; + notAClass = (flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; } if (callSuper == null) { @@ -127,7 +127,7 @@ public class HandleToString implements JavacAnnotationHandler<ToString> { } if (notAClass) { - errorNode.addError("@ToString is only supported on a class."); + errorNode.addError("@ToString is only supported on a class or enum."); return false; } diff --git a/website/features/Constructor.html b/website/features/Constructor.html index 6a7cfd2f..072e46ed 100644 --- a/website/features/Constructor.html +++ b/website/features/Constructor.html @@ -68,6 +68,9 @@ The <code>@java.beans.ConstructorProperties</code> annotation is never generated for a constructor with no arguments. This also explains why <code>@NoArgsConstructor</code> lacks the <code>suppressConstructorProperties</code> annotation method. The <code>@ConstructorProperties</code> annotation is also omitted for private constructors. The generated static factory methods also do not get <code>@ConstructorProperties</code>, as this annotation can only be added to real constructors. + </p><p> + <code>@XArgsConstructor</code> can also be used on an enum definition. The generated constructor will always be + private, because non-private constructors aren't legal in enums. You don't have to specify <code>AccessLevel.PRIVATE</code>. </p> </div> </div> diff --git a/website/features/GetterSetter.html b/website/features/GetterSetter.html index fd4ed17d..8e7a1ede 100644 --- a/website/features/GetterSetter.html +++ b/website/features/GetterSetter.html @@ -67,6 +67,9 @@ </p><p> Using the <code>AccessLevel.NONE</code> access level simply generates nothing. It's useful only in combination with <a href="Data.html"><code>@Data</code></a> or a class-wide <code>@Getter</code> or <code>@Setter</code>. + </p><p> + <code>@Getter</code> can also be used on enums. <code>@Setter</code> can't, not for a technical reason, but + for a pragmatic one: Setters on enums are an extremely bad idea. </p> </div> </div> diff --git a/website/features/ToString.html b/website/features/ToString.html index e350c265..8ac29bb1 100644 --- a/website/features/ToString.html +++ b/website/features/ToString.html @@ -62,6 +62,8 @@ </p><p> If a getter exists for a field to be included, it is called instead of using a direct field reference. This behaviour can be suppressed:<br /> <code>@ToString(doNotUseGetters = true)</code> + </p><p> + <code>@ToString</code> can also be used on an enum definition. </p> </div> </div> |