From 6d50bf41d15d664933d6ac4b16962eee793f1c17 Mon Sep 17 00:00:00 2001 From: glelouet Date: Fri, 6 Oct 2023 19:41:39 +0200 Subject: [PATCH 1/7] #3517 added annotation --- src/core/lombok/Onstruct.java | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/core/lombok/Onstruct.java diff --git a/src/core/lombok/Onstruct.java b/src/core/lombok/Onstruct.java new file mode 100644 index 0000000000..c5c84c4fd5 --- /dev/null +++ b/src/core/lombok/Onstruct.java @@ -0,0 +1,56 @@ +package lombok; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link Onstruct} annotation declares variables based on getters of an + * object.
+ * The variables names are the one specified. If the annotation has parameters for prefix and suffix, those parameters are added to the variable names.
+ * The getter is the existing method in the object's class verifying + *
    + *
  1. return non-void type
  2. + *
  3. requires no argument
  4. + *
  5. match the variable name specified, prefixed by get|is, and ignoring case. In the order : + *
      + *
    1. getName is selected if exists
    2. + *
    3. isName is selected if exists
    4. + *
    5. getname (ignoring case) is selected if exists only ONE. compiling error if several found
    6. + *
    7. isname (ignoring case) is selected if exists only ONE. compiling error if several found
    8. + *
    9. if no matching method exists, error
    10. + *
    + *
  6. + *
+ * + *

+ * It MUST only be applied to var declarations. + *

+ * + * + *

+ * Before: + * + *

+ * @Onstruct(prefix="b_")
+ * var author, name=mybook;
+ * 
+ * + * After: + * + *
+ * var b_author = mybook.getAuthor();
+ * var b_name = mybook.getName();
+ * 
+ * + */ +@Target(ElementType.LOCAL_VARIABLE) +@Retention(RetentionPolicy.SOURCE) +public @interface Onstruct { + + String prefix() default ""; + + String suffix() default ""; + +} From 82dd42807aa7a114c286eb6f681d1d0b48210730 Mon Sep 17 00:00:00 2001 From: glelouet Date: Fri, 6 Oct 2023 20:00:30 +0200 Subject: [PATCH 2/7] #3517 add eclipse files to .gitignore, update example with more variations --- .gitignore | 10 ++++++++++ src/core/lombok/Onstruct.java | 14 ++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index c9dcccb544..7325f384dd 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,13 @@ /jvm.locations /testenv /gpg.keyring + + +#eclipse files + +org.eclipse.core.resources.prefs +org.eclipse.jdt.core.prefs +target/ +.classpath +.project +.settings \ No newline at end of file diff --git a/src/core/lombok/Onstruct.java b/src/core/lombok/Onstruct.java index c5c84c4fd5..94a76b8d5c 100644 --- a/src/core/lombok/Onstruct.java +++ b/src/core/lombok/Onstruct.java @@ -13,28 +13,28 @@ *
    *
  1. return non-void type
  2. *
  3. requires no argument
  4. - *
  5. match the variable name specified, prefixed by get|is, and ignoring case. In the order : + *
  6. match the variable name specified, prefixed by get|is, and ignoring case. In the order : *
      *
    1. getName is selected if exists
    2. *
    3. isName is selected if exists
    4. *
    5. getname (ignoring case) is selected if exists only ONE. compiling error if several found
    6. *
    7. isname (ignoring case) is selected if exists only ONE. compiling error if several found
    8. - *
    9. if no matching method exists, error
    10. + *
    11. if no matching method exists, error
    12. *
    *
  7. - *
- * + * + * *

* It MUST only be applied to var declarations. *

* - * + * *

* Before: * *

  * @Onstruct(prefix="b_")
- * var author, name=mybook;
+ * var author, name, editiondate, purchasable = mybook;
  * 
* * After: @@ -42,6 +42,8 @@ *
  * var b_author = mybook.getAuthor();
  * var b_name = mybook.getName();
+ * var b_editiondate = mybook.getEditionDate();
+ * var b_purchasable = mybook.isPurchasable();
  * 
