From 73844e37eaeda1a36b024fd2672cb64937230f43 Mon Sep 17 00:00:00 2001 From: michiel Date: Mon, 7 Apr 2014 20:28:51 +0200 Subject: New Annotation: FieldNameConstants. --- .../eclipse/handlers/HandleFieldNameConstants.java | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java (limited to 'src/core/lombok/eclipse') diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java new file mode 100644 index 00000000..bee50f36 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java @@ -0,0 +1,129 @@ +package lombok.eclipse.handlers; + +import static java.lang.Character.*; +import static lombok.eclipse.handlers.EclipseHandlerUtil.*; + +import java.lang.reflect.Modifier; +import java.util.Collection; + +import lombok.AccessLevel; +import lombok.FieldNameConstants; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.mangosdk.spi.ProviderFor; + +@ProviderFor(EclipseAnnotationHandler.class) public class HandleFieldNameConstants extends EclipseAnnotationHandler { + + public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, boolean checkForTypeLevelFieldNameConstants) { + + if (checkForTypeLevelFieldNameConstants) { + if (hasAnnotation(FieldNameConstants.class, typeNode)) { + return true; + } + } + + TypeDeclaration typeDecl = null; + if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); + + int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; + boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; + + if (typeDecl == null || notAClass) { + errorNode.addError("@FieldNameConstants is only supported on a class or an enum or a field."); + return false; + } + + for (EclipseNode field : typeNode.down()) { + if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level); + if (field.getKind() != Kind.FIELD) return false; + } + return true; + } + + private void generateFieldNameConstantsForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level) { + if (hasAnnotation(FieldNameConstants.class, fieldNode)) { + return; + } + createFieldNameConstantsForField(level, fieldNode, fieldNode, pos, false); + } + + private void createFieldNameConstantsForField(AccessLevel level, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { + if (fieldNode.getKind() != Kind.FIELD) { + errorNode.addError("@FieldNameConstants is only supported on a class or a field"); + return; + } + FieldDeclaration field = (FieldDeclaration) fieldNode.get(); + String constantName = camelCaseToConstant(new String(field.name)); + if (constantName == null) { + errorNode.addWarning("Not generating constant for this field: It does not fit in your @Accessors prefix list"); + return; + } + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + FieldDeclaration fieldConstant = new FieldDeclaration(constantName.toCharArray(), pS,pE); + fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fieldConstant.modifiers = toEclipseModifier(level) | Modifier.STATIC | Modifier.FINAL; + fieldConstant.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[]{p,p,p}); + fieldConstant.initialization = new StringLiteral(field.name, pS,pE,0); + injectField(fieldNode.up(), fieldConstant); + } + + private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field) { + if (field.getKind() != Kind.FIELD) return false; + FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); + return filterField(fieldDecl); + } + + public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { + EclipseNode node = annotationNode.up(); + FieldNameConstants annotatationInstance = annotation.getInstance(); + AccessLevel level = annotatationInstance.level(); + if (node == null) return; + switch (node.getKind()){ + case FIELD: + createFieldNameConstantsForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true); + break; + case TYPE: + generateFieldDefaultsForType(node, annotationNode, level, false); + break; + + } + + } + + private void createFieldNameConstantsForFields(AccessLevel level, Collection fieldNodes, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { + for (EclipseNode fieldNode : fieldNodes){ + createFieldNameConstantsForField(level, fieldNode, errorNode, source, whineIfExists); + } + } + + public static String camelCaseToConstant(final String fieldName) { + if (fieldName == null || fieldName.isEmpty()) return ""; + char[] chars = fieldName.toCharArray(); + StringBuilder b = new StringBuilder(); + b.append(toUpperCase(chars[0])); + for (int i = 1, iend = chars.length; i < iend; i++) { + char c = chars[i]; + if (isUpperCase(c)) { + b.append('_'); + } else { + c = toUpperCase(c); + } + b.append(c); + } + return b.toString(); + } + +} -- cgit From f4998b57753257d485ccc76f569e439721c30dbf Mon Sep 17 00:00:00 2001 From: Michiel Verheul Date: Fri, 3 Oct 2014 13:00:36 +0200 Subject: Added copyright header --- .../eclipse/handlers/HandleFieldNameConstants.java | 23 ++++++++++++++- .../lombok/experimental/FieldNameConstants.java | 34 ++++++++++++++++++++-- .../javac/handlers/HandleFieldNameConstants.java | 25 ++++++++++++++-- 3 files changed, 77 insertions(+), 5 deletions(-) (limited to 'src/core/lombok/eclipse') diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java index bee50f36..597b0937 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2012-2014 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.eclipse.handlers; import static java.lang.Character.*; @@ -7,12 +28,12 @@ import java.lang.reflect.Modifier; import java.util.Collection; import lombok.AccessLevel; -import lombok.FieldNameConstants; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.experimental.FieldNameConstants; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; diff --git a/src/core/lombok/experimental/FieldNameConstants.java b/src/core/lombok/experimental/FieldNameConstants.java index 3bfeb1c1..0f582f18 100644 --- a/src/core/lombok/experimental/FieldNameConstants.java +++ b/src/core/lombok/experimental/FieldNameConstants.java @@ -1,13 +1,43 @@ -package lombok; +/* + * Copyright (C) 2009-2013 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.experimental; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import lombok.AccessLevel; + + + + +/** + * Generates String constants containing the field name for each field. + * + */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface FieldNameConstants { - lombok.AccessLevel level()default lombok.AccessLevel.PUBLIC; + lombok.AccessLevel level()default AccessLevel.PUBLIC; } diff --git a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java index 45943a33..cf0fad0c 100644 --- a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java @@ -1,3 +1,24 @@ +/* + * Copyright (C) 2012-2014 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package lombok.javac.handlers; import static lombok.javac.handlers.JavacHandlerUtil.*; @@ -6,9 +27,9 @@ import java.lang.reflect.Modifier; import java.util.Collection; import lombok.AccessLevel; -import lombok.FieldNameConstants; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; +import lombok.experimental.FieldNameConstants; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -24,7 +45,7 @@ import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; -@ProviderFor(JavacAnnotationHandler.class) @SuppressWarnings("restriction") public class HandleFieldConstants extends JavacAnnotationHandler { +@ProviderFor(JavacAnnotationHandler.class) @SuppressWarnings("restriction") public class HandleFieldNameConstants extends JavacAnnotationHandler { public void generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelFieldNameConstants) { -- cgit From b6fb3c03fa35db3423ea4274f35d2c77859a3286 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Thu, 5 Apr 2018 14:22:40 +0200 Subject: [fieldNameConstants] code review, docs, tests, fixup. --- doc/changelog.markdown | 6 +- src/core/lombok/ConfigurationKeys.java | 8 ++ src/core/lombok/core/handlers/HandlerUtil.java | 12 +++ .../eclipse/handlers/EclipseHandlerUtil.java | 2 +- .../eclipse/handlers/HandleFieldNameConstants.java | 107 +++++++++------------ .../lombok/experimental/FieldNameConstants.java | 11 +-- .../javac/handlers/HandleFieldNameConstants.java | 78 +++++---------- test/core/src/lombok/AbstractRunTests.java | 2 +- .../EqualsAndHashCodeWithGenericsOnInners.java | 1 + .../after-delombok/FieldNameConstantsBasic.java | 8 ++ .../after-delombok/FieldNameConstantsWeird.java | 4 + .../resource/after-delombok/InnerClass.java | 3 +- .../after-ecj/FieldNameConstantsBasic.java | 15 +++ .../after-ecj/FieldNameConstantsWeird.java | 9 ++ .../resource/before/FieldNameConstantsBasic.java | 11 +++ .../resource/before/FieldNameConstantsWeird.java | 9 ++ .../FieldNameConstantsWeird.java.messages | 1 + .../FieldNameConstantsWeird.java.messages | 1 + .../FieldNameConstantsExample_post.jpage | 7 ++ .../FieldNameConstantsExample_pre.jpage | 9 ++ .../features/experimental/FieldNameConstants.html | 44 +++++++++ website/templates/features/experimental/index.html | 4 + 22 files changed, 222 insertions(+), 130 deletions(-) create mode 100644 test/transform/resource/after-delombok/FieldNameConstantsBasic.java create mode 100644 test/transform/resource/after-delombok/FieldNameConstantsWeird.java create mode 100644 test/transform/resource/after-ecj/FieldNameConstantsBasic.java create mode 100644 test/transform/resource/after-ecj/FieldNameConstantsWeird.java create mode 100644 test/transform/resource/before/FieldNameConstantsBasic.java create mode 100644 test/transform/resource/before/FieldNameConstantsWeird.java create mode 100644 test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages create mode 100644 test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages create mode 100644 usage_examples/experimental/FieldNameConstantsExample_post.jpage create mode 100644 usage_examples/experimental/FieldNameConstantsExample_pre.jpage create mode 100644 website/templates/features/experimental/FieldNameConstants.html (limited to 'src/core/lombok/eclipse') diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 9e84afb8..51da4651 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -3,9 +3,11 @@ Lombok Changelog ### v1.16.21 "Edgy Guinea Pig" * v1.16.20 is the latest stable release of Project Lombok. +* FEATURE: `@FieldNameConstants` is an new feature that generates string constants for your field names. [Docs on @FieldNameConstants](https://projectlombok.org/features/experimental/FieldNameConstants). * PLATFORM: Fix for using lombok together with JDK9's new `module-info.java` feature. [Issue #985](https://github.com/rzwitserloot/lombok/issues/985) -* PLATFORM: Some initial work on supporting JDK10. -* BUGFIX: Potential fix for Netbeans < 9. [Issue #1555](https://github.com/rzwitserloot/lombok/issues/1555) +* PLATFORM: Some initial work on supporting JDK10 and JDK11. +* BUGFIX: Solved some issues in eclipse that resulted in error 'A save participant caused problems'. [Issue #879](https://github.com/rzwitserloot/lombok/issues/879) +* BUGFIX: Bugfix for Netbeans < 9. [Issue #1555](https://github.com/rzwitserloot/lombok/issues/1555) * PROMOTION: `var` has been promoted from experimental to the main package with no changes. The 'old' experimental one is still around but is deprecated, and is an alias for the new main package one. [var documentation](https://projectlombok.org/features/var.html). * OLD-CRUFT: `lombok.experimental.Builder` and `lombok.experimental.Value` are deprecated remnants of when these features were still in experimental. They are now removed entirely. If your project is dependent on an older version of lombok which still has those; fret not, lombok still processes these annotations. It just no longer includes them in the jar. diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 29c43d3f..7efe20bd 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -499,6 +499,14 @@ public class ConfigurationKeys { */ public static final ConfigurationKey UTILITY_CLASS_FLAG_USAGE = new ConfigurationKey("lombok.utilityClass.flagUsage", "Emit a warning or error if @UtilityClass is used.") {}; + // ----- FieldNameConstants ----- + /** + * lombok configuration: {@code lombok.fieldNameConstants.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, any usage of {@code @FieldNameConstants} results in a warning / error. + */ + public static final ConfigurationKey FIELD_NAME_CONSTANTS_FLAG_USAGE = new ConfigurationKey("lombok.fieldNameConstants.flagUsage", "Emit a warning or error if @FieldNameConstants is used.") {}; + // ----- Wither ----- /** diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 6b516904..211b4924 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -441,4 +441,16 @@ public class HandlerUtil { } return String.format("%s%s", prefix, suffix); } + + public static String camelCaseToConstant(String fieldName) { + if (fieldName == null || fieldName.isEmpty()) return ""; + StringBuilder b = new StringBuilder(); + b.append(Character.toUpperCase(fieldName.charAt(0))); + for (int i = 1; i < fieldName.length(); i++) { + char c = fieldName.charAt(i); + if (Character.isUpperCase(c)) b.append('_'); + b.append(Character.toUpperCase(c)); + } + return b.toString(); + } } diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index 6617d21a..c49107e5 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -1097,7 +1097,7 @@ public class EclipseHandlerUtil { public static boolean filterField(FieldDeclaration declaration, boolean skipStatic) { // Skip the fake fields that represent enum constants. if (declaration.initialization instanceof AllocationExpression && - ((AllocationExpression)declaration.initialization).enumConstant != null) return false; + ((AllocationExpression) declaration.initialization).enumConstant != null) return false; if (declaration.type == null) return false; diff --git a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java index 597b0937..754ddf47 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2014-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,15 +21,17 @@ */ package lombok.eclipse.handlers; -import static java.lang.Character.*; +import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; import java.util.Collection; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; +import lombok.core.handlers.HandlerUtil; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; @@ -45,16 +47,9 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.mangosdk.spi.ProviderFor; -@ProviderFor(EclipseAnnotationHandler.class) public class HandleFieldNameConstants extends EclipseAnnotationHandler { - - public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level, boolean checkForTypeLevelFieldNameConstants) { - - if (checkForTypeLevelFieldNameConstants) { - if (hasAnnotation(FieldNameConstants.class, typeNode)) { - return true; - } - } - +@ProviderFor(EclipseAnnotationHandler.class) +public class HandleFieldNameConstants extends EclipseAnnotationHandler { + public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode errorNode, AccessLevel level) { TypeDeclaration typeDecl = null; if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); @@ -62,45 +57,20 @@ import org.mangosdk.spi.ProviderFor; boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0; if (typeDecl == null || notAClass) { - errorNode.addError("@FieldNameConstants is only supported on a class or an enum or a field."); - return false; + errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field."); + return; } for (EclipseNode field : typeNode.down()) { if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level); - if (field.getKind() != Kind.FIELD) return false; } - return true; } private void generateFieldNameConstantsForField(EclipseNode fieldNode, ASTNode pos, AccessLevel level) { - if (hasAnnotation(FieldNameConstants.class, fieldNode)) { - return; - } + if (hasAnnotation(FieldNameConstants.class, fieldNode)) return; createFieldNameConstantsForField(level, fieldNode, fieldNode, pos, false); } - private void createFieldNameConstantsForField(AccessLevel level, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { - if (fieldNode.getKind() != Kind.FIELD) { - errorNode.addError("@FieldNameConstants is only supported on a class or a field"); - return; - } - FieldDeclaration field = (FieldDeclaration) fieldNode.get(); - String constantName = camelCaseToConstant(new String(field.name)); - if (constantName == null) { - errorNode.addWarning("Not generating constant for this field: It does not fit in your @Accessors prefix list"); - return; - } - int pS = source.sourceStart, pE = source.sourceEnd; - long p = (long) pS << 32 | pE; - FieldDeclaration fieldConstant = new FieldDeclaration(constantName.toCharArray(), pS,pE); - fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; - fieldConstant.modifiers = toEclipseModifier(level) | Modifier.STATIC | Modifier.FINAL; - fieldConstant.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[]{p,p,p}); - fieldConstant.initialization = new StringLiteral(field.name, pS,pE,0); - injectField(fieldNode.up(), fieldConstant); - } - private boolean fieldQualifiesForFieldNameConstantsGeneration(EclipseNode field) { if (field.getKind() != Kind.FIELD) return false; FieldDeclaration fieldDecl = (FieldDeclaration) field.get(); @@ -108,43 +78,52 @@ import org.mangosdk.spi.ProviderFor; } public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_NAME_CONSTANTS_FLAG_USAGE, "@FieldNameConstants"); + EclipseNode node = annotationNode.up(); FieldNameConstants annotatationInstance = annotation.getInstance(); AccessLevel level = annotatationInstance.level(); if (node == null) return; - switch (node.getKind()){ + + switch (node.getKind()) { case FIELD: - createFieldNameConstantsForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true); + if (level != AccessLevel.NONE) createFieldNameConstantsForFields(level, annotationNode.upFromAnnotationToFields(), annotationNode, annotationNode.get(), true); break; case TYPE: - generateFieldDefaultsForType(node, annotationNode, level, false); + if (level == AccessLevel.NONE) { + annotationNode.addWarning("type-level '@FieldNameConstants' does not work with AccessLevel.NONE."); + return; + } + generateFieldNameConstantsForType(node, annotationNode, level); break; - } - } private void createFieldNameConstantsForFields(AccessLevel level, Collection fieldNodes, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { - for (EclipseNode fieldNode : fieldNodes){ - createFieldNameConstantsForField(level, fieldNode, errorNode, source, whineIfExists); - } + for (EclipseNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, fieldNode, errorNode, source, whineIfExists); } - - public static String camelCaseToConstant(final String fieldName) { - if (fieldName == null || fieldName.isEmpty()) return ""; - char[] chars = fieldName.toCharArray(); - StringBuilder b = new StringBuilder(); - b.append(toUpperCase(chars[0])); - for (int i = 1, iend = chars.length; i < iend; i++) { - char c = chars[i]; - if (isUpperCase(c)) { - b.append('_'); - } else { - c = toUpperCase(c); - } - b.append(c); + + private void createFieldNameConstantsForField(AccessLevel level, EclipseNode fieldNode, EclipseNode errorNode, ASTNode source, boolean whineIfExists) { + if (fieldNode.getKind() != Kind.FIELD) { + errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field"); + return; } - return b.toString(); + + FieldDeclaration field = (FieldDeclaration) fieldNode.get(); + String fieldName = new String(field.name); + String constantName = HandlerUtil.camelCaseToConstant(fieldName); + if (constantName.equals(fieldName)) { + fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field."); + return; + } + + int pS = source.sourceStart, pE = source.sourceEnd; + long p = (long) pS << 32 | pE; + FieldDeclaration fieldConstant = new FieldDeclaration(constantName.toCharArray(), pS,pE); + fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG; + fieldConstant.modifiers = toEclipseModifier(level) | Modifier.STATIC | Modifier.FINAL; + fieldConstant.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p,p,p}); + fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0); + injectField(fieldNode.up(), fieldConstant); } - } diff --git a/src/core/lombok/experimental/FieldNameConstants.java b/src/core/lombok/experimental/FieldNameConstants.java index 0f582f18..41b33ac7 100644 --- a/src/core/lombok/experimental/FieldNameConstants.java +++ b/src/core/lombok/experimental/FieldNameConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2014-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,16 +28,11 @@ import java.lang.annotation.Target; import lombok.AccessLevel; - - - /** * Generates String constants containing the field name for each field. - * */ -@Target(ElementType.TYPE) +@Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.SOURCE) public @interface FieldNameConstants { - - lombok.AccessLevel level()default AccessLevel.PUBLIC; + lombok.AccessLevel level() default AccessLevel.PUBLIC; } diff --git a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java index cf0fad0c..089d225d 100644 --- a/src/core/lombok/javac/handlers/HandleFieldNameConstants.java +++ b/src/core/lombok/javac/handlers/HandleFieldNameConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 The Project Lombok Authors. + * Copyright (C) 2014-2018 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,13 +21,16 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.lang.reflect.Modifier; import java.util.Collection; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; +import lombok.core.handlers.HandlerUtil; import lombok.core.AnnotationValues; import lombok.experimental.FieldNameConstants; import lombok.javac.JavacAnnotationHandler; @@ -43,18 +46,10 @@ import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; -import com.sun.tools.javac.util.List; -@ProviderFor(JavacAnnotationHandler.class) @SuppressWarnings("restriction") public class HandleFieldNameConstants extends JavacAnnotationHandler { - - public void generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean checkForTypeLevelFieldNameConstants) { - - if (checkForTypeLevelFieldNameConstants) { - if (hasAnnotation(FieldNameConstants.class, typeNode)) { - return; - } - } - +@ProviderFor(JavacAnnotationHandler.class) +public class HandleFieldNameConstants extends JavacAnnotationHandler { + public void generateFieldNameConstantsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level) { JCClassDecl typeDecl = null; if (typeNode.get() instanceof JCClassDecl) typeDecl = (JCClassDecl) typeNode.get(); @@ -62,21 +57,18 @@ import com.sun.tools.javac.util.List; boolean notAClass = (modifiers & (Flags.INTERFACE | Flags.ANNOTATION)) != 0; if (typeDecl == null || notAClass) { - errorNode.addError("@FieldNameConstants is only supported on a class or an enum or a field."); + errorNode.addError("@FieldNameConstants is only supported on a class, an enum, or a field."); return; } for (JavacNode field : typeNode.down()) { if (fieldQualifiesForFieldNameConstantsGeneration(field)) generateFieldNameConstantsForField(field, errorNode.get(), level); - } } private void generateFieldNameConstantsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level) { - if (hasAnnotation(FieldNameConstants.class, fieldNode)) { - return; - } - createFieldNameConstantsForField(level, fieldNode, fieldNode, false, List.nil()); + if (hasAnnotation(FieldNameConstants.class, fieldNode)) return; + createFieldNameConstantsForField(level, fieldNode, fieldNode, false); } private boolean fieldQualifiesForFieldNameConstantsGeneration(JavacNode field) { @@ -88,6 +80,7 @@ import com.sun.tools.javac.util.List; } public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_NAME_CONSTANTS_FLAG_USAGE, "@FieldNameConstants"); Collection fields = annotationNode.upFromAnnotationToFields(); deleteAnnotationIfNeccessary(annotationNode, FieldNameConstants.class); @@ -95,40 +88,36 @@ import com.sun.tools.javac.util.List; JavacNode node = annotationNode.up(); FieldNameConstants annotatationInstance = annotation.getInstance(); AccessLevel level = annotatationInstance.level(); - if (level == AccessLevel.NONE) { - annotationNode.addWarning("'lazy' does not work with AccessLevel.NONE."); - return; - } if (node == null) return; - List onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@FieldNameConstants(onMethod=", annotationNode); switch (node.getKind()) { case FIELD: - createFieldNameConstantsForFields(level, fields, annotationNode, annotationNode, true, onMethod); + if (level != AccessLevel.NONE) createFieldNameConstantsForFields(level, fields, annotationNode, annotationNode, true); break; case TYPE: - if (!onMethod.isEmpty()) { - annotationNode.addError("'onMethod' is not supported for @FieldNameConstants on a type."); + if (level == AccessLevel.NONE) { + annotationNode.addWarning("type-level '@FieldNameConstants' does not work with AccessLevel.NONE."); + return; } - generateFieldDefaultsForType(node, annotationNode, level, false); + generateFieldNameConstantsForType(node, annotationNode, level); break; } } - private void createFieldNameConstantsForFields(AccessLevel level, Collection fieldNodes, JavacNode annotationNode, JavacNode errorNode, boolean whineIfExists, List onMethod) { - for (JavacNode fieldNode : fieldNodes) { - createFieldNameConstantsForField(level, fieldNode, errorNode, whineIfExists, onMethod); - } + private void createFieldNameConstantsForFields(AccessLevel level, Collection fieldNodes, JavacNode annotationNode, JavacNode errorNode, boolean whineIfExists) { + for (JavacNode fieldNode : fieldNodes) createFieldNameConstantsForField(level, fieldNode, errorNode, whineIfExists); } - private void createFieldNameConstantsForField(AccessLevel level, JavacNode fieldNode, JavacNode source, boolean whineIfExists, List onMethod) { + private void createFieldNameConstantsForField(AccessLevel level, JavacNode fieldNode, JavacNode source, boolean whineIfExists) { if (fieldNode.getKind() != Kind.FIELD) { - source.addError("@FieldNameConstants is only supported on a class or a field"); + source.addError("@FieldNameConstants is only supported on a class, an enum, or a field"); return; } + JCVariableDecl field = (JCVariableDecl) fieldNode.get(); - String constantName = camelCaseToConstant(field.name.toString()); - if (constantName == null) { - source.addWarning("Not generating constant for this field: It does not fit in your @Accessors prefix list"); + String fieldName = field.name.toString(); + String constantName = HandlerUtil.camelCaseToConstant(fieldName); + if (constantName.equals(fieldName)) { + fieldNode.addWarning("Not generating constant for this field: The name of the constant would be equal to the name of this field."); return; } @@ -139,21 +128,4 @@ import com.sun.tools.javac.util.List; JCVariableDecl fieldConstant = treeMaker.VarDef(modifiers, fieldNode.toName(constantName), returnType, init); injectField(fieldNode.up(), fieldConstant); } - - public static String camelCaseToConstant(final String fieldName) { - if (fieldName == null || fieldName.isEmpty()) return ""; - char[] chars = fieldName.toCharArray(); - StringBuilder b = new StringBuilder(); - b.append(Character.toUpperCase(chars[0])); - for (int i = 1, iend = chars.length; i < iend; i++) { - char c = chars[i]; - if (Character.isUpperCase(c)) { - b.append('_'); - } else { - c = Character.toUpperCase(c); - } - b.append(c); - } - return b.toString(); - } } \ No newline at end of file diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java index 1a454585..3d672bc4 100644 --- a/test/core/src/lombok/AbstractRunTests.java +++ b/test/core/src/lombok/AbstractRunTests.java @@ -243,7 +243,7 @@ public abstract class AbstractRunTests { int size = Math.min(expectedLines.length, actualLines.length); if (size == 0 && expectedLines.length + actualLines.length > 0) { - Assert.fail("Missing / empty expected file."); + Assert.fail("Missing / empty expected file: " + name); } for (int i = 0; i < size; i++) { diff --git a/test/transform/resource/after-delombok/EqualsAndHashCodeWithGenericsOnInners.java b/test/transform/resource/after-delombok/EqualsAndHashCodeWithGenericsOnInners.java index 501d45a1..9eb3cdb3 100644 --- a/test/transform/resource/after-delombok/EqualsAndHashCodeWithGenericsOnInners.java +++ b/test/transform/resource/after-delombok/EqualsAndHashCodeWithGenericsOnInners.java @@ -1,3 +1,4 @@ +//version 7: public class EqualsAndHashCodeWithGenericsOnInners { class Inner { int x; diff --git a/test/transform/resource/after-delombok/FieldNameConstantsBasic.java b/test/transform/resource/after-delombok/FieldNameConstantsBasic.java new file mode 100644 index 00000000..de5d68c6 --- /dev/null +++ b/test/transform/resource/after-delombok/FieldNameConstantsBasic.java @@ -0,0 +1,8 @@ +public class FieldNameConstantsBasic { + protected static final java.lang.String I_AM_A_DVD_PLAYER = "iAmADvdPlayer"; + public static final java.lang.String BUT_PRINT_ME_PLEASE = "butPrintMePlease"; + String iAmADvdPlayer; + int $skipMe; + static double skipMeToo; + String butPrintMePlease; +} diff --git a/test/transform/resource/after-delombok/FieldNameConstantsWeird.java b/test/transform/resource/after-delombok/FieldNameConstantsWeird.java new file mode 100644 index 00000000..a256f5ba --- /dev/null +++ b/test/transform/resource/after-delombok/FieldNameConstantsWeird.java @@ -0,0 +1,4 @@ +public class FieldNameConstantsWeird { + String iAmADvdPlayer; + String X; +} diff --git a/test/transform/resource/after-delombok/InnerClass.java b/test/transform/resource/after-delombok/InnerClass.java index 6d42bb79..2e49b9ad 100644 --- a/test/transform/resource/after-delombok/InnerClass.java +++ b/test/transform/resource/after-delombok/InnerClass.java @@ -1,3 +1,4 @@ +//version 8: class A { class B { String s; @@ -48,4 +49,4 @@ class C { return "C.D(a=" + this.getA() + ")"; } } -} \ No newline at end of file +} diff --git a/test/transform/resource/after-ecj/FieldNameConstantsBasic.java b/test/transform/resource/after-ecj/FieldNameConstantsBasic.java new file mode 100644 index 00000000..bfa339fb --- /dev/null +++ b/test/transform/resource/after-ecj/FieldNameConstantsBasic.java @@ -0,0 +1,15 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; +public @FieldNameConstants class FieldNameConstantsBasic { + public static final java.lang.String BUT_PRINT_ME_PLEASE = "butPrintMePlease"; + protected static final java.lang.String I_AM_A_DVD_PLAYER = "iAmADvdPlayer"; + @FieldNameConstants(level = AccessLevel.PROTECTED) String iAmADvdPlayer; + int $skipMe; + static double skipMeToo; + String butPrintMePlease; + () { + } + public FieldNameConstantsBasic() { + super(); + } +} diff --git a/test/transform/resource/after-ecj/FieldNameConstantsWeird.java b/test/transform/resource/after-ecj/FieldNameConstantsWeird.java new file mode 100644 index 00000000..c581b7ef --- /dev/null +++ b/test/transform/resource/after-ecj/FieldNameConstantsWeird.java @@ -0,0 +1,9 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; +public @FieldNameConstants class FieldNameConstantsWeird { + @FieldNameConstants(level = AccessLevel.NONE) String iAmADvdPlayer; + String X; + public FieldNameConstantsWeird() { + super(); + } +} diff --git a/test/transform/resource/before/FieldNameConstantsBasic.java b/test/transform/resource/before/FieldNameConstantsBasic.java new file mode 100644 index 00000000..1bc15d84 --- /dev/null +++ b/test/transform/resource/before/FieldNameConstantsBasic.java @@ -0,0 +1,11 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; + +@FieldNameConstants +public class FieldNameConstantsBasic { + @FieldNameConstants(level = AccessLevel.PROTECTED) + String iAmADvdPlayer; + int $skipMe; + static double skipMeToo; + String butPrintMePlease; +} diff --git a/test/transform/resource/before/FieldNameConstantsWeird.java b/test/transform/resource/before/FieldNameConstantsWeird.java new file mode 100644 index 00000000..0f99133d --- /dev/null +++ b/test/transform/resource/before/FieldNameConstantsWeird.java @@ -0,0 +1,9 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; + +@FieldNameConstants +public class FieldNameConstantsWeird { + @FieldNameConstants(level = AccessLevel.NONE) + String iAmADvdPlayer; + String X; +} diff --git a/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages b/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages new file mode 100644 index 00000000..d5fc44f5 --- /dev/null +++ b/test/transform/resource/messages-delombok/FieldNameConstantsWeird.java.messages @@ -0,0 +1 @@ +8 Not generating constant for this field: The name of the constant would be equal to the name of this field. diff --git a/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages b/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages new file mode 100644 index 00000000..d5fc44f5 --- /dev/null +++ b/test/transform/resource/messages-ecj/FieldNameConstantsWeird.java.messages @@ -0,0 +1 @@ +8 Not generating constant for this field: The name of the constant would be equal to the name of this field. diff --git a/usage_examples/experimental/FieldNameConstantsExample_post.jpage b/usage_examples/experimental/FieldNameConstantsExample_post.jpage new file mode 100644 index 00000000..6300ed0e --- /dev/null +++ b/usage_examples/experimental/FieldNameConstantsExample_post.jpage @@ -0,0 +1,7 @@ +public class FieldNameConstantsExample { + public static final String I_AM_A_FIELD = "iAmAField"; + static final String AND_SO_AM_I = "andSoAmI"; + + private final String iAmAField; + private final int andSoAmI; +} diff --git a/usage_examples/experimental/FieldNameConstantsExample_pre.jpage b/usage_examples/experimental/FieldNameConstantsExample_pre.jpage new file mode 100644 index 00000000..dc6376eb --- /dev/null +++ b/usage_examples/experimental/FieldNameConstantsExample_pre.jpage @@ -0,0 +1,9 @@ +import lombok.experimental.FieldNameConstants; +import lombok.AccessLevel; + +@FieldNameConstants +public class FieldNameConstantsExample { + private final String iAmAField; + @FieldNameConstants(level = AccessLevel.PACKAGE) + private final int andSoAmI; +} diff --git a/website/templates/features/experimental/FieldNameConstants.html b/website/templates/features/experimental/FieldNameConstants.html new file mode 100644 index 00000000..05c57f84 --- /dev/null +++ b/website/templates/features/experimental/FieldNameConstants.html @@ -0,0 +1,44 @@ +<#import "../_features.html" as f> + +<@f.scaffold title="@FieldNameConstants" logline="Name... that... field! String constants for your field's names."> + <@f.history> +

