From 06c4f80509bcb96117722c4646735823a99c5b27 Mon Sep 17 00:00:00 2001 From: HliasMpGH Date: Sat, 13 Apr 2024 15:22:42 +0300 Subject: [PATCH 1/8] add lambda creation feature *add lambda with body feature *add overloads of lambda creation --- .gitignore | 1 + .../com/squareup/javapoet/MethodSpec.java | 72 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target diff --git a/src/main/java/com/squareup/javapoet/MethodSpec.java b/src/main/java/com/squareup/javapoet/MethodSpec.java index 6914858..7cbb777 100644 --- a/src/main/java/com/squareup/javapoet/MethodSpec.java +++ b/src/main/java/com/squareup/javapoet/MethodSpec.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; @@ -532,6 +533,77 @@ public Builder addStatement(CodeBlock codeBlock) { return this; } + /** + * Structures a lambda expression containing a body, + * and not only an expression. + * @param parameters the input parameters of the function. + * @param body the body of the function. + * Should be used with addCode(), to provide specific behaviour such as + * methodcall((int x, int y) -> {return x + y;}, 5). + */ + public Builder addLambda(List parameters, CodeBlock body) { + // check that the input types are valid + for (ParameterSpec parameter : parameters) { + checkArgument(!parameter.type.equals(TypeName.VOID), + "lambda input parameters cannot be of void type!"); + } + + // the inputs of the lambda (left side) + String inputSide = String.join( + ", ", parameters.stream() + .map(p -> p.toString()) + .collect(Collectors.toList()) + ); + + // the body of the lambda (right side) + String bodySide = body.toString().replaceAll("\n$", ""); + + // the full lambda structure + code.add("(" + inputSide + ") -> {" + bodySide + "}"); + return this; + } + + /** + * Structures a lambda expression containing an expression. + * @param parameters the input parameters of the function. + * @param expressionFromat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + * Should be used with addCode(), to provide specific behaviour such as + * methodcall((int x, int y) -> x + y, 5). + */ + public Builder addLambda(List parameters, String expressionFromat, Object... args) { + addLambda(parameters, CodeBlock.of("return " + expressionFromat + ";", args)); + return this; + } + + /** + * Structures a producer lambda expression containing a body, + * and not only an expression. + * @param body the body of the lambda + * Should be used with addCode(), to provide specific behaviour such as + * methodcall(() -> {return x + y;}, 5). + */ + public Builder addLambda(CodeBlock body) { + addLambda(Collections.emptyList(), body); + return this; + } + + /** + * Structures a producer lambda expression containing an expression. + * @param expressionFromat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + * Should be used with addCode(), to provide specific behaviour such as + * methodcall(() -> x + y, 5). + */ + public Builder addLambda(String expressionFromat, Object... args) { + addLambda(Collections.emptyList(), expressionFromat, args); + return this; + } + public MethodSpec build() { return new MethodSpec(this); } From 130b30de4ae323532f5309d22ad22c788b249bf3 Mon Sep 17 00:00:00 2001 From: HliasMpGH Date: Sun, 14 Apr 2024 18:38:14 +0300 Subject: [PATCH 2/8] add lambda usability for CodeBlocks --- .../java/com/squareup/javapoet/CodeBlock.java | 73 +++++++++++++++++++ .../com/squareup/javapoet/MethodSpec.java | 20 +---- 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/squareup/javapoet/CodeBlock.java b/src/main/java/com/squareup/javapoet/CodeBlock.java index 5376984..471b1f6 100644 --- a/src/main/java/com/squareup/javapoet/CodeBlock.java +++ b/src/main/java/com/squareup/javapoet/CodeBlock.java @@ -21,11 +21,13 @@ import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collector; +import java.util.stream.Collectors; import java.util.stream.StreamSupport; import static com.squareup.javapoet.Util.checkArgument; @@ -415,6 +417,77 @@ public Builder add(CodeBlock codeBlock) { return this; } + /** + * Structures a lambda expression containing a body, + * and not only an expression. + * @param parameters the input parameters of the function. + * @param body the body of the function. + * Should be used with addCode(), to provide specific behaviour such as + * methodcall((int x, int y) -> {return x + y;}, 5). + */ + public Builder addLambda(List parameters, CodeBlock body) { + // check that the input types are valid + for (ParameterSpec parameter : parameters) { + checkArgument(!parameter.type.equals(TypeName.VOID), + "lambda input parameters cannot be of void type!"); + } + + // the inputs of the lambda (left side) + String inputSide = String.join( + ", ", parameters.stream() + .map(p -> p.toString()) + .collect(Collectors.toList()) + ); + + // the body of the lambda (right side) + String bodySide = body.toString().replaceAll("\n$", ""); + + // the full lambda structure + add("(" + inputSide + ") -> {" + bodySide + "}"); + return this; + } + + /** + * Structures a lambda expression containing an expression. + * @param parameters the input parameters of the function. + * @param expressionFromat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + * Should be used with addCode(), to provide specific behaviour such as + * methodcall((int x, int y) -> x + y, 5). + */ + public Builder addLambda(List parameters, String expressionFromat, Object... args) { + addLambda(parameters, CodeBlock.of("return " + expressionFromat + ";", args)); + return this; + } + + /** + * Structures a producer lambda expression containing a body, + * and not only an expression. + * @param body the body of the lambda + * Should be used with addCode(), to provide specific behaviour such as + * methodcall(() -> {return x + y;}, 5). + */ + public Builder addLambda(CodeBlock body) { + addLambda(Collections.emptyList(), body); + return this; + } + + /** + * Structures a producer lambda expression containing an expression. + * @param expressionFromat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + * Should be used with addCode(), to provide specific behaviour such as + * methodcall(() -> x + y, 5). + */ + public Builder addLambda(String expressionFromat, Object... args) { + addLambda(Collections.emptyList(), expressionFromat, args); + return this; + } + public Builder indent() { this.formatParts.add("$>"); return this; diff --git a/src/main/java/com/squareup/javapoet/MethodSpec.java b/src/main/java/com/squareup/javapoet/MethodSpec.java index 7cbb777..53a4d06 100644 --- a/src/main/java/com/squareup/javapoet/MethodSpec.java +++ b/src/main/java/com/squareup/javapoet/MethodSpec.java @@ -35,7 +35,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; @@ -542,24 +541,7 @@ public Builder addStatement(CodeBlock codeBlock) { * methodcall((int x, int y) -> {return x + y;}, 5). */ public Builder addLambda(List parameters, CodeBlock body) { - // check that the input types are valid - for (ParameterSpec parameter : parameters) { - checkArgument(!parameter.type.equals(TypeName.VOID), - "lambda input parameters cannot be of void type!"); - } - - // the inputs of the lambda (left side) - String inputSide = String.join( - ", ", parameters.stream() - .map(p -> p.toString()) - .collect(Collectors.toList()) - ); - - // the body of the lambda (right side) - String bodySide = body.toString().replaceAll("\n$", ""); - - // the full lambda structure - code.add("(" + inputSide + ") -> {" + bodySide + "}"); + code.addLambda(parameters, body); return this; } From b905d139cb6108ab31ca5f7bd85ff01c8d483727 Mon Sep 17 00:00:00 2001 From: HliasMpGH Date: Sat, 13 Apr 2024 15:22:42 +0300 Subject: [PATCH 3/8] add lambda creation feature *add testing *add javadoc documentation --- .gitignore | 2 + pom.xml | 10 +++ .../java/com/squareup/javapoet/CodeBlock.java | 77 ++++++++++++++++++ .../com/squareup/javapoet/MethodSpec.java | 75 ++++++++++++++++-- .../java/com/squareup/javapoet/TypeSpec.java | 2 +- .../com/squareup/javapoet/MethodSpecTest.java | 78 ++++++++++++++++++- 6 files changed, 237 insertions(+), 7 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3df278e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +.vscode diff --git a/pom.xml b/pom.xml index 2343fd7..9a71722 100644 --- a/pom.xml +++ b/pom.xml @@ -135,6 +135,16 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + private + true + + + org.apache.maven.plugins maven-surefire-plugin diff --git a/src/main/java/com/squareup/javapoet/CodeBlock.java b/src/main/java/com/squareup/javapoet/CodeBlock.java index 5376984..37cedc3 100644 --- a/src/main/java/com/squareup/javapoet/CodeBlock.java +++ b/src/main/java/com/squareup/javapoet/CodeBlock.java @@ -21,11 +21,13 @@ import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collector; +import java.util.stream.Collectors; import java.util.stream.StreamSupport; import static com.squareup.javapoet.Util.checkArgument; @@ -415,6 +417,81 @@ public Builder add(CodeBlock codeBlock) { return this; } + /** + * Structures a lambda expression containing a body, + * and not only an expression.
+ * Should be used with {@link #add(String, Object...) add}, + * to provide specific behaviour such as + * methodcall((int x, int y) -> {return x + y;}, 5). + * @param parameters the input parameters of the function. + * @param body the body of the function. + */ + public Builder addLambda(List parameters, CodeBlock body) { + // check that the input types are valid + for (ParameterSpec parameter : parameters) { + checkArgument(!parameter.type.equals(TypeName.VOID), + "lambda input parameters cannot be of void type!"); + } + + // the inputs of the lambda (left side) + String inputSide = String.join( + ", ", parameters.stream() + .map(p -> p.toString()) + .collect(Collectors.toList()) + ); + + // the body of the lambda (right side) + String bodySide = body.toString().replaceAll("\n$", ""); + + // the full lambda structure + add("(" + inputSide + ") -> {" + bodySide + "}"); + return this; + } + + /** + * Structures a lambda expression containing an expression.
+ * Should be used with {@link #add(String, Object...) add}, + * to provide specific behaviour such as + * methodcall((int x, int y) -> {return x + y;}, 5). + * @param parameters the input parameters of the function. + * @param expressionFromat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + */ + public Builder addLambda(List parameters, String expressionFromat, Object... args) { + addLambda(parameters, CodeBlock.of("return " + expressionFromat + ";", args)); + return this; + } + + /** + * Structures a producer lambda expression containing a body, + * and not only an expression.
+ * Should be used with {@link #add(String, Object...) add}, + * to provide specific behaviour such as + * methodcall(() -> {return 5 + 3;}, 5). + * @param body the body of the lambda. + */ + public Builder addLambda(CodeBlock body) { + addLambda(Collections.emptyList(), body); + return this; + } + + /** + * Structures a producer lambda expression containing an expression.
+ * Should be used with {@link #add(String, Object...) add}, + * to provide specific behaviour such as + * methodcall(() -> {return 5 + 3;}, 5). + * @param expressionFromat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + */ + public Builder addLambda(String expressionFromat, Object... args) { + addLambda(Collections.emptyList(), expressionFromat, args); + return this; + } + public Builder indent() { this.formatParts.add("$>"); return this; diff --git a/src/main/java/com/squareup/javapoet/MethodSpec.java b/src/main/java/com/squareup/javapoet/MethodSpec.java index 6914858..dd8e325 100644 --- a/src/main/java/com/squareup/javapoet/MethodSpec.java +++ b/src/main/java/com/squareup/javapoet/MethodSpec.java @@ -296,21 +296,25 @@ public static final class Builder { private String name; private final CodeBlock.Builder javadoc = CodeBlock.builder(); + public final List annotations = new ArrayList<>(); + public final List modifiers = new ArrayList<>(); + public final List typeVariables = new ArrayList<>(); private TypeName returnType; + public final List parameters = new ArrayList<>(); + private boolean varargs; private final Set exceptions = new LinkedHashSet<>(); private final CodeBlock.Builder code = CodeBlock.builder(); - private boolean varargs; private CodeBlock defaultValue; - public final List typeVariables = new ArrayList<>(); - public final List annotations = new ArrayList<>(); - public final List modifiers = new ArrayList<>(); - public final List parameters = new ArrayList<>(); private Builder(String name) { setName(name); } + /** + * Sets a name for this builder. + * @param name the name to be set for the method. + */ public Builder setName(String name) { checkNotNull(name, "name == null"); checkArgument(name.equals(CONSTRUCTOR) || SourceVersion.isName(name), @@ -500,6 +504,10 @@ public Builder nextControlFlow(CodeBlock codeBlock) { return nextControlFlow("$L", codeBlock); } + /** + * Ends the last open control flow. + * Should be used once for every control flow. + */ public Builder endControlFlow() { code.endControlFlow(); return this; @@ -532,6 +540,63 @@ public Builder addStatement(CodeBlock codeBlock) { return this; } + /** + * Structures a lambda expression containing a body, + * and not only an expression.
+ * Should be used with {@link #addCode(String, Object...) addCode}, + * to provide specific behaviour such as + * methodcall((int x, int y) -> {return x + y;}, 5). + * @param parameters the input parameters of the function. + * @param body the body of the function. + */ + public Builder addLambda(List parameters, CodeBlock body) { + code.addLambda(parameters, body); + return this; + } + + /** + * Structures a lambda expression containing an expression.
+ * Should be used with {@link #addCode(String, Object...) addCode}, + * to provide specific behaviour such as + * methodcall((int x, int y) -> {return x + y;}, 5). + * @param parameters the input parameters of the function. + * @param expressionFromat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + */ + public Builder addLambda(List parameters, String expressionFromat, Object... args) { + addLambda(parameters, CodeBlock.of("return " + expressionFromat + ";", args)); + return this; + } + + /** + * Structures a producer lambda expression containing a body, + * and not only an expression.
+ * Should be used with {@link #addCode(String, Object...) addCode}, + * to provide specific behaviour such as + * methodcall(() -> {return 3 + 2;}, 5). + * @param body the body of the lambda. + */ + public Builder addLambda(CodeBlock body) { + addLambda(Collections.emptyList(), body); + return this; + } + + /** + * Structures a producer lambda expression containing an expression.
+ * Should be used with {@link #addCode(String, Object...) addCode}, to provide specific behaviour such as + * methodcall(() -> {return 3 + 2;}, 5). + * @param expressionFromat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + */ + public Builder addLambda(String expressionFromat, Object... args) { + addLambda(Collections.emptyList(), expressionFromat, args); + return this; + } + public MethodSpec build() { return new MethodSpec(this); } diff --git a/src/main/java/com/squareup/javapoet/TypeSpec.java b/src/main/java/com/squareup/javapoet/TypeSpec.java index 9befa29..f717be0 100644 --- a/src/main/java/com/squareup/javapoet/TypeSpec.java +++ b/src/main/java/com/squareup/javapoet/TypeSpec.java @@ -609,7 +609,7 @@ public Builder addSuperinterface(Type superinterface, boolean avoidNestedTypeNam public Builder addPermits(Iterable permits) { checkArgument(permits != null, "permits == null"); for (TypeName permit : permits) { - addPermits(permits); + addPermits(permit); } return this; } diff --git a/src/test/java/com/squareup/javapoet/MethodSpecTest.java b/src/test/java/com/squareup/javapoet/MethodSpecTest.java index 56cc3b7..6b67bc5 100644 --- a/src/test/java/com/squareup/javapoet/MethodSpecTest.java +++ b/src/test/java/com/squareup/javapoet/MethodSpecTest.java @@ -482,8 +482,84 @@ abstract static class AbstractClassWithPrivateAnnotation { "}\n"); } - private static CodeBlock named(String format, Map args){ + private static CodeBlock named(String format, Map args) { return CodeBlock.builder().addNamed(format, args).build(); } + @Test public void ensureLambdaTypeError() { + // parameter with void type - should be rejected + ParameterSpec p1 = ParameterSpec.builder(TypeName.VOID, "x").build(); + + try { + // check that void type will cause errors + CodeBlock.builder() + .addLambda(List.of(p1), "$N * 2", "x"); + + fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().isEqualTo( + "lambda input parameters cannot be of void type!" + ); + } + } + + @Test public void ensureLambdaMethodSpecLiability() { + // lambda inputs + ParameterSpec p1 = ParameterSpec.builder(TypeName.INT, "x").build(); + ParameterSpec p2 = ParameterSpec.builder(TypeName.DOUBLE, "y").build(); + + // lambda body that considers input values + CodeBlock body1 = CodeBlock.of("int $3N = 3; return $1N + $2N + $3N;", "x", "y", "z"); + + // lambda body that does not consider input values + CodeBlock body2 = CodeBlock.of("int $1N = 3; int $2N = 5; return $1N + $2N;", "x", "y"); + + MethodSpec method = MethodSpec.methodBuilder("method") + .addCode("methodCall(") + .addLambda(List.of(p1, p2), body1) // lambda with multiple inputs and (CodeBlock) body + .addCode(", ") + .addLambda(List.of(p2), + "$N + $N", "x", "y") // lambda with single input and (String) body + .addCode(", ") + .addLambda(body2) // lambda with no inputs and (CodeBlock) body + .addCode(", ") + .addLambda("5 + 7") // lambda with no inputs and (String) body + .addCode(");\n") + .build(); + + assertThat(method.toString()).isEqualTo( + "void method() {\n" + + " methodCall(" + + "(int x, double y) -> {int z = 3; return x + y + z;}, " + + "(double y) -> {return x + y;}, " + + "() -> {int x = 3; int y = 5; return x + y;}, " + + "() -> {return 5 + 7;}" + + ");\n" + + "}\n" + ); + } + + @Test public void ensureLambdaCodeBlockLiability() { + // lambda body that does not consider input values + CodeBlock body = CodeBlock.of("int $1N = 3; int $2N = 5; return $1N + $2N;", "x", "y"); + + CodeBlock codeWithLambda = CodeBlock.builder() + .add("methodCall(") + .addLambda(body) // producer lambda + .add(");") + .build(); + + MethodSpec method = MethodSpec.methodBuilder("method") + .addCode(codeWithLambda) + .build(); + + assertThat(method.toString()).isEqualTo( + "void method() {\n" + + " methodCall(" + + "() -> {int x = 3; int y = 5; return x + y;}" + + ");\n" + + "}\n" + ); + } + } From beb18efee98e2e3f78fae5043b1fcd6cb8dda437 Mon Sep 17 00:00:00 2001 From: HliasMpGH Date: Tue, 30 Apr 2024 20:57:15 +0300 Subject: [PATCH 4/8] nit: fix infinite recursion bug on method call --- src/main/java/com/squareup/javapoet/TypeSpec.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/squareup/javapoet/TypeSpec.java b/src/main/java/com/squareup/javapoet/TypeSpec.java index 44d9bc9..2296129 100644 --- a/src/main/java/com/squareup/javapoet/TypeSpec.java +++ b/src/main/java/com/squareup/javapoet/TypeSpec.java @@ -610,7 +610,7 @@ public Builder addSuperinterface(Type superinterface, boolean avoidNestedTypeNam public Builder addPermits(Iterable permits) { checkArgument(permits != null, "permits == null"); for (TypeName permit : permits) { - addPermits(permits); + addPermits(permit); } return this; } From 49534e2216f476808afd90c31649517b77571887 Mon Sep 17 00:00:00 2001 From: HliasMpGH Date: Tue, 30 Apr 2024 20:59:09 +0300 Subject: [PATCH 5/8] add javadoc generation plugin on build --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index 2343fd7..9a71722 100644 --- a/pom.xml +++ b/pom.xml @@ -135,6 +135,16 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + private + true + + + org.apache.maven.plugins maven-surefire-plugin From 46d52115d35d57284c61b203c7cc796294ea2ac5 Mon Sep 17 00:00:00 2001 From: HliasMpGH Date: Tue, 30 Apr 2024 21:12:16 +0300 Subject: [PATCH 6/8] fix typos on parameter names --- src/main/java/com/squareup/javapoet/CodeBlock.java | 12 ++++++------ src/main/java/com/squareup/javapoet/MethodSpec.java | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/squareup/javapoet/CodeBlock.java b/src/main/java/com/squareup/javapoet/CodeBlock.java index 37cedc3..df81833 100644 --- a/src/main/java/com/squareup/javapoet/CodeBlock.java +++ b/src/main/java/com/squareup/javapoet/CodeBlock.java @@ -454,13 +454,13 @@ public Builder addLambda(List parameters, CodeBlock body) { * to provide specific behaviour such as * methodcall((int x, int y) -> {return x + y;}, 5). * @param parameters the input parameters of the function. - * @param expressionFromat the format that should be used + * @param expressionFormat the format that should be used * for the expression. * @param args the values that should be placed in the holders * of the format. */ - public Builder addLambda(List parameters, String expressionFromat, Object... args) { - addLambda(parameters, CodeBlock.of("return " + expressionFromat + ";", args)); + public Builder addLambda(List parameters, String expressionFormat, Object... args) { + addLambda(parameters, CodeBlock.of("return " + expressionFormat + ";", args)); return this; } @@ -482,13 +482,13 @@ public Builder addLambda(CodeBlock body) { * Should be used with {@link #add(String, Object...) add}, * to provide specific behaviour such as * methodcall(() -> {return 5 + 3;}, 5). - * @param expressionFromat the format that should be used + * @param expressionFormat the format that should be used * for the expression. * @param args the values that should be placed in the holders * of the format. */ - public Builder addLambda(String expressionFromat, Object... args) { - addLambda(Collections.emptyList(), expressionFromat, args); + public Builder addLambda(String expressionFormat, Object... args) { + addLambda(Collections.emptyList(), expressionFormat, args); return this; } diff --git a/src/main/java/com/squareup/javapoet/MethodSpec.java b/src/main/java/com/squareup/javapoet/MethodSpec.java index dd8e325..33fcec0 100644 --- a/src/main/java/com/squareup/javapoet/MethodSpec.java +++ b/src/main/java/com/squareup/javapoet/MethodSpec.java @@ -560,13 +560,13 @@ public Builder addLambda(List parameters, CodeBlock body) { * to provide specific behaviour such as * methodcall((int x, int y) -> {return x + y;}, 5). * @param parameters the input parameters of the function. - * @param expressionFromat the format that should be used + * @param expressionFormat the format that should be used * for the expression. * @param args the values that should be placed in the holders * of the format. */ - public Builder addLambda(List parameters, String expressionFromat, Object... args) { - addLambda(parameters, CodeBlock.of("return " + expressionFromat + ";", args)); + public Builder addLambda(List parameters, String expressionFormat, Object... args) { + addLambda(parameters, CodeBlock.of("return " + expressionFormat + ";", args)); return this; } @@ -587,13 +587,13 @@ public Builder addLambda(CodeBlock body) { * Structures a producer lambda expression containing an expression.
* Should be used with {@link #addCode(String, Object...) addCode}, to provide specific behaviour such as * methodcall(() -> {return 3 + 2;}, 5). - * @param expressionFromat the format that should be used + * @param expressionFormat the format that should be used * for the expression. * @param args the values that should be placed in the holders * of the format. */ - public Builder addLambda(String expressionFromat, Object... args) { - addLambda(Collections.emptyList(), expressionFromat, args); + public Builder addLambda(String expressionFormat, Object... args) { + addLambda(Collections.emptyList(), expressionFormat, args); return this; } From 71afbec0547ac773657fade34e10bd76039609ed Mon Sep 17 00:00:00 2001 From: HliasMpGH Date: Tue, 30 Apr 2024 21:17:23 +0300 Subject: [PATCH 7/8] remove ! from exception message --- src/main/java/com/squareup/javapoet/CodeBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/squareup/javapoet/CodeBlock.java b/src/main/java/com/squareup/javapoet/CodeBlock.java index df81833..85a54df 100644 --- a/src/main/java/com/squareup/javapoet/CodeBlock.java +++ b/src/main/java/com/squareup/javapoet/CodeBlock.java @@ -430,7 +430,7 @@ public Builder addLambda(List parameters, CodeBlock body) { // check that the input types are valid for (ParameterSpec parameter : parameters) { checkArgument(!parameter.type.equals(TypeName.VOID), - "lambda input parameters cannot be of void type!"); + "lambda input parameters cannot be of void type"); } // the inputs of the lambda (left side) From 4e2357e1173816dbe2d3f3521b5b6ce92ec10ab9 Mon Sep 17 00:00:00 2001 From: HliasMpGH Date: Thu, 9 May 2024 18:50:11 +0300 Subject: [PATCH 8/8] improve lambda feature --- .../java/com/squareup/javapoet/CodeBlock.java | 104 +++++++++++++----- .../com/squareup/javapoet/MethodSpec.java | 69 +++++++++--- .../com/squareup/javapoet/MethodSpecTest.java | 22 +++- 3 files changed, 144 insertions(+), 51 deletions(-) diff --git a/src/main/java/com/squareup/javapoet/CodeBlock.java b/src/main/java/com/squareup/javapoet/CodeBlock.java index 85a54df..4d4ae6d 100644 --- a/src/main/java/com/squareup/javapoet/CodeBlock.java +++ b/src/main/java/com/squareup/javapoet/CodeBlock.java @@ -28,6 +28,7 @@ import java.util.regex.Pattern; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.stream.StreamSupport; import static com.squareup.javapoet.Util.checkArgument; @@ -418,78 +419,123 @@ public Builder add(CodeBlock codeBlock) { } /** - * Structures a lambda expression containing a body, - * and not only an expression.
- * Should be used with {@link #add(String, Object...) add}, + * Structures a lambda function based on some inputs and a CodeBlock body.
+ * Should be used with {@link #add(String, Object...) addCode}, * to provide specific behaviour such as - * methodcall((int x, int y) -> {return x + y;}, 5). + * methodcall((int x, int y) -> x + y, 5). * @param parameters the input parameters of the function. + * @param emitTypes true if the types of the inputs should + * be emitted on the result. * @param body the body of the function. */ - public Builder addLambda(List parameters, CodeBlock body) { + public Builder addLambda(Iterable parameters, boolean emitTypes, CodeBlock body) { + int paramsLen = 0; // check that the input types are valid for (ParameterSpec parameter : parameters) { checkArgument(!parameter.type.equals(TypeName.VOID), "lambda input parameters cannot be of void type"); + paramsLen++; } - // the inputs of the lambda (left side) - String inputSide = String.join( - ", ", parameters.stream() - .map(p -> p.toString()) - .collect(Collectors.toList()) + Stream parameterStream = StreamSupport.stream( + parameters.spliterator(), + false ); + // the inputs of the lambda (left side) + String inputSide = parameterStream + .map(p -> emitTypes ? p.toString() : p.name) + .collect(Collectors.joining(", ")); + + // on 0 or more than 1 inputs, or in case of type emission, + // parentheses are mandatory + if (paramsLen == 0 || paramsLen > 1 || emitTypes) { + inputSide = "(" + inputSide + ")"; + } + // the body of the lambda (right side) - String bodySide = body.toString().replaceAll("\n$", ""); + String bodySide = body.toString().replaceAll("\n", ""); + + // in case of multiple statements, braces are mandatory + if (bodySide.contains(";")) { + bodySide = "{" + bodySide + "}"; + } // the full lambda structure - add("(" + inputSide + ") -> {" + bodySide + "}"); + add(inputSide + " -> " + bodySide); return this; } /** - * Structures a lambda expression containing an expression.
- * Should be used with {@link #add(String, Object...) add}, + * Structures a lambda function based on some inputs and a CodeBlock body.
+ * Will not emit the input types.
+ * Should be used with {@link #add(String, Object...) addCode}, + * to provide specific behaviour such as + * methodcall((x, y) -> x + y, 5). + * @param parameters the input parameters of the function. + * @param body the body of the function. + */ + public Builder addLambda(Iterable parameters, CodeBlock body) { + return addLambda(parameters, false, body); + } + + /** + * Structures a lambda function based on some inputs and an expression body.
+ * Should be used with {@link #add(String, Object...) addCode}, * to provide specific behaviour such as - * methodcall((int x, int y) -> {return x + y;}, 5). + * methodcall((int x, int y) -> x + y, 5). * @param parameters the input parameters of the function. + * @param emitTypes true if the types of the inputs should + * be emitted on the result. * @param expressionFormat the format that should be used * for the expression. * @param args the values that should be placed in the holders * of the format. */ - public Builder addLambda(List parameters, String expressionFormat, Object... args) { - addLambda(parameters, CodeBlock.of("return " + expressionFormat + ";", args)); - return this; + public Builder addLambda(Iterable parameters, boolean emitTypes, + String expressionFormat, Object... args) { + return addLambda(parameters, emitTypes, CodeBlock.of(expressionFormat, args)); } /** - * Structures a producer lambda expression containing a body, - * and not only an expression.
- * Should be used with {@link #add(String, Object...) add}, + * Structures a lambda function based on some inputs and an expression body.
+ * Will not emit the input types.
+ * Should be used with {@link #add(String, Object...) addCode}, * to provide specific behaviour such as - * methodcall(() -> {return 5 + 3;}, 5). + * methodcall((x, y) -> x + y, 5). + * @param parameters the input parameters of the function. + * @param expressionFormat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + */ + public Builder addLambda(Iterable parameters, String expressionFormat, Object... args) { + return addLambda(parameters, false, CodeBlock.of(expressionFormat, args)); + } + + /** + * Structures a producer lambda function based on a CodeBlock body.
+ * Should be used with {@link #add(String, Object...) addCode}, + * to provide specific behaviour such as + * methodcall(() -> 3 + 2, 5). * @param body the body of the lambda. */ public Builder addLambda(CodeBlock body) { - addLambda(Collections.emptyList(), body); - return this; + return addLambda(Collections.emptyList(), false, body); } /** - * Structures a producer lambda expression containing an expression.
- * Should be used with {@link #add(String, Object...) add}, + * Structures a producer lambda function based on an expression body.
+ * Should be used with {@link #add(String, Object...) addCode}, * to provide specific behaviour such as - * methodcall(() -> {return 5 + 3;}, 5). + * methodcall(() -> 3 + 2, 5). * @param expressionFormat the format that should be used * for the expression. * @param args the values that should be placed in the holders * of the format. */ public Builder addLambda(String expressionFormat, Object... args) { - addLambda(Collections.emptyList(), expressionFormat, args); - return this; + return addLambda(Collections.emptyList(), false, expressionFormat, args); } public Builder indent() { diff --git a/src/main/java/com/squareup/javapoet/MethodSpec.java b/src/main/java/com/squareup/javapoet/MethodSpec.java index 33fcec0..a738a9a 100644 --- a/src/main/java/com/squareup/javapoet/MethodSpec.java +++ b/src/main/java/com/squareup/javapoet/MethodSpec.java @@ -541,59 +541,94 @@ public Builder addStatement(CodeBlock codeBlock) { } /** - * Structures a lambda expression containing a body, - * and not only an expression.
+ * Structures a lambda function based on some inputs and a CodeBlock body.
* Should be used with {@link #addCode(String, Object...) addCode}, * to provide specific behaviour such as - * methodcall((int x, int y) -> {return x + y;}, 5). + * methodcall((int x, int y) -> x + y, 5). * @param parameters the input parameters of the function. + * @param emitTypes true if the types of the inputs should + * be emitted on the result. * @param body the body of the function. */ - public Builder addLambda(List parameters, CodeBlock body) { - code.addLambda(parameters, body); + public Builder addLambda(Iterable parameters, boolean emitTypes, CodeBlock body) { + code.addLambda(parameters, emitTypes, body); return this; } /** - * Structures a lambda expression containing an expression.
+ * Structures a lambda function based on some inputs and a CodeBlock body.
+ * Will not emit the input types.
* Should be used with {@link #addCode(String, Object...) addCode}, * to provide specific behaviour such as - * methodcall((int x, int y) -> {return x + y;}, 5). + * methodcall((x, y) -> x + y, 5). * @param parameters the input parameters of the function. + * @param body the body of the function. + */ + public Builder addLambda(Iterable parameters, CodeBlock body) { + code.addLambda(parameters, false, body); + return this; + } + + /** + * Structures a lambda function based on some inputs and an expression body.
+ * Should be used with {@link #addCode(String, Object...) addCode}, + * to provide specific behaviour such as + * methodcall((int x, int y) -> x + y, 5). + * @param parameters the input parameters of the function. + * @param emitTypes true if the types of the inputs should + * be emitted on the result. * @param expressionFormat the format that should be used * for the expression. * @param args the values that should be placed in the holders * of the format. */ - public Builder addLambda(List parameters, String expressionFormat, Object... args) { - addLambda(parameters, CodeBlock.of("return " + expressionFormat + ";", args)); + public Builder addLambda(Iterable parameters, boolean emitTypes, + String expressionFormat, Object... args) { + code.addLambda(parameters, emitTypes, CodeBlock.of(expressionFormat, args)); return this; } /** - * Structures a producer lambda expression containing a body, - * and not only an expression.
+ * Structures a lambda function based on some inputs and an expression body.
+ * Will not emit the input types.
* Should be used with {@link #addCode(String, Object...) addCode}, * to provide specific behaviour such as - * methodcall(() -> {return 3 + 2;}, 5). + * methodcall((x, y) -> x + y, 5). + * @param parameters the input parameters of the function. + * @param expressionFormat the format that should be used + * for the expression. + * @param args the values that should be placed in the holders + * of the format. + */ + public Builder addLambda(Iterable parameters, String expressionFormat, Object... args) { + code.addLambda(parameters, false, CodeBlock.of(expressionFormat, args)); + return this; + } + + /** + * Structures a producer lambda function based on a CodeBlock body.
+ * Should be used with {@link #addCode(String, Object...) addCode}, + * to provide specific behaviour such as + * methodcall(() -> 3 + 2, 5). * @param body the body of the lambda. */ public Builder addLambda(CodeBlock body) { - addLambda(Collections.emptyList(), body); + code.addLambda(Collections.emptyList(), false, body); return this; } /** - * Structures a producer lambda expression containing an expression.
- * Should be used with {@link #addCode(String, Object...) addCode}, to provide specific behaviour such as - * methodcall(() -> {return 3 + 2;}, 5). + * Structures a producer lambda function based on an expression body.
+ * Should be used with {@link #addCode(String, Object...) addCode}, + * to provide specific behaviour such as + * methodcall(() -> 3 + 2, 5). * @param expressionFormat the format that should be used * for the expression. * @param args the values that should be placed in the holders * of the format. */ public Builder addLambda(String expressionFormat, Object... args) { - addLambda(Collections.emptyList(), expressionFormat, args); + code.addLambda(Collections.emptyList(), false, expressionFormat, args); return this; } diff --git a/src/test/java/com/squareup/javapoet/MethodSpecTest.java b/src/test/java/com/squareup/javapoet/MethodSpecTest.java index 6b67bc5..7611eb4 100644 --- a/src/test/java/com/squareup/javapoet/MethodSpecTest.java +++ b/src/test/java/com/squareup/javapoet/MethodSpecTest.java @@ -498,7 +498,7 @@ private static CodeBlock named(String format, Map args) { fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo( - "lambda input parameters cannot be of void type!" + "lambda input parameters cannot be of void type" ); } } @@ -516,7 +516,7 @@ private static CodeBlock named(String format, Map args) { MethodSpec method = MethodSpec.methodBuilder("method") .addCode("methodCall(") - .addLambda(List.of(p1, p2), body1) // lambda with multiple inputs and (CodeBlock) body + .addLambda(List.of(p1, p2), true, body1) // lambda with multiple inputs and (CodeBlock) body .addCode(", ") .addLambda(List.of(p2), "$N + $N", "x", "y") // lambda with single input and (String) body @@ -524,6 +524,10 @@ private static CodeBlock named(String format, Map args) { .addLambda(body2) // lambda with no inputs and (CodeBlock) body .addCode(", ") .addLambda("5 + 7") // lambda with no inputs and (String) body + .addCode(", ") + .addLambda("method1(); method2();") // lambda with multiple statements + .addCode(", ") + .addLambda(List.of(p1), true, "x + 5") // lambda with single input of emitted type .addCode(");\n") .build(); @@ -531,9 +535,11 @@ private static CodeBlock named(String format, Map args) { "void method() {\n" + " methodCall(" + "(int x, double y) -> {int z = 3; return x + y + z;}, " + - "(double y) -> {return x + y;}, " + + "y -> x + y, " + "() -> {int x = 3; int y = 5; return x + y;}, " + - "() -> {return 5 + 7;}" + + "() -> 5 + 7, " + + "() -> {method1(); method2();}, " + + "(int x) -> x + 5" + ");\n" + "}\n" ); @@ -543,9 +549,14 @@ private static CodeBlock named(String format, Map args) { // lambda body that does not consider input values CodeBlock body = CodeBlock.of("int $1N = 3; int $2N = 5; return $1N + $2N;", "x", "y"); + // lambda expression that does not consider input values + CodeBlock body2 = CodeBlock.of("5 + 3"); + CodeBlock codeWithLambda = CodeBlock.builder() .add("methodCall(") .addLambda(body) // producer lambda + .add(", ") + .addLambda(body2) .add(");") .build(); @@ -556,7 +567,8 @@ private static CodeBlock named(String format, Map args) { assertThat(method.toString()).isEqualTo( "void method() {\n" + " methodCall(" + - "() -> {int x = 3; int y = 5; return x + y;}" + + "() -> {int x = 3; int y = 5; return x + y;}, " + + "() -> 5 + 3" + ");\n" + "}\n" );