Skip to content

Commit

Permalink
[feature] FieldNameConstants now works like Builder: Make whatever bi…
Browse files Browse the repository at this point in the history
…ts it does by hand and lombok fills in whatever is missing.
  • Loading branch information
rzwitserloot committed Nov 1, 2018
1 parent 21878e2 commit 2fd46a5
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 68 deletions.
2 changes: 1 addition & 1 deletion doc/changelog.markdown
Expand Up @@ -3,7 +3,7 @@ Lombok Changelog

### v1.18.5 "Edgy Guinea Pig"
* BUGFIX: Since version 1.18.4, the delombok ant task didn't work and errored with a `NoClassDefFoundError`. [Issue #1932](https://github.com/rzwitserloot/lombok/issues/1932)

* FEATURE: The `@FieldNameConstants` feature now allows you to write the inner type by hand and add whatever you like to it; lombok will add the constants to this class. See the updated [FieldNameConstants feature](https://projectlombok.org/features/experimental/FieldNameConstants) page.
### v1.18.4 (October 30th, 2018)
* PLATFORM: Support for Eclipse Photon. [Issue #1831](https://github.com/rzwitserloot/lombok/issues/1831)
* PLATFORM: Angular IDE is now recognized by the installer [Issue #1830](https://github.com/rzwitserloot/lombok/issues/1830)
Expand Down
10 changes: 10 additions & 0 deletions src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java
Expand Up @@ -672,6 +672,16 @@ public static boolean hasAnnotation(String type, EclipseNode node) {
}
}

public static EclipseNode findInnerClass(EclipseNode parent, String name) {
char[] c = name.toCharArray();
for (EclipseNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
TypeDeclaration td = (TypeDeclaration) child.get();
if (Arrays.equals(td.name, c)) return child;
}
return null;
}

public static EclipseNode findAnnotation(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) {
if (node == null) return null;
if (type == null) return null;
Expand Down
10 changes: 0 additions & 10 deletions src/core/lombok/eclipse/handlers/HandleBuilder.java
Expand Up @@ -802,16 +802,6 @@ private void makeSimpleSetterMethodForBuilder(EclipseNode builderType, boolean d
injectMethod(builderType, setter);
}

public EclipseNode findInnerClass(EclipseNode parent, String name) {
char[] c = name.toCharArray();
for (EclipseNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
TypeDeclaration td = (TypeDeclaration) child.get();
if (Arrays.equals(td.name, c)) return child;
}
return null;
}

public EclipseNode makeBuilderClass(boolean isStatic, EclipseNode tdParent, String builderClassName, TypeParameter[] typeParams, ASTNode source) {
TypeDeclaration parent = (TypeDeclaration) tdParent.get();
TypeDeclaration builder = new TypeDeclaration(parent.compilationResult);
Expand Down
82 changes: 50 additions & 32 deletions src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java
Expand Up @@ -48,7 +48,6 @@
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.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.mangosdk.spi.ProviderFor;

Expand All @@ -75,7 +74,7 @@ public void generateFieldNameConstantsForType(EclipseNode typeNode, EclipseNode
if (qualified.isEmpty()) {
errorNode.addWarning("No fields qualify for @FieldNameConstants, therefore this annotation does nothing");
} else {
createInnerTypeFieldNameConstants(typeNode, errorNode.get(), level, qualified, asEnum, innerTypeName);
createInnerTypeFieldNameConstants(typeNode, errorNode, errorNode.get(), level, qualified, asEnum, innerTypeName);
}
}

Expand Down Expand Up @@ -115,53 +114,72 @@ public void handle(AnnotationValues<FieldNameConstants> annotation, Annotation a
generateFieldNameConstantsForType(node, annotationNode, level, asEnum, innerTypeName, annotationInstance.onlyExplicitlyIncluded());
}

private void createInnerTypeFieldNameConstants(EclipseNode typeNode, ASTNode source, AccessLevel level, List<EclipseNode> fields, boolean asEnum, String innerTypeName) {
private void createInnerTypeFieldNameConstants(EclipseNode typeNode, EclipseNode errorNode, ASTNode source, AccessLevel level, List<EclipseNode> fields, boolean asEnum, String innerTypeName) {
if (fields.isEmpty()) return;

TypeDeclaration parent = (TypeDeclaration) typeNode.get();
TypeDeclaration innerType = new TypeDeclaration(parent.compilationResult);
innerType.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
innerType.modifiers = toEclipseModifier(level) | (asEnum ? ClassFileConstants.AccEnum : ClassFileConstants.AccStatic | ClassFileConstants.AccFinal);
EclipseNode fieldsType = findInnerClass(typeNode, innerTypeName);
boolean genConstr = false, genClinit = false;
char[] name = innerTypeName.toCharArray();
innerType.name = name;
innerType.traverse(new SetGeneratedByVisitor(source), (ClassScope) null);
EclipseNode innerNode = injectType(typeNode, innerType);

ConstructorDeclaration constructor = new ConstructorDeclaration(parent.compilationResult);
constructor.selector = name;
constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
constructor.modifiers = ClassFileConstants.AccPrivate;
ExplicitConstructorCall superCall = new ExplicitConstructorCall(0);
superCall.sourceStart = source.sourceStart;
superCall.sourceEnd = source.sourceEnd;
superCall.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
constructor.constructorCall = superCall;
if (!asEnum) constructor.statements = new Statement[0];

injectMethod(innerNode, constructor);
TypeDeclaration generatedInnerType = null;
if (fieldsType == null) {
generatedInnerType = new TypeDeclaration(parent.compilationResult);
generatedInnerType.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
generatedInnerType.modifiers = toEclipseModifier(level) | (asEnum ? ClassFileConstants.AccEnum : ClassFileConstants.AccStatic | ClassFileConstants.AccFinal);
generatedInnerType.name = name;
fieldsType = injectType(typeNode, generatedInnerType);
genConstr = true;
genClinit = asEnum;
} else {
TypeDeclaration builderTypeDeclaration = (TypeDeclaration) fieldsType.get();
if ((builderTypeDeclaration.modifiers & (ClassFileConstants.AccEnum | ClassFileConstants.AccStatic)) == 0) {
errorNode.addError("Existing " + innerTypeName + " must be declared as an 'enum' or a 'static class'.");
return;
}
genConstr = constructorExists(fieldsType) == MemberExistsResult.NOT_EXISTS;
}

if (asEnum) injectMethod(innerNode, new Clinit(parent.compilationResult));
if (genConstr) {
ConstructorDeclaration constructor = new ConstructorDeclaration(parent.compilationResult);
constructor.selector = name;
constructor.declarationSourceStart = constructor.sourceStart = source.sourceStart;
constructor.declarationSourceEnd = constructor.sourceEnd = source.sourceEnd;
constructor.modifiers = ClassFileConstants.AccPrivate;
ExplicitConstructorCall superCall = new ExplicitConstructorCall(0);
superCall.sourceStart = source.sourceStart;
superCall.sourceEnd = source.sourceEnd;
superCall.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
constructor.constructorCall = superCall;
if (!asEnum) constructor.statements = new Statement[0];
injectMethod(fieldsType, constructor);
}
if (genClinit) {
Clinit cli = new Clinit(parent.compilationResult);
injectMethod(fieldsType, cli);
}


for (EclipseNode fieldNode : fields) {
FieldDeclaration field = (FieldDeclaration) fieldNode.get();
char[] fName = field.name;
if (fieldExists(new String(fName), fieldsType) != MemberExistsResult.NOT_EXISTS) continue;
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;
FieldDeclaration fieldConstant = new FieldDeclaration(fName, pS, pE);
fieldConstant.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
fieldConstant.modifiers = asEnum ? 0 : ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal;
fieldConstant.type = asEnum ? null : new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p});
FieldDeclaration constantField = new FieldDeclaration(fName, pS, pE);
constantField.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
constantField.modifiers = asEnum ? 0 : ClassFileConstants.AccPublic | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal;
constantField.type = asEnum ? null : new QualifiedTypeReference(TypeConstants.JAVA_LANG_STRING, new long[] {p, p, p});
if (asEnum) {
AllocationExpression ac = new AllocationExpression();
ac.enumConstant = fieldConstant;
ac.enumConstant = constantField;
ac.sourceStart = source.sourceStart;
ac.sourceEnd = source.sourceEnd;
fieldConstant.initialization = ac;
constantField.initialization = ac;
} else {
fieldConstant.initialization = new StringLiteral(field.name, pS, pE, 0);
constantField.initialization = new StringLiteral(field.name, pS, pE, 0);
}
injectField(innerNode, fieldConstant);
constantField.traverse(new SetGeneratedByVisitor(source), null);
injectField(fieldsType, constantField);
}
}
}
9 changes: 0 additions & 9 deletions src/core/lombok/javac/handlers/HandleBuilder.java
Expand Up @@ -726,15 +726,6 @@ private void makeSimpleSetterMethodForBuilder(JavacNode builderType, boolean dep
injectMethod(builderType, newMethod);
}

public JavacNode findInnerClass(JavacNode parent, String name) {
for (JavacNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
JCClassDecl td = (JCClassDecl) child.get();
if (td.name.contentEquals(name)) return child;
}
return null;
}

public JavacNode makeBuilderClass(boolean isStatic, JavacNode source, JavacNode tdParent, String builderClassName, List<JCTypeParameter> typeParams, JCAnnotation ast) {
JavacTreeMaker maker = tdParent.getTreeMaker();
int modifiers = Flags.PUBLIC;
Expand Down
46 changes: 34 additions & 12 deletions src/core/lombok/javac/handlers/HandleFieldNameConstants.java
Expand Up @@ -48,7 +48,6 @@
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;

Expand All @@ -75,7 +74,7 @@ public void generateFieldNameConstantsForType(JavacNode typeNode, JavacNode erro
if (qualified.isEmpty()) {
errorNode.addWarning("No fields qualify for @FieldNameConstants, therefore this annotation does nothing");
} else {
createInnerTypeFieldNameConstants(typeNode, errorNode.get(), level, qualified, asEnum, innerTypeName);
createInnerTypeFieldNameConstants(typeNode, errorNode, errorNode.get(), level, qualified, asEnum, innerTypeName);
}
}

Expand Down Expand Up @@ -122,24 +121,44 @@ public void handle(AnnotationValues<FieldNameConstants> annotation, JCAnnotation
generateFieldNameConstantsForType(node, annotationNode, level, asEnum, innerTypeName, annotationInstance.onlyExplicitlyIncluded());
}

private void createInnerTypeFieldNameConstants(JavacNode typeNode, DiagnosticPosition pos, AccessLevel level, java.util.List<JavacNode> fields, boolean asEnum, String innerTypeName) {
private void createInnerTypeFieldNameConstants(JavacNode typeNode, JavacNode errorNode, JCTree pos, AccessLevel level, java.util.List<JavacNode> fields, boolean asEnum, String innerTypeName) {
if (fields.isEmpty()) return;

JavacTreeMaker maker = typeNode.getTreeMaker();
JCModifiers mods = maker.Modifiers(toJavacModifier(level) | (asEnum ? Flags.ENUM : Flags.STATIC | Flags.FINAL));

Name fieldsName = typeNode.toName(innerTypeName);
JCClassDecl innerType = maker.ClassDef(mods, fieldsName, List.<JCTypeParameter>nil(), null, List.<JCExpression>nil(), List.<JCTree>nil());
JavacNode innerNode = injectType(typeNode, innerType);

JCModifiers genConstrMods = maker.Modifiers(Flags.GENERATEDCONSTR | (asEnum ? 0L : Flags.PRIVATE));
JCBlock genConstrBody = maker.Block(0L, List.<JCStatement>of(maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("super")), List.<JCExpression>nil()))));
JCMethodDecl genConstr = maker.MethodDef(genConstrMods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), genConstrBody, null);
JavacNode fieldsType = findInnerClass(typeNode, innerTypeName);
boolean genConstr = false;
if (fieldsType == null) {
JCClassDecl innerType = maker.ClassDef(mods, fieldsName, List.<JCTypeParameter>nil(), null, List.<JCExpression>nil(), List.<JCTree>nil());
fieldsType = injectType(typeNode, innerType);
recursiveSetGeneratedBy(innerType, pos, typeNode.getContext());
genConstr = true;
} else {
JCClassDecl builderTypeDeclaration = (JCClassDecl) fieldsType.get();
long f = builderTypeDeclaration.getModifiers().flags;
if ((f & (Flags.STATIC | Flags.ENUM)) == 0) {
errorNode.addError("Existing " + innerTypeName + " must be declared as an 'enum' or a 'static class'.");
return;
}
genConstr = constructorExists(fieldsType) == MemberExistsResult.NOT_EXISTS;
}

injectMethod(innerNode, genConstr);
if (genConstr) {
JCModifiers genConstrMods = maker.Modifiers(Flags.GENERATEDCONSTR | (asEnum ? 0L : Flags.PRIVATE));
JCBlock genConstrBody = maker.Block(0L, List.<JCStatement>of(maker.Exec(maker.Apply(List.<JCExpression>nil(), maker.Ident(typeNode.toName("super")), List.<JCExpression>nil()))));
JCMethodDecl c = maker.MethodDef(genConstrMods, typeNode.toName("<init>"), null, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), genConstrBody, null);
recursiveSetGeneratedBy(c, pos, typeNode.getContext());
injectMethod(fieldsType, c);
}

