Skip to content
This repository was archived by the owner on Aug 11, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions src/main/java/com/squareup/javapoet/CodeBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,133 @@ public Builder endControlFlow(String controlFlow, Object... args) {
return this;
}

/**
* Starts a switch statement. To start a switch expression it should be
* used with {@link #add(String, Object...) add}.
* @param expressionFormat the format of the expression that will be calculated
* by the switch statement. It should not include parentheses, braces or
* newline characters
* @param args the values to be placed instead of the format's placeholders
*/
public Builder beginSwitchStatement(String expressionFormat, Object... args) {
add("switch ("+expressionFormat+") {\n", args);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must not assume where newlines, indenting, tabs, etc. go. The library already has mechanisms for users to specify these.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The already existing methods that implement control flows do the same thing, adding newlines and adjusting the indentation. I tried to be consistent with the codebase and create methods that follow the same logic. If that's a problem would you suggest I simply remove newline characters?

indent();
return this;
}

/**
* Starts a switch statement. To start a switch expression it should be
* used with {@link #add(String, Object...) add}.
* @param codeblock the expression that will be calculated
* by the switch statement. It should not include parentheses, braces or
* newline characters
*/
public Builder beginSwitchStatement(CodeBlock codeBlock) {
return beginSwitchStatement("$L", codeBlock);
}



/**
* Adds a case to a switch statement or expression.
* @param isFirstCase indicates whether the case is the first of the switch
* statement. Necessary to apply proper indentation
* @param valueFormat the value(-s) that the calculated expression will be compared
* against
* @param args the values to be placed instead of the format's placeholders
*/
public Builder addSwitchCase(Boolean isFirstCase, String valueFormat, Object... args) {
// If a previous case already exists, we must unindent
if (!isFirstCase) {
unindent();
}
add("case " + valueFormat + ":\n", args);
indent();
return this;
}

/**
* Adds a case to a switch statement or expression.
* @param isFirstCase indicates whether the case is the first of the switch
* statement. Necessary to apply proper indentation
* @param codeBlock the value(-s) that the calculated expression will be compared
* against
*/
public Builder addSwitchCase(Boolean isFirstCase, CodeBlock codeBlock) {
return addSwitchCase(isFirstCase,"$L", codeBlock);
}

/**
* Adds a case to an extended switch statement or expression.
* @param body the code that needs to be executed if the case is true.
* @param valueFormat the value(-s) that the calculated expression will be compared
* against
* @param args the values to be placed instead of the format's placeholders
*/
public Builder addExtendedSwitchCase(CodeBlock body,
String valueFormat, Object... args) {
String bodySide = body.toString();
// If the body contains multiple lines or expressions it should be
// contained in braces
if (bodySide.lines().count() > 1 || bodySide.split(";").length > 1) {
bodySide = "{ " + bodySide + " }";
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is coming up in the lambda PR too. We need a general way for users to specify how to output lambda-like code bodies. We can't assume how the braces are positioned around the code block.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you suggest I tackle this issue? Would simply removing these lines of code and assuming that the user includes the braces in the code block when necessary resolve the problem?

Copy link
Owner

@Randgalt Randgalt Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I just wrote in the lambda PR, I'd like to see a new builder class shared by both lambda and this PR that is similar to CodeBlock. Users should be able to specify exactly (and in detail) how newlines, indents, etc. are generated.

i.e.

Some people will want:

switch (foo) {
  case X -> { yield 10; }
}

Some will want:

switch (foo) {
  case X -> 
  { yield 10; 
  }
}

Some will want:

switch (foo) {
  case X -> { 
    yield 10; 
  }
}

And every variation inbetween

}
add("case " + valueFormat + " -> " + bodySide + "\n", args);
return this;
}

/**
* Adds a case to an extended switch statement or expression.
* @param body the code that needs to be executed if the case is true.
* @param value the value(-s) that the calculated expression will be compared
* against
*/
public Builder addExtendedSwitchCase(CodeBlock body, CodeBlock value) {
return addExtendedSwitchCase(body, "$L", value);
}


/**
* Adds the default case to a switch statement or expression
*/
public Builder addDefaultCase() {
unindent();
add("default:\n");
indent();
return this;
}

