diff options
author | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-10-06 00:25:32 +0200 |
---|---|---|
committer | Reinier Zwitserloot <reinier@zwitserloot.com> | 2015-10-06 00:25:32 +0200 |
commit | 9d8c4e4099bef9b7854cc5d77bc996c3b3bf0e41 (patch) | |
tree | 6ca48925c989d3f314fbeef08931a1940b5c03cf /src | |
parent | b4d69f5cb8b093718bb7ffb539e5875df52ca48e (diff) | |
download | lombok-9d8c4e4099bef9b7854cc5d77bc996c3b3bf0e41.tar.gz lombok-9d8c4e4099bef9b7854cc5d77bc996c3b3bf0e41.tar.bz2 lombok-9d8c4e4099bef9b7854cc5d77bc996c3b3bf0e41.zip |
New feature: FieldDefaults can now be configured to apply to _every_ file, regardless of annotations.
Diffstat (limited to 'src')
-rw-r--r-- | src/core/lombok/ConfigurationKeys.java | 14 | ||||
-rw-r--r-- | src/core/lombok/eclipse/handlers/HandleFieldDefaults.java | 81 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleFieldDefaults.java | 75 | ||||
-rw-r--r-- | src/core/lombok/javac/handlers/HandleVal.java | 14 | ||||
-rw-r--r-- | src/utils/lombok/eclipse/Eclipse.java | 19 |
5 files changed, 142 insertions, 61 deletions
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 67d51895..dd6732ed 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -396,6 +396,20 @@ public class ConfigurationKeys { // ----- FieldDefaults ----- /** + * lombok configuration: {@code lombok.fieldDefaults.defaultPrivate} = {@code true} | {@code false}. + * + * If set to <code>true</code> <em>any</em> field without an access modifier or {@code @PackagePrivate} is marked as {@code private} by lombok, in all source files compiled. + */ + public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_PRIVATE_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultPrivate", "If true, fields without any access modifier, in any file (lombok annotated or not) are marked as private. Use @PackagePrivate or an explicit modifier to override this.") {}; + + /** + * lombok configuration: {@code lombok.fieldDefaults.defaultFinal} = {@code true} | {@code false}. + * + * If set to <code>true</code> <em>any</em> field without {@code @NonFinal} is marked as {@code final} by lombok, in all source files compiled. + */ + public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_FINAL_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultFinal", "If true, fields, in any file (lombok annotated or not) are marked as final. Use @NonFinal to override this.") {}; + + /** * lombok configuration: {@code lombok.fieldDefaults.flagUsage} = {@code WARNING} | {@code ERROR}. * * If set, <em>any</em> usage of {@code @FieldDefaults} results in a warning / error. diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java index 33e796b7..f97a721b 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java @@ -23,12 +23,17 @@ package lombok.eclipse.handlers; import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; + +import java.util.Arrays; + import lombok.AccessLevel; import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; -import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.Eclipse; +import lombok.eclipse.EclipseASTAdapter; +import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseNode; import lombok.experimental.FieldDefaults; import lombok.experimental.NonFinal; @@ -37,16 +42,19 @@ import lombok.experimental.PackagePrivate; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.mangosdk.spi.ProviderFor; /** * Handles the {@code lombok.FieldDefaults} annotation for eclipse. */ -@ProviderFor(EclipseAnnotationHandler.class) +@ProviderFor(EclipseASTVisitor.class) @HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier. -public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> { +public class HandleFieldDefaults extends EclipseASTAdapter { public boolean generateFieldDefaultsForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) { if (checkForTypeLevelFieldDefaults) { if (hasAnnotation(FieldDefaults.class, typeNode)) { @@ -105,29 +113,60 @@ public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> fieldNode.rebuild(); } - public void handle(AnnotationValues<FieldDefaults> annotation, Annotation ast, EclipseNode annotationNode) { - handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults"); - - EclipseNode node = annotationNode.up(); - FieldDefaults instance = annotation.getInstance(); - AccessLevel level = instance.level(); - boolean makeFinal = instance.makeFinal(); + private static final char[] FIELD_DEFAULTS = "FieldDefaults".toCharArray(); + + @Override public void visitType(EclipseNode typeNode, TypeDeclaration type) { + AnnotationValues<FieldDefaults> fieldDefaults = null; + EclipseNode source = typeNode; - if (level == AccessLevel.NONE && !makeFinal) { - annotationNode.addError("This does nothing; provide either level or makeFinal or both."); - return; + boolean levelIsExplicit = false; + boolean makeFinalIsExplicit = false; + FieldDefaults fd = null; + for (EclipseNode jn : typeNode.down()) { + if (jn.getKind() != Kind.ANNOTATION) continue; + Annotation ann = (Annotation) jn.get(); + TypeReference typeTree = ann.type; + if (typeTree == null) continue; + if (typeTree instanceof SingleTypeReference) { + char[] t = ((SingleTypeReference) typeTree).token; + if (!Arrays.equals(t, FIELD_DEFAULTS)) continue; + } else if (typeTree instanceof QualifiedTypeReference) { + char[][] t = ((QualifiedTypeReference) typeTree).tokens; + if (!Eclipse.nameEquals(t, "lombok.experimental.FieldDefaults")) continue; + } else { + continue; + } + + if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue; + + source = jn; + fieldDefaults = createAnnotation(FieldDefaults.class, jn); + levelIsExplicit = fieldDefaults.isExplicit("level"); + makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal"); + + handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults"); + + fd = fieldDefaults.getInstance(); + if (!levelIsExplicit && !makeFinalIsExplicit) { + jn.addError("This does nothing; provide either level or makeFinal or both."); + } + + if (levelIsExplicit && fd.level() == AccessLevel.NONE) { + jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value."); + levelIsExplicit = false; + } + break; } - if (level == AccessLevel.PACKAGE) { - annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field."); - } + if (fd == null && (type.modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation)) != 0) return; - if (!makeFinal && annotation.isExplicit("makeFinal")) { - annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field."); - } + boolean defaultToPrivate = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE)); + boolean defaultToFinal = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE)); - if (node == null) return; + if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return; + AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null; + boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal; - generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false); + generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false); } } diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java index 95effded..5fb46afe 100644 --- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java @@ -31,23 +31,24 @@ import lombok.core.HandlerPriority; import lombok.experimental.FieldDefaults; import lombok.experimental.NonFinal; import lombok.experimental.PackagePrivate; -import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacASTAdapter; +import lombok.javac.JavacASTVisitor; import lombok.javac.JavacNode; import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; -import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; /** * Handles the {@code lombok.FieldDefaults} annotation for eclipse. */ -@ProviderFor(JavacAnnotationHandler.class) +@ProviderFor(JavacASTVisitor.class) @HandlerPriority(-2048) //-2^11; to ensure @Value picks up on messing with the fields' 'final' state, run earlier. -public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> { +public class HandleFieldDefaults extends JavacASTAdapter { public boolean generateFieldDefaultsForType(JavacNode typeNode, JavacNode errorNode, AccessLevel level, boolean makeFinal, boolean checkForTypeLevelFieldDefaults) { if (checkForTypeLevelFieldDefaults) { if (hasAnnotation(FieldDefaults.class, typeNode)) { @@ -72,13 +73,13 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> { //Skip fields that start with $ if (fieldDecl.name.toString().startsWith("$")) continue; - setFieldDefaultsForField(field, errorNode.get(), level, makeFinal); + setFieldDefaultsForField(field, level, makeFinal); } return true; } - public void setFieldDefaultsForField(JavacNode fieldNode, DiagnosticPosition pos, AccessLevel level, boolean makeFinal) { + public void setFieldDefaultsForField(JavacNode fieldNode, AccessLevel level, boolean makeFinal) { JCVariableDecl field = (JCVariableDecl) fieldNode.get(); if (level != null && level != AccessLevel.NONE) { if ((field.mods.flags & (Flags.PUBLIC | Flags.PRIVATE | Flags.PROTECTED)) == 0) { @@ -99,31 +100,53 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> { fieldNode.rebuild(); } - @Override public void handle(AnnotationValues<FieldDefaults> annotation, JCAnnotation ast, JavacNode annotationNode) { - handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults"); + @Override public void visitType(JavacNode typeNode, JCClassDecl type) { + AnnotationValues<FieldDefaults> fieldDefaults = null; + JavacNode source = typeNode; - deleteAnnotationIfNeccessary(annotationNode, FieldDefaults.class); - deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); - JavacNode node = annotationNode.up(); - FieldDefaults instance = annotation.getInstance(); - AccessLevel level = instance.level(); - boolean makeFinal = instance.makeFinal(); - - if (level == AccessLevel.NONE && !makeFinal) { - annotationNode.addError("This does nothing; provide either level or makeFinal or both."); - return; + boolean levelIsExplicit = false; + boolean makeFinalIsExplicit = false; + FieldDefaults fd = null; + for (JavacNode jn : typeNode.down()) { + if (jn.getKind() != Kind.ANNOTATION) continue; + JCAnnotation ann = (JCAnnotation) jn.get(); + JCTree typeTree = ann.annotationType; + if (typeTree == null) continue; + String typeTreeToString = typeTree.toString(); + if (!typeTreeToString.equals("FieldDefaults") && !typeTreeToString.equals("lombok.experimental.FieldDefaults")) continue; + if (!typeMatches(FieldDefaults.class, jn, typeTree)) continue; + + source = jn; + fieldDefaults = createAnnotation(FieldDefaults.class, jn); + levelIsExplicit = fieldDefaults.isExplicit("level"); + makeFinalIsExplicit = fieldDefaults.isExplicit("makeFinal"); + + handleExperimentalFlagUsage(jn, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults"); + + fd = fieldDefaults.getInstance(); + if (!levelIsExplicit && !makeFinalIsExplicit) { + jn.addError("This does nothing; provide either level or makeFinal or both."); + } + + if (levelIsExplicit && fd.level() == AccessLevel.NONE) { + jn.addError("AccessLevel.NONE doesn't mean anything here. Pick another value."); + levelIsExplicit = false; + } + + deleteAnnotationIfNeccessary(jn, FieldDefaults.class); + deleteImportFromCompilationUnit(jn, "lombok.AccessLevel"); + break; } - if (level == AccessLevel.PACKAGE) { - annotationNode.addError("Setting 'level' to PACKAGE does nothing. To force fields as package private, use the @PackagePrivate annotation on the field."); - } + if (fd == null && (type.mods.flags & (Flags.INTERFACE | Flags.ANNOTATION)) != 0) return; - if (!makeFinal && annotation.isExplicit("makeFinal")) { - annotationNode.addError("Setting 'makeFinal' to false does nothing. To force fields to be non-final, use the @NonFinal annotation on the field."); - } + boolean defaultToPrivate = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_PRIVATE_EVERYWHERE)); + boolean defaultToFinal = Boolean.TRUE.equals(typeNode.getAst().readConfiguration(ConfigurationKeys.FIELD_DEFAULTS_FINAL_EVERYWHERE)); - if (node == null) return; + if (!defaultToPrivate && !defaultToFinal && fieldDefaults == null) return; + AccessLevel fdAccessLevel = (fieldDefaults != null && levelIsExplicit) ? fd.level() : defaultToPrivate ? AccessLevel.PRIVATE : null; + boolean fdToFinal = (fieldDefaults != null && makeFinalIsExplicit) ? fd.makeFinal() : defaultToFinal; - generateFieldDefaultsForType(node, annotationNode, level, makeFinal, false); + generateFieldDefaultsForType(typeNode, source, fdAccessLevel, fdToFinal, false); } } diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 9eadd750..337ab2d7 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -51,11 +51,11 @@ import com.sun.tools.javac.util.List; @ResolutionResetNeeded public class HandleVal extends JavacASTAdapter { @Override public void visitLocal(JavacNode localNode, JCVariableDecl local) { - if (local.vartype == null || (!local.vartype.toString().equals("val") && !local.vartype.toString().equals("lombok.val"))) return; - - JCTree source = local.vartype; - - if (!typeMatches(val.class, localNode, local.vartype)) return; + JCTree typeTree = local.vartype; + if (typeTree == null) return; + String typeTreeToString = typeTree.toString(); + if (!typeTreeToString.equals("val") && !typeTreeToString.equals("lombok.val")) return; + if (!typeMatches(val.class, localNode, typeTree)) return; handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); @@ -88,7 +88,7 @@ public class HandleVal extends JavacASTAdapter { local.mods.flags |= Flags.FINAL; if (!localNode.shouldDeleteLombokAnnotations()) { - JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), source, localNode.getContext()); + JCAnnotation valAnnotation = recursiveSetGeneratedBy(localNode.getTreeMaker().Annotation(local.vartype, List.<JCExpression>nil()), typeTree, localNode.getContext()); local.mods.annotations = local.mods.annotations == null ? List.of(valAnnotation) : local.mods.annotations.append(valAnnotation); } @@ -156,7 +156,7 @@ public class HandleVal extends JavacASTAdapter { local.vartype = JavacResolution.createJavaLangObject(localNode.getAst()); throw e; } finally { - recursiveSetGeneratedBy(local.vartype, source, localNode.getContext()); + recursiveSetGeneratedBy(local.vartype, typeTree, localNode.getContext()); } } } diff --git a/src/utils/lombok/eclipse/Eclipse.java b/src/utils/lombok/eclipse/Eclipse.java index c2a863d5..18b22256 100644 --- a/src/utils/lombok/eclipse/Eclipse.java +++ b/src/utils/lombok/eclipse/Eclipse.java @@ -98,15 +98,20 @@ public class Eclipse { * string containing the same fully qualified name with dots in the string. */ public static boolean nameEquals(char[][] typeName, String string) { - StringBuilder sb = new StringBuilder(); - boolean first = true; - for (char[] elem : typeName) { - if (first) first = false; - else sb.append('.'); - sb.append(elem); + int pos = 0, len = string.length(); + for (int i = 0; i < typeName.length; i++) { + char[] t = typeName[i]; + if (i > 0) { + if (pos == len) return false; + if (string.charAt(pos++) != '.') return false; + } + for (int j = 0; j < t.length; j++) { + if (pos == len) return false; + if (string.charAt(pos++) != t[j]) return false; + } } - return string.contentEquals(sb); + return true; } public static boolean hasClinit(TypeDeclaration parent) { |