+ @FieldNameConstants was introduced as experimental feature in lombok v2.0.0. +

+ + + <@f.experimental> +
    +
  • + New feature; unsure if this busts enough boilerplate. +
  • +
+ Current status: neutral - As a just-introduced feature we're still gathering feedback. + + + <@f.overview> +

+ The @FieldNameConstants annotation generates string constants (fields marked public static final, of type java.lang.String) containing the field's name, as a string. This is useful for various marshalling and serialization frameworks. The constant field has the same as the field it represents, except with all uppercase letters, with underscores in front of the uppercase letters in the original field. If this results in the exact same name, the constant is not generated (a warning is generated instead). +

+ The public access modifier can be changed via the parameter level = AccessLevel.PACKAGE for example. You can force a field to be skipped by supplying level = AccessLevel.NONE. +

+ Can be applied to classes (in which case every field gets a constant), or to an individual field. +

+ + + <@f.snippets name="experimental/FieldNameConstants" /> + + <@f.confKeys> +
+ lombok.fieldNameConstants.flagUsage = [warning | error] (default: not set) +
+ Lombok will flag any usage of @FieldDefaults as a warning or error if configured. +
+ + + <@f.smallPrint> +

+ Like other lombok handlers that touch fields, any field whose name starts with a dollar ($) symbol is skipped entirely. Such a field will not be modified at all. Static fields are also skipped. +

+ + diff --git a/website/templates/features/experimental/index.html b/website/templates/features/experimental/index.html index 21e8fceb..d9d67219 100644 --- a/website/templates/features/experimental/index.html +++ b/website/templates/features/experimental/index.html @@ -62,6 +62,10 @@ <@main.feature title="@Helper" href="Helper"> With a little help from my friends... Helper methods for java. + + <@main.feature title="@FieldNameConstants" href="FieldNameConstants"> + Name... that... field! String constants for your field's names. + <@f.confKeys> -- cgit