java.util.List<JCVariableDecl> generated = new ArrayList<JCVariableDecl>();
for (JavacNode field : fields) {
JCModifiers enumValueMods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC | Flags.FINAL | (asEnum ? Flags.ENUM : 0L));
Name fName = ((JCVariableDecl) field.get()).name;
if (fieldExists(fName.toString(), fieldsType) != MemberExistsResult.NOT_EXISTS) continue;
JCModifiers constantValueMods = maker.Modifiers(Flags.PUBLIC | Flags.STATIC | Flags.FINAL | (asEnum ? Flags.ENUM : 0L));
JCExpression returnType;
JCExpression init;
if (asEnum) {
Expand All @@ -149,8 +168,11 @@ private void createInnerTypeFieldNameConstants(JavacNode typeNode, DiagnosticPos
returnType = chainDots(field, "java", "lang", "String");
init = maker.Literal(field.getName());
}
JCVariableDecl enumField = maker.VarDef(enumValueMods, ((JCVariableDecl) field.get()).name, returnType, init);
injectField(innerNode, enumField);
JCVariableDecl constantField = maker.VarDef(constantValueMods, fName, returnType, init);
injectField(fieldsType, constantField, false, true);
setGeneratedBy(constantField, pos, typeNode.getContext());
generated.add(constantField);
}
for (JCVariableDecl cf : generated) recursiveSetGeneratedBy(cf, pos, typeNode.getContext());
}
}
17 changes: 15 additions & 2 deletions src/core/lombok/javac/handlers/JavacHandlerUtil.java
Expand Up @@ -224,6 +224,15 @@ private static boolean hasAnnotation(String type, JavacNode node, boolean delete
}
}