* */ From 7571e415bf21c3a158811532e98110c09264ec85 Mon Sep 17 00:00:00 2001 From: glelouet Date: Fri, 6 Oct 2023 20:32:50 +0200 Subject: [PATCH 3/7] #3517 added handler and test, nothing compiles ? --- .../eclipse/handlers/HandleOnstruct.java | 20 ++++++++++++ .../resource/before/OnstructBook.java | 32 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/core/lombok/eclipse/handlers/HandleOnstruct.java create mode 100644 test/transform/resource/before/OnstructBook.java diff --git a/src/core/lombok/eclipse/handlers/HandleOnstruct.java b/src/core/lombok/eclipse/handlers/HandleOnstruct.java new file mode 100644 index 0000000000..f332648478 --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleOnstruct.java @@ -0,0 +1,20 @@ +package lombok.eclipse.handlers; + +import lombok.Onstruct; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; + +@HandlerPriority(65536) // same as HandleValue // TODO +public class HandleOnstruct extends EclipseAnnotationHandler { + + public static final HandleOnstruct INSTANCE = new HandleOnstruct(); + + @Override + public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { + // TODO Auto-generated method stub + + } + +} diff --git a/test/transform/resource/before/OnstructBook.java b/test/transform/resource/before/OnstructBook.java new file mode 100644 index 0000000000..e77aa3013a --- /dev/null +++ b/test/transform/resource/before/OnstructBook.java @@ -0,0 +1,32 @@ +import java.util.Date; +import java.util.Objects; + +import lombok.AllArgsConstructor; +import lombok.Onstruct; +import lombok.experimental.Accessors; + +public class OnstructBook { + + @lombok.Getter + @AllArgsConstructor + public static class Book { + + private String author; + @Accessors(fluent = true) + private String name; + private Date editionDate; + private boolean purchasable; + + } + + void test() { + Book mybook = new Book("author0", "bookname0", new Date(), true); + @Onstruct(prefix = "b_") + Object author, name, editiondate, purchasable = mybook; + assert Objects.equals(mybook.getAuthor(), author); + assert Objects.equals(mybook.name(), name); + assert Objects.equals(mybook.getEditionDate(), editiondate); + assert Objects.equals(mybook.isPurchasable(), purchasable); + } + +} From eab7d44bb245245f69dc00c03da7019b36d753cc Mon Sep 17 00:00:00 2001 From: glelouet Date: Tue, 10 Oct 2023 03:59:58 +0200 Subject: [PATCH 4/7] using ant to make eclipse project, compiles now. Test fails on the test class (expected) --- src/core/lombok/Onstruct.java | 39 ++++++++++++------- .../eclipse/handlers/HandleOnstruct.java | 12 +++--- .../resource/before/OnstructBook.java | 12 +++++- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/core/lombok/Onstruct.java b/src/core/lombok/Onstruct.java index 94a76b8d5c..34cb2d4df0 100644 --- a/src/core/lombok/Onstruct.java +++ b/src/core/lombok/Onstruct.java @@ -8,24 +8,33 @@ /** * The {@link Onstruct} annotation declares variables based on getters of an * object.
- * The variables names are the one specified. If the annotation has parameters for prefix and suffix, those parameters are added to the variable names.
+ * The variables names are the one specified. If the annotation has parameters + * for prefix and suffix, those parameters are added to the variable + * names.
* The getter is the existing method in the object's class verifying *
    - *
  1. return non-void type
  2. - *
  3. requires no argument
  4. - *
  5. match the variable name specified, prefixed by get|is, and ignoring case. In the order : - *
      - *
    1. getName is selected if exists
    2. - *
    3. isName is selected if exists
    4. - *
    5. getname (ignoring case) is selected if exists only ONE. compiling error if several found
    6. - *
    7. isname (ignoring case) is selected if exists only ONE. compiling error if several found
    8. - *
    9. if no matching method exists, error
    10. - *
    - *
  6. + *
  7. return non-void type
  8. + *
  9. requires no argument
  10. + *
  11. match the variable name specified, prefixed by get|is, and ignoring case. + * In the order : + *
      + *
    1. getName is selected if exists
    2. + *
    3. isName is selected if exists
    4. + *
    5. getname (ignoring case) is selected if exists only ONE. compiling error + * if several found
    6. + *
    7. isname (ignoring case) is selected if exists only ONE. compiling error if + * several found
    8. + *
    9. name is selected if exists
    10. + *
    11. name (ignoring case) is selected if exists only ONE. compiling error if + * several found
    12. + *
    13. if no matching method exists, error
    14. + *
    + *
  12. *