/**
* Adds the default case to an enhanced switch statement or expression
* @param body the code that needs to be executed if none of the cases
* are true.
*/
public Builder addExtendedDefaultCase (CodeBlock body) {
String bodySide = body.toString();
// If the body contains multiple lines or expressions it should be
// contained in braces
if (bodySide.lines().count() > 1 || bodySide.split(";").length > 1) {
bodySide = "{" + bodySide + "}";
}
add("default -> " + bodySide + "\n", args);
return this;

}

/**
* Closes the switch statement or expression
* @param isExtended whether the switch block to close is an extended switch
* block or not. Necessary to implement proper indentation
*/
public Builder endSwitchStatement(Boolean isExtended) {
// unindent needs to be called twice if the switch block isn't an extended
// switch block
if (!isExtended) {
unindent();
}
return endControlFlow();
}

public Builder addStatement(String format, Object... args) {
add("$[");
add(format, args);
Expand Down
103 changes: 103 additions & 0 deletions src/main/java/com/squareup/javapoet/MethodSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,109 @@ public Builder endControlFlow(CodeBlock codeBlock) {
return endControlFlow("$L", codeBlock);
}

/**
* Starts a switch statement. To start a switch expression it should be
* used with {@link #addCode(String, Object...) addCode}.
* @param expressionFormat the format of the expression that will be calculated
* by the switch statement. It should not include parentheses, braces or
* newline characters
* @param args the values to be placed instead of the format's placeholders
*/
public Builder beginSwitchStatement(String expressionFormat, Object... args) {
code.beginSwitchStatement(expressionFormat, args);
return this;
}

/**
* Starts a switch statement. To start a switch expression it should be
* used with {@link #addCode(String, Object...) addCode}.
* @param codeblock the expression that will be calculated
* by the switch statement. It should not include parentheses, braces or
* newline characters
*/
public Builder beginSwitchStatement (CodeBlock codeblock) {
code.beginSwitchStatement(codeblock);
return this;
}

/**
* Adds a case to a switch statement or expression.
* @param isFirstCase indicates whether the case is the first of the switch
* statement. Necessary to apply proper indentation
* @param valueFormat the value that the calculated expression will be compared
* against
* @param args the values to be placed instead of the format's placeholders
*/
public Builder addSwitchCase(Boolean isFirstCase, String valueFormat, Object... args) {
code.addSwitchCase(isFirstCase, valueFormat, args);
return this;
}

/**
* Adds a case to a switch statement or expression.
* @param isFirstCase indicates whether the case is the first of the switch
* statement. Necessary to apply proper indentation
* @param codeblock the value that the calculated expression will be compared
* against
*/
public Builder addSwitchCase(Boolean isFirstCase, CodeBlock codeblock) {
code.addSwitchCase(isFirstCase, codeblock);
return this;
}

/**
* Adds a case to an extended switch statement or expression.
* @param body the code that needs to be executed if the case is true.
* @param valueFormat the value(-s) that the calculated expression will be compared
* against
* @param args the values to be placed instead of the format's placeholders
*/
public Builder addExtendedSwitchCase(CodeBlock body,
String valueFormat, Object... args) {
code.addExtendedSwitchCase(body, valueFormat, args);
return this;

}

/**
* Adds a case to an extended switch statement or expression.
* @param body the code that needs to be executed if the case is true.
* @param value the value(-s) that the calculated expression will be compared
* against
*/
public Builder addExtendedSwitchCase(CodeBlock body, CodeBlock value) {
code.addExtendedSwitchCase(body, value);
return this;
}

/**
* Adds the default case to a switch statement or expression
*/
public Builder addDefaultCase() {
code.addDefaultCase();
return this;
}

/**
* Adds the default case to an enhanced switch statement or expression
* @param body the code that needs to be executed if none of the cases
* are true.
*/
public Builder addExtendedDefaultCase (CodeBlock body) {
code.addExtendedDefaultCase(body);
return this;
}

/**
* Closes the switch statement or expression
* @param isExtended whether the switch block to close is an extended switch
* block or not. Necessary to implement proper indentation
*/
public Builder endSwitchStatement(Boolean isExtended) {
code.endSwitchStatement(isExtended);
return this;
}

public Builder addStatement(String format, Object... args) {
code.addStatement(format, args);
return this;
Expand Down
98 changes: 98 additions & 0 deletions src/test/java/com/squareup/javapoet/MethodSpecTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -486,4 +486,102 @@ private static CodeBlock named(String format, Map<String, ?> args){
return CodeBlock.builder().addNamed(format, args).build();
}

@Test public void ensureSwitchBlockFunctionality() {
CodeBlock cb1 = CodeBlock.builder()
.addStatement("result = $S", "domestic animal")
.addStatement("break")
.build();

CodeBlock cb2 = CodeBlock.builder()
.addStatement("result = $S", "wild animal")
.addStatement("break")
.build();

CodeBlock cb3 = CodeBlock.builder()
.addStatement("result = $S", "unknown animal")
.addStatement("break")
.build();

MethodSpec method = MethodSpec.methodBuilder("method")
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(String.class, "animal").build())
.returns(String.class)
.addStatement("$T result", String.class)
.beginSwitchStatement("animal")
.addSwitchCase(true, "$S", "DOG")
.addSwitchCase(false, "$S", "CAT")
.addCode(cb1)
.addSwitchCase(false, CodeBlock.of("$S", "TIGER"))
.addCode(cb2)
.addDefaultCase()
.addCode(cb3)
.endSwitchStatement(false)
.addStatement("return result")
.build();

assertThat(method.toString()).isEqualTo(
"public java.lang.String method(java.lang.String animal) {\n"
+" java.lang.String result;\n"
+" switch (animal) {\n"
+" case \"DOG\":\n"
+" case \"CAT\":\n"
+" result = \"domestic animal\";\n"
+" break;\n"
+" case \"TIGER\":\n"
+" result = \"wild animal\";\n"
+" break;\n"
+" default:\n"
+" result = \"unknown animal\";\n"
+" break;\n"
+" }\n"
+" return result;\n"
+"}\n"
);
}

@Test public void ensureExtendedSwitchExpressionFunctionality() {
// Assume an enum named month is defined, which contains every month of the year
MethodSpec method = MethodSpec.methodBuilder("method")
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(String.class, "month").build())
.returns(String.class)
.addCode("$T season = ", String.class)
.beginSwitchStatement(CodeBlock.of("month"))
.addExtendedSwitchCase(CodeBlock.of("$S;","WINTER"), "$L, $L, $L", "DECEMBER", "JANUARY", "FEBRUARY")
.addExtendedSwitchCase(CodeBlock.of("$S;","SPRING"), "$L, $L, $L", "MARCH", "APRIL", "MAY")
.addExtendedSwitchCase(CodeBlock.of("$S;","SUMMER"), CodeBlock.of("$L, $L, $L", "JUNE", "JULY", "AUGUST"))
.addExtendedDefaultCase(CodeBlock.of("$S;","AUTUMN"))
.endSwitchStatement(true)
.addStatement("return season")
.build();

assertThat(method.toString()).isEqualTo(
"public java.lang.String method(java.lang.String month) {\n"
+" java.lang.String season = switch (month) {\n"
+" case DECEMBER, JANUARY, FEBRUARY -> \"WINTER\";\n"
+" case MARCH, APRIL, MAY -> \"SPRING\";\n"
+" case JUNE, JULY, AUGUST -> \"SUMMER\";\n"
+" default -> \"AUTUMN\";\n"
+" }\n"
+" return season;\n"
+"}\n"
);
}

@Test public void bracesInExtendedSwitch() {
MethodSpec method = MethodSpec.methodBuilder("method")
.addExtendedSwitchCase(CodeBlock.of("x++; y++;"),"increase")
// Semicolumns ommited deliberately to ensure that multiline codeblocks are included in braces
.addExtendedSwitchCase(CodeBlock.of("x--\n y--"),"decrease")
.build();

assertThat(method.toString()).isEqualTo(
"void method() {\n"
+" case increase -> { x++; y++; }\n"
+" case decrease -> { x--\n"
+" y-- }\n"
+"}\n"
);
}

}