public static JavacNode findInnerClass(JavacNode parent, String name) {
for (JavacNode child : parent.down()) {
if (child.getKind() != Kind.TYPE) continue;
JCClassDecl td = (JCClassDecl) child.get();
if (td.name.contentEquals(name)) return child;
}
return null;
}

public static JavacNode findAnnotation(Class<? extends Annotation> type, JavacNode node) {
return findAnnotation(type, node, false);
}
Expand Down Expand Up @@ -1001,7 +1010,11 @@ public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field) {
return injectField(typeNode, field, false);
}

private static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addGenerated) {
public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addGenerated) {
return injectField(typeNode, field, addGenerated, false);
}

public static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, boolean addGenerated, boolean specialEnumHandling) {
JCClassDecl type = (JCClassDecl) typeNode.get();

if (addGenerated) {
Expand All @@ -1015,7 +1028,7 @@ private static JavacNode injectField(JavacNode typeNode, JCVariableDecl field, b
boolean skip = false;
if (insertBefore.head instanceof JCVariableDecl) {
JCVariableDecl f = (JCVariableDecl) insertBefore.head;
if (isEnumConstant(f) || isGenerated(f)) skip = true;
if ((!specialEnumHandling && isEnumConstant(f)) || isGenerated(f)) skip = true;
} else if (insertBefore.head instanceof JCMethodDecl) {
if ((((JCMethodDecl) insertBefore.head).mods.flags & GENERATEDCONSTR) != 0) skip = true;
}
Expand Down
Expand Up @@ -6,7 +6,7 @@ public class FieldNameConstantsBasic {
String butPrintMePlease;
@java.lang.SuppressWarnings("all")
static final class Fields {
public static final java.lang.String butPrintMePlease = "butPrintMePlease";
public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer";
public static final java.lang.String butPrintMePlease = "butPrintMePlease";
}
}
@@ -0,0 +1,30 @@
class FieldNameConstantsHandrolled1 {
int field1;
int alsoAField;
int thirdField;
public enum TypeTest {
alsoAField, thirdField, field1;
}
}
class FieldNameConstantsHandrolled2 {
int field1;
int alsoAField;
int thirdField;
public enum TypeTest {
alsoAField, thirdField, field1;
public String foo() {
return name();
}
}
}

class FieldNameConstantsHandrolled3 {
int field1;
int alsoAField;
int thirdField;
static class Fields {
public static final java.lang.String field1 = "field1";
public static final java.lang.String thirdField = "thirdField";
public static final int alsoAField = 5;
}
}
Expand Up @@ -2,8 +2,8 @@
import lombok.AccessLevel;
public @FieldNameConstants(level = AccessLevel.PACKAGE) class FieldNameConstantsBasic {
static final @java.lang.SuppressWarnings("all") class Fields {
public static final java.lang.String butPrintMePlease = "butPrintMePlease";
public static final java.lang.String iAmADvdPlayer = "iAmADvdPlayer";
public static final java.lang.String butPrintMePlease = "butPrintMePlease";
<clinit>() {
}
private @java.lang.SuppressWarnings("all") Fields() {
Expand Down

0 comments on commit 2fd46a5

Please sign in to comment.