* *

- * It MUST only be applied to var declarations. + * It MUST only be applied to typed declarations. No garantee is present for var + * declaration. *

* * @@ -33,8 +42,8 @@ * Before: * *
- * @Onstruct(prefix="b_")
- * var author, name, editiondate, purchasable = mybook;
+ * @Onstruct(prefix = "b_")
+ * Object author, name, editiondate, purchasable = mybook;
  * 
* * After: diff --git a/src/core/lombok/eclipse/handlers/HandleOnstruct.java b/src/core/lombok/eclipse/handlers/HandleOnstruct.java index f332648478..c2fb638a2a 100644 --- a/src/core/lombok/eclipse/handlers/HandleOnstruct.java +++ b/src/core/lombok/eclipse/handlers/HandleOnstruct.java @@ -1,5 +1,7 @@ package lombok.eclipse.handlers; +import org.eclipse.jdt.internal.compiler.ast.Annotation; + import lombok.Onstruct; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; @@ -8,13 +10,13 @@ @HandlerPriority(65536) // same as HandleValue // TODO public class HandleOnstruct extends EclipseAnnotationHandler { - + public static final HandleOnstruct INSTANCE = new HandleOnstruct(); - - @Override - public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { + + @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { // TODO Auto-generated method stub } - + + } diff --git a/test/transform/resource/before/OnstructBook.java b/test/transform/resource/before/OnstructBook.java index e77aa3013a..6d4501a10d 100644 --- a/test/transform/resource/before/OnstructBook.java +++ b/test/transform/resource/before/OnstructBook.java @@ -21,7 +21,7 @@ public static class Book { void test() { Book mybook = new Book("author0", "bookname0", new Date(), true); - @Onstruct(prefix = "b_") + @Onstruct() Object author, name, editiondate, purchasable = mybook; assert Objects.equals(mybook.getAuthor(), author); assert Objects.equals(mybook.name(), name); @@ -29,4 +29,14 @@ void test() { assert Objects.equals(mybook.isPurchasable(), purchasable); } + void testPrefix() { + Book mybook = new Book("author0", "bookname0", new Date(), true); + @Onstruct(prefix = "b_") + Object author, name, editiondate, purchasable = mybook; + assert Objects.equals(mybook.getAuthor(), b_author); + assert Objects.equals(mybook.name(), b_name); + assert Objects.equals(mybook.getEditionDate(), b_editiondate); + assert Objects.equals(mybook.isPurchasable(), b_purchasable); + } + } From 51195c1676ad18b0117ca845409beaa062791d86 Mon Sep 17 00:00:00 2001 From: glelouet Date: Wed, 11 Oct 2023 18:24:45 +0200 Subject: [PATCH 5/7] issue #3517 modified annotation to have shorter name for prefix, suffix, added option to camelcase the variable name and the method name. Created util to extract the var name and met name from the requested name and the annotation. Changed the tests to have the var match the getter, and implemented the var name and assignment replacement in the node. --- src/core/lombok/Onstruct.java | 28 +++- .../lombok/core/handlers/OnstructUtils.java | 24 ++++ .../eclipse/handlers/HandleOnstruct.java | 12 +- .../lombok/javac/handlers/HandleOnstruct.java | 132 ++++++++++++++++++ .../resource/before/OnstructBook.java | 20 ++- 5 files changed, 203 insertions(+), 13 deletions(-) create mode 100644 src/core/lombok/core/handlers/OnstructUtils.java create mode 100644 src/core/lombok/javac/handlers/HandleOnstruct.java diff --git a/src/core/lombok/Onstruct.java b/src/core/lombok/Onstruct.java index 34cb2d4df0..2c6f175818 100644 --- a/src/core/lombok/Onstruct.java +++ b/src/core/lombok/Onstruct.java @@ -42,8 +42,7 @@ * Before: * *
- * @Onstruct(prefix = "b_")
- * Object author, name, editiondate, purchasable = mybook;
+ * @Onstruct(pre = "b_") Object author, name, editiondate, purchasable = mybook;
  * 
