Skip to content

Commit

Permalink
issue #3517
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
glelouet committed Oct 11, 2023
1 parent eab7d44 commit 51195c1
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 13 deletions.
28 changes: 24 additions & 4 deletions src/core/lombok/Onstruct.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@
* Before:
*
* <pre>
* &#064;Onstruct(prefix = "b_")
* Object author, name, editiondate, purchasable = mybook;
* &#064;Onstruct(pre = "b_") Object author, name, editiondate, purchasable = mybook;
* </pre>
*
* After:
Expand All @@ -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;

}
24 changes: 24 additions & 0 deletions src/core/lombok/core/handlers/OnstructUtils.java
Original file line number Diff line number Diff line change
@@ -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);
}

}
12 changes: 10 additions & 2 deletions src/core/lombok/eclipse/handlers/HandleOnstruct.java
Original file line number Diff line number Diff line change
@@ -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<Onstruct> {

public static final HandleOnstruct INSTANCE = new HandleOnstruct();

@Override public void handle(AnnotationValues<Onstruct> 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));
}


Expand Down
132 changes: 132 additions & 0 deletions src/core/lombok/javac/handlers/HandleOnstruct.java
Original file line number Diff line number Diff line change
@@ -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<Onstruct> {


/**
* 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<JavacNode> upFromAnnotationToSameKind(JavacNode node) {
if (node.getKind() != Kind.ANNOTATION) return Collections.emptyList();
JavacNode declaration = node.up();
if (declaration == null) return Collections.emptyList();

List<JavacNode> 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<JCTree> findChildrenStatements(Collection<JavacNode> fields) {
List<JCTree> 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<Onstruct> annotation, JCAnnotation ast, JavacNode annotationNode) {
Collection<JavacNode> annotatedVariables = upFromAnnotationToSameKind(annotationNode);
JavacNode parentNode = annotationNode.up();
Onstruct annotationInstance = annotation.getInstance();
deleteAnnotationIfNeccessary(annotationNode, Onstruct.class);

List<JCTree> 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.<JCExpression>nil(),
methCall,
com.sun.tools.javac.util.List.<JCExpression>nil());
variable.name = varNode.toName(varName);
// System.out.println("replaced with " + variable);
} else
System.err.println(varNode.get());
}

}
20 changes: 13 additions & 7 deletions test/transform/resource/before/OnstructBook.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import lombok.AllArgsConstructor;
import lombok.Onstruct;
import lombok.core.PrintAST;
import lombok.experimental.Accessors;

public class OnstructBook {
Expand All @@ -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);
}

Expand Down

0 comments on commit 51195c1

Please sign in to comment.