* * After: @@ -60,8 +59,29 @@ @Retention(RetentionPolicy.SOURCE) public @interface Onstruct { - String prefix() default ""; + /** + * prefix to start the created var name with. Default is empty + */ + String pre() default ""; - String suffix() default ""; + /** + * suffix to append to created var name. Default is empty + */ + String suf() default ""; + + /** + * if true, should camel case the variable name. Only applied when prefix is + * non blank. Default is false. + */ + boolean cml() default false; + + /** prefix to start the getter call by. Default is "get" */ + String methodPre() default "get"; + + /** + * if true, should camel case the method call. Only applied when method + * prefix is non blank. Default is true. + */ + boolean methodCml() default true; } diff --git a/src/core/lombok/core/handlers/OnstructUtils.java b/src/core/lombok/core/handlers/OnstructUtils.java new file mode 100644 index 0000000000..fa4254dfbc --- /dev/null +++ b/src/core/lombok/core/handlers/OnstructUtils.java @@ -0,0 +1,24 @@ +package lombok.core.handlers; + +import lombok.Onstruct; + +public class OnstructUtils { + + public static String varName(String requestedName, Onstruct instance) { + String prefix = instance.pre(); + String suffix = instance.suf(); + boolean cml = instance.cml() && prefix != null && !prefix.isEmpty(); + return (prefix != null ? prefix : "") + (cml ? cml(requestedName) : requestedName) + (suffix != null ? suffix : ""); + } + + public static String methodName(String requestedName, Onstruct instance) { + String methodPrefix = instance.methodPre(); + if (methodPrefix == null || methodPrefix.isEmpty()) return requestedName; + return methodPrefix + (instance.methodCml() ? cml(requestedName) : requestedName); + } + + public static String cml(String name) { + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + +} diff --git a/src/core/lombok/eclipse/handlers/HandleOnstruct.java b/src/core/lombok/eclipse/handlers/HandleOnstruct.java index c2fb638a2a..059c6d34f8 100644 --- a/src/core/lombok/eclipse/handlers/HandleOnstruct.java +++ b/src/core/lombok/eclipse/handlers/HandleOnstruct.java @@ -1,21 +1,29 @@ package lombok.eclipse.handlers; +import java.io.PrintStream; + import org.eclipse.jdt.internal.compiler.ast.Annotation; import lombok.Onstruct; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; +import lombok.eclipse.DeferUntilPostDiet; +import lombok.eclipse.EclipseASTVisitor; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.spi.Provides; +@Provides +@DeferUntilPostDiet @HandlerPriority(65536) // same as HandleValue // TODO public class HandleOnstruct extends EclipseAnnotationHandler { public static final HandleOnstruct INSTANCE = new HandleOnstruct(); @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { - // TODO Auto-generated method stub - + PrintStream stream = System.out; + stream.println("got annotation on " + ast); + annotationNode.up().traverse(new EclipseASTVisitor.Printer(true, stream, true)); } diff --git a/src/core/lombok/javac/handlers/HandleOnstruct.java b/src/core/lombok/javac/handlers/HandleOnstruct.java new file mode 100644 index 0000000000..121e6520a6 --- /dev/null +++ b/src/core/lombok/javac/handlers/HandleOnstruct.java @@ -0,0 +1,132 @@ +package lombok.javac.handlers; + +import static lombok.javac.handlers.JavacHandlerUtil.deleteAnnotationIfNeccessary; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; + +import lombok.Onstruct; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.core.LombokNode; +import lombok.core.handlers.OnstructUtils; +import lombok.eclipse.DeferUntilPostDiet; +import lombok.javac.JavacAnnotationHandler; +import lombok.javac.JavacNode; +import lombok.javac.JavacTreeMaker; +import lombok.spi.Provides; + +@Provides +@DeferUntilPostDiet +@HandlerPriority(65536) // same as HandleValue // TODO +public class HandleOnstruct extends JavacAnnotationHandler { + + + /** + * find the siblings with same kind and annotation. Copy of + * {@link LombokNode#upFromAnnotationToFields()} with same kind and no check + * on the parent. + * + */ + public static Collection upFromAnnotationToSameKind(JavacNode node) { + if (node.getKind() != Kind.ANNOTATION) return Collections.emptyList(); + JavacNode declaration = node.up(); + if (declaration == null) return Collections.emptyList(); + + List fields = new ArrayList(); + + for (JavacNode potentialField : declaration.up().down()) { + if (potentialField.getKind() != declaration.getKind()) continue; + for (JavacNode child : potentialField.down()) { + if (child.getKind() != Kind.ANNOTATION) continue; + if (child.get() == node.get()) fields.add(potentialField); + } + } + + return fields; + } + + /** + * retrieve the children statements from a list of node + */ + protected static List findChildrenStatements(Collection fields) { + List ret = new ArrayList(); + for (JavacNode f : fields) { + for (JavacNode potentialStatement : f.down()) { + if (potentialStatement.getKind() == Kind.STATEMENT) { + ret.add(potentialStatement.get()); + } + } + } + return ret; + } + + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { + Collection annotatedVariables = upFromAnnotationToSameKind(annotationNode); + JavacNode parentNode = annotationNode.up(); + Onstruct annotationInstance = annotation.getInstance(); + deleteAnnotationIfNeccessary(annotationNode, Onstruct.class); + + List statements = findChildrenStatements(annotatedVariables); + // sanity checks on statements. Among the variables declaration, there + // must be one statement. + if (statements.isEmpty()) { + annotationNode.addError("no assignment. Requires one identifier assignment."); + return; + } + if (statements.size() > 1) { + annotationNode.addError("Too many assignments:" + statements + " Requires exactly one identifier assignment."); + return; + } + + JCTree tree = statements.get(0); + JCTree.JCIdent ident; + String varName = null; + // sanity checks on the assignment. It must be an identifier. + if (tree instanceof JCTree.JCIdent) { + ident = (JCTree.JCIdent) tree; + varName = (ident.name.toString()); + } else { + annotationNode.addError("invalid assignment" + tree + " : must be an identifier"); + return; + } + if (varName == null) { + annotationNode.addError("assignement is null . Must be an identifier"); + return; + } + + for (JavacNode f : annotatedVariables) { + handleVarDeclaration(f, annotationInstance, parentNode, ident); + } + // parentNode.rebuild(); + } + + private void handleVarDeclaration(JavacNode varNode, Onstruct annotationInstance, JavacNode parentNode, JCIdent ident) { + String varName = OnstructUtils.varName(varNode.getName(), annotationInstance); + String methName = OnstructUtils.methodName(varNode.getName(), annotationInstance); + JCTree elem = varNode.get(); + JavacTreeMaker maker = varNode.getTreeMaker(); + // System.out.println("create : var " + varName + " = " + ident.name + "." + methName + "();"); + if (elem instanceof JCVariableDecl) { + JCVariableDecl variable = (JCVariableDecl) elem; + variable.type = (JavacHandlerUtil.chainDotsString(varNode, "java.lang.String")).type; + JCExpression methCall = maker.Select(ident, varNode.toName(methName)); + variable.init = maker.Apply(com.sun.tools.javac.util.List.nil(), + methCall, + com.sun.tools.javac.util.List.nil()); + variable.name = varNode.toName(varName); + // System.out.println("replaced with " + variable); + } else + System.err.println(varNode.get()); + } + +} diff --git a/test/transform/resource/before/OnstructBook.java b/test/transform/resource/before/OnstructBook.java index 6d4501a10d..69a4d801fb 100644 --- a/test/transform/resource/before/OnstructBook.java +++ b/test/transform/resource/before/OnstructBook.java @@ -3,6 +3,7 @@ import lombok.AllArgsConstructor; import lombok.Onstruct; +import lombok.core.PrintAST; import lombok.experimental.Accessors; public class OnstructBook { @@ -21,21 +22,26 @@ public static class Book { void test() { Book mybook = new Book("author0", "bookname0", new Date(), true); - @Onstruct() - Object author, name, editiondate, purchasable = mybook; + @Onstruct + Object author, editionDate = mybook; + @Onstruct(methodPre = "") + Object name = mybook; assert Objects.equals(mybook.getAuthor(), author); assert Objects.equals(mybook.name(), name); - assert Objects.equals(mybook.getEditionDate(), editiondate); + assert Objects.equals(mybook.getEditionDate(), editionDate); + @Onstruct(methodPre = "is") + Object purchasable = mybook; assert Objects.equals(mybook.isPurchasable(), purchasable); } void testPrefix() { Book mybook = new Book("author0", "bookname0", new Date(), true); - @Onstruct(prefix = "b_") - Object author, name, editiondate, purchasable = mybook; + @Onstruct(pre = "b_") + Object author, editionDate = mybook; assert Objects.equals(mybook.getAuthor(), b_author); - assert Objects.equals(mybook.name(), b_name); - assert Objects.equals(mybook.getEditionDate(), b_editiondate); + assert Objects.equals(mybook.getEditionDate(), b_editionDate); + @Onstruct(pre="b_", methodPre = "is") + Object purchasable = mybook; assert Objects.equals(mybook.isPurchasable(), b_purchasable); } From f72ec4d44753273a0a655ab5a4340737efe44b3d Mon Sep 17 00:00:00 2001 From: glelouet Date: Sun, 15 Oct 2023 14:25:14 +0200 Subject: [PATCH 6/7] feat #3517 removed asserts as they are not used. +minor fixes --- .../lombok/javac/handlers/HandleOnstruct.java | 32 ++++++++++--------- .../resource/before/OnstructBook.java | 7 ---- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/core/lombok/javac/handlers/HandleOnstruct.java b/src/core/lombok/javac/handlers/HandleOnstruct.java index 121e6520a6..1cdadc66eb 100644 --- a/src/core/lombok/javac/handlers/HandleOnstruct.java +++ b/src/core/lombok/javac/handlers/HandleOnstruct.java @@ -29,21 +29,22 @@ @DeferUntilPostDiet @HandlerPriority(65536) // same as HandleValue // TODO public class HandleOnstruct extends JavacAnnotationHandler { - - + + /** * find the siblings with same kind and annotation. Copy of * {@link LombokNode#upFromAnnotationToFields()} with same kind and no check * on the parent. * */ + @SuppressWarnings({"unchecked", "rawtypes"}) public static Collection upFromAnnotationToSameKind(JavacNode node) { if (node.getKind() != Kind.ANNOTATION) return Collections.emptyList(); JavacNode declaration = node.up(); if (declaration == null) return Collections.emptyList(); - + List fields = new ArrayList(); - + for (JavacNode potentialField : declaration.up().down()) { if (potentialField.getKind() != declaration.getKind()) continue; for (JavacNode child : potentialField.down()) { @@ -51,13 +52,14 @@ public static Collection upFromAnnotationToSameKind(JavacNode node) { if (child.get() == node.get()) fields.add(potentialField); } } - + return fields; } - + /** * retrieve the children statements from a list of node */ + @SuppressWarnings({"unchecked", "rawtypes"}) protected static List findChildrenStatements(Collection fields) { List ret = new ArrayList(); for (JavacNode f : fields) { @@ -69,16 +71,16 @@ protected static List findChildrenStatements(Collection field } return ret; } - + @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { Collection annotatedVariables = upFromAnnotationToSameKind(annotationNode); JavacNode parentNode = annotationNode.up(); Onstruct annotationInstance = annotation.getInstance(); deleteAnnotationIfNeccessary(annotationNode, Onstruct.class); - + List statements = findChildrenStatements(annotatedVariables); // sanity checks on statements. Among the variables declaration, there - // must be one statement. + // must be exactly one statement. if (statements.isEmpty()) { annotationNode.addError("no assignment. Requires one identifier assignment."); return; @@ -87,7 +89,7 @@ protected static List findChildrenStatements(Collection field annotationNode.addError("Too many assignments:" + statements + " Requires exactly one identifier assignment."); return; } - + JCTree tree = statements.get(0); JCTree.JCIdent ident; String varName = null; @@ -103,13 +105,13 @@ protected static List findChildrenStatements(Collection field annotationNode.addError("assignement is null . Must be an identifier"); return; } - + for (JavacNode f : annotatedVariables) { handleVarDeclaration(f, annotationInstance, parentNode, ident); } // parentNode.rebuild(); } - + private void handleVarDeclaration(JavacNode varNode, Onstruct annotationInstance, JavacNode parentNode, JCIdent ident) { String varName = OnstructUtils.varName(varNode.getName(), annotationInstance); String methName = OnstructUtils.methodName(varNode.getName(), annotationInstance); @@ -118,15 +120,15 @@ private void handleVarDeclaration(JavacNode varNode, Onstruct annotationInstance // System.out.println("create : var " + varName + " = " + ident.name + "." + methName + "();"); if (elem instanceof JCVariableDecl) { JCVariableDecl variable = (JCVariableDecl) elem; - variable.type = (JavacHandlerUtil.chainDotsString(varNode, "java.lang.String")).type; + variable.type = JavacHandlerUtil.chainDots(varNode, "lombok", "var").type; JCExpression methCall = maker.Select(ident, varNode.toName(methName)); variable.init = maker.Apply(com.sun.tools.javac.util.List.nil(), methCall, com.sun.tools.javac.util.List.nil()); variable.name = varNode.toName(varName); - // System.out.println("replaced with " + variable); + System.out.println("replaced with " + variable); } else System.err.println(varNode.get()); } - + } diff --git a/test/transform/resource/before/OnstructBook.java b/test/transform/resource/before/OnstructBook.java index 69a4d801fb..1f2e5f7937 100644 --- a/test/transform/resource/before/OnstructBook.java +++ b/test/transform/resource/before/OnstructBook.java @@ -26,23 +26,16 @@ void test() { Object author, editionDate = mybook; @Onstruct(methodPre = "") Object name = mybook; - assert Objects.equals(mybook.getAuthor(), author); - assert Objects.equals(mybook.name(), name); - assert Objects.equals(mybook.getEditionDate(), editionDate); @Onstruct(methodPre = "is") Object purchasable = mybook; - assert Objects.equals(mybook.isPurchasable(), purchasable); } void testPrefix() { Book mybook = new Book("author0", "bookname0", new Date(), true); @Onstruct(pre = "b_") Object author, editionDate = mybook; - assert Objects.equals(mybook.getAuthor(), b_author); - assert Objects.equals(mybook.getEditionDate(), b_editionDate); @Onstruct(pre="b_", methodPre = "is") Object purchasable = mybook; - assert Objects.equals(mybook.isPurchasable(), b_purchasable); } } From 342615554d2a997886cf729b10710b9eeb314ee0 Mon Sep 17 00:00:00 2001 From: glelouet Date: Sun, 15 Oct 2023 15:38:12 +0200 Subject: [PATCH 7/7] feat #3517 added source type go deduce method name. method options can still be overwritten. --- src/core/lombok/Onstruct.java | 64 ++++++++++++++++--- .../lombok/core/handlers/OnstructUtils.java | 10 ++- .../resource/before/OnstructBook.java | 20 ++++-- 3 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/core/lombok/Onstruct.java b/src/core/lombok/Onstruct.java index 2c6f175818..acb641c7ba 100644 --- a/src/core/lombok/Onstruct.java +++ b/src/core/lombok/Onstruct.java @@ -58,30 +58,78 @@ @Target(ElementType.LOCAL_VARIABLE) @Retention(RetentionPolicy.SOURCE) public @interface Onstruct { - + + // + // variable generation + // + /** * prefix to start the created var name with. Default is empty */ String pre() default ""; - + /** * suffix to append to created var name. Default is empty */ String suf() default ""; - + /** * if true, should camel case the variable name. Only applied when prefix is * non blank. Default is false. */ boolean cml() default false; + + // + // method generation + // + + /** + * how to build the getter for a var name + */ + public enum SourceType { + GET("get", true), BOOL("is", true), FLUENT("", false); + + /** prefix to start the getter method with*/ + public final String pre; + + /** should we uppercase the first letter of the variable in the method name ? */ + public final boolean cml; + + SourceType(String pre, boolean cml) { + this.pre = pre; + this.cml = cml; + } + } + + public SourceType source() default SourceType.GET; + + public enum Cml { + CML(true), NOCML(false), SOURCE(null); + ; + + public final Boolean cml; + + Cml(Boolean cml) { + this.cml = cml; + } + } - /** prefix to start the getter call by. Default is "get" */ - String methodPre() default "get"; + /** + * can't set default value to null or non-constant values :/ + */ + static final String NULLSTRING = "NULLSTRING"; /** - * if true, should camel case the method call. Only applied when method - * prefix is non blank. Default is true. + * overwrite the {@link #source()} prefix to start the getter call by. + * Default is {@link #NULLSTRING} to not overwrite */ - boolean methodCml() default true; + String methodPre() default NULLSTRING; + /** + * Overwrite the {@link #source()}'s method camel case. If + * {@link Cml#SOURCE}(default), don't overwrite. If {@link Cml#CML}, should + * camel case the method call. If {@link Cml#NOCML}, don't camel case it. + */ + Cml methodCml() default Cml.SOURCE; + } diff --git a/src/core/lombok/core/handlers/OnstructUtils.java b/src/core/lombok/core/handlers/OnstructUtils.java index fa4254dfbc..eae92e8505 100644 --- a/src/core/lombok/core/handlers/OnstructUtils.java +++ b/src/core/lombok/core/handlers/OnstructUtils.java @@ -1,6 +1,7 @@ package lombok.core.handlers; import lombok.Onstruct; +import lombok.Onstruct.Cml; public class OnstructUtils { @@ -12,9 +13,12 @@ public static String varName(String requestedName, Onstruct instance) { } public static String methodName(String requestedName, Onstruct instance) { - String methodPrefix = instance.methodPre(); - if (methodPrefix == null || methodPrefix.isEmpty()) return requestedName; - return methodPrefix + (instance.methodCml() ? cml(requestedName) : requestedName); + String methodPrefix = instance.source().pre; + if (!instance.methodPre().equals(Onstruct.NULLSTRING)) methodPrefix = instance.methodPre(); + boolean cml = instance.source().cml; + if (instance.methodCml() != Cml.SOURCE) cml = instance.methodCml().cml; + if (methodPrefix == null || methodPrefix.isEmpty()) methodPrefix = ""; + return methodPrefix + (cml ? cml(requestedName) : requestedName); } public static String cml(String name) { diff --git a/test/transform/resource/before/OnstructBook.java b/test/transform/resource/before/OnstructBook.java index 1f2e5f7937..a834852ca6 100644 --- a/test/transform/resource/before/OnstructBook.java +++ b/test/transform/resource/before/OnstructBook.java @@ -3,6 +3,8 @@ import lombok.AllArgsConstructor; import lombok.Onstruct; +import lombok.Onstruct.Cml; +import lombok.Onstruct.SourceType; import lombok.core.PrintAST; import lombok.experimental.Accessors; @@ -23,14 +25,14 @@ public static class Book { void test() { Book mybook = new Book("author0", "bookname0", new Date(), true); @Onstruct - Object author, editionDate = mybook; - @Onstruct(methodPre = "") - Object name = mybook; - @Onstruct(methodPre = "is") - Object purchasable = mybook; + Object author, editionDate = mybook;// var author = mybook.getAuthor() + @Onstruct(source=SourceType.FLUENT) + Object name = mybook;// var name = mybook.name() + @Onstruct(source=SourceType.BOOL) + Object purchasable = mybook;// var purchasable = mybook.isPurchasable() } - void testPrefix() { + void testVarPrefix() { Book mybook = new Book("author0", "bookname0", new Date(), true); @Onstruct(pre = "b_") Object author, editionDate = mybook; @@ -38,4 +40,10 @@ void testPrefix() { Object purchasable = mybook; } + void testOverWriteSourceType() { + Book mybook = new Book("author0", "bookname0", new Date(), true); + @Onstruct(source = SourceType.FLUENT, methodCml = Cml.CML) + Object name=mybook; // var name = mybook.Name() + } + }