[java] Update Tests for JEP 459 String Templates
TemplateFragment now has an attribute "content".
This commit is contained in:
@ -1,4 +1,6 @@
|
||||
/**
|
||||
* Support "JEP 459: String Templates (Second Preview)" (Java 22)
|
||||
* Use ASTTemplate.setContent instead of setImage.
|
||||
* Remove support for Record pattern in enhanced for statements. This was only a Java 20 Preview feature.
|
||||
* Remove support for ParenthesizedPatterns. This was only a Java 20 Preview feature.
|
||||
* Andreas Dangel 02/2024
|
||||
@ -1053,7 +1055,7 @@ TOKEN :
|
||||
// In order to produce the correct token sequence, the ambiguity needs to be resolved using the context.
|
||||
// That means, that STRING_TEMPLATE_MID/END and TEXT_BLOCK_TEMPLATE_MID/END could actually be a closing bracket ("}").
|
||||
// Additionally, a STRING_TEMPLATE_MID could be a TEXT_BLOCK_TEMPLATE_MID and the other way round.
|
||||
// See JLS 3.13 Fragments (Java 21 Preview)
|
||||
// See JLS 3.13 Fragments (Java 21 Preview and Java 22 Preview)
|
||||
|
||||
TOKEN :
|
||||
{
|
||||
@ -2336,10 +2338,10 @@ void StringTemplate() #void :
|
||||
{
|
||||
{ tokenContexts.push(TokenContext.STRING_TEMPLATE); }
|
||||
|
||||
<STRING_TEMPLATE_BEGIN> { setLastTokenImage(jjtThis); } #TemplateFragment
|
||||
<STRING_TEMPLATE_BEGIN> { jjtThis.setContent(getToken(0).getImage()); } #TemplateFragment
|
||||
EmbeddedExpression()
|
||||
( <STRING_TEMPLATE_MID> { setLastTokenImage(jjtThis); } #TemplateFragment EmbeddedExpression() )*
|
||||
<STRING_TEMPLATE_END> { setLastTokenImage(jjtThis); } #TemplateFragment
|
||||
( <STRING_TEMPLATE_MID> { jjtThis.setContent(getToken(0).getImage()); } #TemplateFragment EmbeddedExpression() )*
|
||||
<STRING_TEMPLATE_END> { jjtThis.setContent(getToken(0).getImage()); } #TemplateFragment
|
||||
|
||||
{ tokenContexts.pop(); }
|
||||
}
|
||||
@ -2349,10 +2351,10 @@ void TextBlockTemplate() #void :
|
||||
{
|
||||
{ tokenContexts.push(TokenContext.TEXT_BLOCK_TEMPLATE); }
|
||||
|
||||
<TEXT_BLOCK_TEMPLATE_BEGIN> { setLastTokenImage(jjtThis); } #TemplateFragment
|
||||
<TEXT_BLOCK_TEMPLATE_BEGIN> { jjtThis.setContent(getToken(0).getImage()); } #TemplateFragment
|
||||
EmbeddedExpression()
|
||||
( <TEXT_BLOCK_TEMPLATE_MID> { setLastTokenImage(jjtThis); } #TemplateFragment EmbeddedExpression() )*
|
||||
<TEXT_BLOCK_TEMPLATE_END> { setLastTokenImage(jjtThis); } #TemplateFragment
|
||||
( <TEXT_BLOCK_TEMPLATE_MID> { jjtThis.setContent(getToken(0).getImage()); } #TemplateFragment EmbeddedExpression() )*
|
||||
<TEXT_BLOCK_TEMPLATE_END> { jjtThis.setContent(getToken(0).getImage()); } #TemplateFragment
|
||||
|
||||
{ tokenContexts.pop(); }
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
|
||||
/**
|
||||
* This is a Java 21 Preview feature.
|
||||
* This is a Java 21/22 Preview feature.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
@ -15,9 +15,10 @@ import net.sourceforge.pmd.annotation.Experimental;
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="https://openjdk.org/jeps/430">JEP 430: String Templates (Preview)</a>
|
||||
* @see <a href="https://openjdk.org/jeps/430">JEP 430: String Templates (Preview)</a> (Java 21)
|
||||
* @see <a href="https://openjdk.org/jeps/459">JEP 459: String Templates (Second Preview)</a> (Java 22)
|
||||
*/
|
||||
@Experimental
|
||||
@Experimental("String templates is a Java 21/22 Preview feature")
|
||||
public final class ASTTemplate extends AbstractJavaNode {
|
||||
ASTTemplate(int i) {
|
||||
super(i);
|
||||
|
@ -8,7 +8,7 @@ import net.sourceforge.pmd.annotation.Experimental;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
|
||||
|
||||
/**
|
||||
* This is a Java 21 Preview feature.
|
||||
* This is a Java 21/22 Preview feature.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
@ -17,9 +17,10 @@ import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="https://openjdk.org/jeps/430">JEP 430: String Templates (Preview)</a>
|
||||
* @see <a href="https://openjdk.org/jeps/430">JEP 430: String Templates (Preview)</a> (Java 21)
|
||||
* @see <a href="https://openjdk.org/jeps/459">JEP 459: String Templates (Second Preview)</a> (Java 22)
|
||||
*/
|
||||
@Experimental
|
||||
@Experimental("String templates is a Java 21/22 Preview feature")
|
||||
public final class ASTTemplateExpression extends AbstractJavaExpr {
|
||||
ASTTemplateExpression(int i) {
|
||||
super(i);
|
||||
|
@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.java.ast;
|
||||
import net.sourceforge.pmd.annotation.Experimental;
|
||||
|
||||
/**
|
||||
* This is a Java 21 Preview feature.
|
||||
* This is a Java 21/22 Preview feature.
|
||||
*
|
||||
* <pre class="grammar">
|
||||
*
|
||||
@ -16,10 +16,13 @@ import net.sourceforge.pmd.annotation.Experimental;
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="https://openjdk.org/jeps/430">JEP 430: String Templates (Preview)</a>
|
||||
* @see <a href="https://openjdk.org/jeps/430">JEP 430: String Templates (Preview)</a> (Java 21)
|
||||
* @see <a href="https://openjdk.org/jeps/459">JEP 459: String Templates (Second Preview)</a> (Java 22)
|
||||
*/
|
||||
@Experimental
|
||||
@Experimental("String templates is a Java 21/22 Preview feature")
|
||||
public final class ASTTemplateFragment extends AbstractJavaNode {
|
||||
private String content;
|
||||
|
||||
ASTTemplateFragment(int i) {
|
||||
super(i);
|
||||
}
|
||||
@ -28,4 +31,13 @@ public final class ASTTemplateFragment extends AbstractJavaNode {
|
||||
protected <P, R> R acceptVisitor(JavaVisitor<? super P, ? extends R> visitor, P data) {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -125,20 +125,21 @@ public class LanguageLevelChecker<T> {
|
||||
/**
|
||||
* String Templates.
|
||||
* @see <a href="https://openjdk.org/jeps/430">JEP 430: String Templates (Preview)</a> (Java 21)
|
||||
* @see <a href="https://openjdk.org/jeps/459">JEP 459: String Templates (Second Preview)</a> (Java 22)
|
||||
*/
|
||||
STRING_TEMPLATES(21, 21, false),
|
||||
STRING_TEMPLATES(21, 22, false),
|
||||
|
||||
/**
|
||||
* Unnamed patterns and variables.
|
||||
* @see <a href="https://openjdk.org/jeps/443">JEP 443: Unnamed patterns and variables (Preview)</a> (Java 21)
|
||||
*/
|
||||
UNNAMED_PATTERNS_AND_VARIABLES(21, 21, false),
|
||||
UNNAMED_PATTERNS_AND_VARIABLES(21, 22, false),
|
||||
|
||||
/**
|
||||
* Unnamed Classes and Instance Main Methods
|
||||
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a> (Java 21)
|
||||
*/
|
||||
UNNAMED_CLASSES(21, 21, false),
|
||||
UNNAMED_CLASSES(21, 22, false),
|
||||
|
||||
; // SUPPRESS CHECKSTYLE enum trailing semi is awesome
|
||||
|
||||
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.ParseException;
|
||||
import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper;
|
||||
import net.sourceforge.pmd.lang.java.BaseJavaTreeDumpTest;
|
||||
import net.sourceforge.pmd.lang.java.JavaParsingHelper;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
|
||||
|
||||
class Java22PreviewTreeDumpTest extends BaseJavaTreeDumpTest {
|
||||
private final JavaParsingHelper java22p =
|
||||
JavaParsingHelper.DEFAULT.withDefaultVersion("22-preview")
|
||||
.withResourceContext(Java22PreviewTreeDumpTest.class, "jdkversiontests/java22p/");
|
||||
private final JavaParsingHelper java22 = java22p.withDefaultVersion("22");
|
||||
|
||||
@Override
|
||||
public BaseParsingHelper<?, ?> getParser() {
|
||||
return java22p;
|
||||
}
|
||||
|
||||
@Test
|
||||
void jep459TemplateProcessors() {
|
||||
doTest("Jep459_StringTemplates");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jep459TemplateProcessorsBeforeJava22Preview() {
|
||||
ParseException thrown = assertThrows(ParseException.class, () -> java22.parseResource("Jep459_StringTemplates.java"));
|
||||
assertThat(thrown.getMessage(), containsString("String templates is a preview feature of JDK 22, you should select your language version accordingly"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void jep459TemplateExpressionType() {
|
||||
ASTCompilationUnit unit = java22p.parse("class Foo {{ int i = 1; String s = STR.\"i = \\{i}\"; }}");
|
||||
ASTTemplateExpression templateExpression = unit.descendants(ASTTemplateExpression.class).first();
|
||||
JTypeMirror typeMirror = templateExpression.getTypeMirror();
|
||||
assertEquals("java.lang.String", ((JClassSymbol) typeMirror.getSymbol()).getCanonicalName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unnamedPatternsAndVariables() {
|
||||
doTest("Jep443_UnnamedPatternsAndVariables");
|
||||
}
|
||||
|
||||
@Test
|
||||
void unnamedPatternsAndVariablesBeforeJava22Preview() {
|
||||
ParseException thrown = assertThrows(ParseException.class, () -> java22.parseResource("Jep443_UnnamedPatternsAndVariables.java"));
|
||||
assertThat(thrown.getMessage(), containsString("Since Java 9, '_' is reserved and cannot be used as an identifier"));
|
||||
|
||||
thrown = assertThrows(ParseException.class, () -> java22.parseResource("Jep443_UnnamedPatternsAndVariables2.java"));
|
||||
assertThat(thrown.getMessage(), containsString("Unnamed patterns and variables is a preview feature of JDK 22, you should select your language version accordingly"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unnamedClasses1() {
|
||||
doTest("Jep445_UnnamedClasses1");
|
||||
ASTCompilationUnit compilationUnit = java22p.parseResource("Jep445_UnnamedClasses1.java");
|
||||
assertTrue(compilationUnit.isUnnamedClass());
|
||||
ASTMethodCall methodCall = compilationUnit.descendants(ASTMethodCall.class).first();
|
||||
assertNotNull(methodCall.getTypeMirror());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unnamedClasses2() {
|
||||
doTest("Jep445_UnnamedClasses2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void unnamedClasses3() {
|
||||
doTest("Jep445_UnnamedClasses3");
|
||||
}
|
||||
|
||||
@Test
|
||||
void unnamedClassesBeforeJava22Preview() {
|
||||
ParseException thrown = assertThrows(ParseException.class, () -> java22.parseResource("Jep445_UnnamedClasses1.java"));
|
||||
assertThat(thrown.getMessage(), containsString("Unnamed classes is a preview feature of JDK 22, you should select your language version accordingly"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOrdinaryCompilationUnit() {
|
||||
ASTCompilationUnit compilationUnit = java22.parse("public class Foo { public static void main(String[] args) {}}");
|
||||
assertFalse(compilationUnit.isUnnamedClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testModularCompilationUnit() {
|
||||
ASTCompilationUnit compilationUnit = java22.parse("module foo {}");
|
||||
assertFalse(compilationUnit.isUnnamedClass());
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @see <a href="https://openjdk.org/jeps/443">JEP 443: Unnamed Patterns and Variables (Preview)</a>
|
||||
*/
|
||||
class Jep443_UnamedPatternsAndVariables {
|
||||
record Point(int x, int y) { }
|
||||
enum Color { RED, GREEN, BLUE }
|
||||
record ColoredPoint(Point p, Color c) { }
|
||||
|
||||
void unnamedPatterns1() {
|
||||
ColoredPoint r = new ColoredPoint(new Point(3,4), Color.GREEN);
|
||||
|
||||
if (r instanceof ColoredPoint(Point p, Color _)) {
|
||||
System.out.println(p.x() + " " + p.y());
|
||||
}
|
||||
|
||||
if (r instanceof ColoredPoint(Point(int x, int y), _)) {
|
||||
System.out.println(x + " " + y);
|
||||
}
|
||||
}
|
||||
|
||||
sealed abstract class Ball permits RedBall, BlueBall, GreenBall { }
|
||||
final class RedBall extends Ball { }
|
||||
final class BlueBall extends Ball { }
|
||||
final class GreenBall extends Ball { }
|
||||
|
||||
record Box<T extends Ball>(T content) { }
|
||||
|
||||
void unnamedPatterns2() {
|
||||
Box<? extends Ball> b = new Box<>(new RedBall());
|
||||
switch (b) {
|
||||
case Box(RedBall _) -> processBox(b);
|
||||
case Box(BlueBall _) -> processBox(b);
|
||||
case Box(GreenBall _) -> stopProcessing();
|
||||
}
|
||||
|
||||
switch (b) {
|
||||
case Box(RedBall _), Box(BlueBall _) -> processBox(b);
|
||||
case Box(GreenBall _) -> stopProcessing();
|
||||
case Box(_) -> pickAnotherBox();
|
||||
}
|
||||
|
||||
int x = 42;
|
||||
switch (b) {
|
||||
// multiple patterns guarded by one guard
|
||||
case Box(RedBall _), Box(BlueBall _) when x == 42 -> processBox(b);
|
||||
case Box(_) -> pickAnotherBox();
|
||||
}
|
||||
}
|
||||
|
||||
private void processBox(Box<? extends Ball> b) {}
|
||||
private void stopProcessing() {}
|
||||
private void pickAnotherBox() {}
|
||||
|
||||
class Order {}
|
||||
private static final int LIMIT = 10;
|
||||
private int sideEffect() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unnamedVariables(List<Order> orders) {
|
||||
int total = 0;
|
||||
for (Order _ : orders) {
|
||||
if (total < LIMIT) {
|
||||
total++;
|
||||
}
|
||||
}
|
||||
System.out.println("total: " + total);
|
||||
|
||||
for (int i = 0, _ = sideEffect(); i < 10; i++) {
|
||||
System.out.println(i);
|
||||
}
|
||||
|
||||
Queue<Integer> q = new ArrayDeque<>(); // x1, y1, z1, x2, y2, z2 ..
|
||||
while (q.size() >= 3) {
|
||||
int x = q.remove();
|
||||
int y = q.remove();
|
||||
int _ = q.remove(); // z is unused
|
||||
Point p = new Point(x, y);
|
||||
}
|
||||
while (q.size() >= 3) {
|
||||
var x = q.remove();
|
||||
var _ = q.remove();
|
||||
var _ = q.remove();
|
||||
Point p = new Point(x, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static class ScopedContext implements AutoCloseable {
|
||||
@Override
|
||||
public void close() { }
|
||||
public static ScopedContext acquire() {
|
||||
return new ScopedContext();
|
||||
}
|
||||
}
|
||||
|
||||
void unusedVariables2() {
|
||||
try (var _ = ScopedContext.acquire()) {
|
||||
//... acquiredContext not used ...
|
||||
}
|
||||
|
||||
String s = "123";
|
||||
try {
|
||||
int i = Integer.parseInt(s);
|
||||
System.out.println(i);
|
||||
} catch (NumberFormatException _) {
|
||||
System.out.println("Bad number: " + s);
|
||||
} catch (Exception _) {
|
||||
System.out.println("error...");
|
||||
}
|
||||
|
||||
List.of("a", "b").stream().collect(Collectors.toMap(String::toUpperCase, _ -> "NO_DATA"));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @see <a href="https://openjdk.org/jeps/443">JEP 443: Unnamed Patterns and Variables (Preview)</a>
|
||||
*/
|
||||
class Jep443_UnamedPatternsAndVariables2 {
|
||||
record Point(int x, int y) { }
|
||||
enum Color { RED, GREEN, BLUE }
|
||||
record ColoredPoint(Point p, Color c) { }
|
||||
|
||||
void unnamedPatterns1() {
|
||||
ColoredPoint r = new ColoredPoint(new Point(3,4), Color.GREEN);
|
||||
|
||||
if (r instanceof ColoredPoint(Point(int x, int y), _)) {
|
||||
System.out.println(x + " " + y);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a>
|
||||
*/
|
||||
|
||||
void main() {
|
||||
System.out.println("Hello World");
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
+- CompilationUnit[@PackageName = ""]
|
||||
+- MethodDeclaration[@Abstract = false, @Arity = 0, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Image = "main", @MainMethod = true, @Name = "main", @Overridden = false, @Static = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = true]
|
||||
+- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"]
|
||||
+- VoidType[]
|
||||
+- FormalParameters[@Empty = true, @Size = 0]
|
||||
+- Block[@Empty = false, @Size = 1, @containsComment = false]
|
||||
+- ExpressionStatement[]
|
||||
+- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
+- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "out", @Name = "out", @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
| +- TypeExpression[@CompileTimeConstant = false, @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
| +- ClassType[@FullyQualified = false, @SimpleName = "System"]
|
||||
+- ArgumentList[@Empty = false, @Size = 1]
|
||||
+- StringLiteral[@CompileTimeConstant = true, @ConstValue = "Hello World", @Empty = false, @Image = "\"Hello World\"", @Length = 11, @LiteralText = "\"Hello World\"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false]
|
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a>
|
||||
*/
|
||||
|
||||
String greeting() { return "Hello, World!"; }
|
||||
|
||||
void main() {
|
||||
System.out.println(greeting());
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
+- CompilationUnit[@PackageName = ""]
|
||||
+- MethodDeclaration[@Abstract = false, @Arity = 0, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Image = "greeting", @Name = "greeting", @Overridden = false, @Static = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = false]
|
||||
| +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"]
|
||||
| +- ClassType[@FullyQualified = false, @SimpleName = "String"]
|
||||
| +- FormalParameters[@Empty = true, @Size = 0]
|
||||
| +- Block[@Empty = false, @Size = 1, @containsComment = false]
|
||||
| +- ReturnStatement[]
|
||||
| +- StringLiteral[@CompileTimeConstant = true, @ConstValue = "Hello, World!", @Empty = false, @Image = "\"Hello, World!\"", @Length = 13, @LiteralText = "\"Hello, World!\"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false]
|
||||
+- MethodDeclaration[@Abstract = false, @Arity = 0, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Image = "main", @MainMethod = true, @Name = "main", @Overridden = false, @Static = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = true]
|
||||
+- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"]
|
||||
+- VoidType[]
|
||||
+- FormalParameters[@Empty = true, @Size = 0]
|
||||
+- Block[@Empty = false, @Size = 1, @containsComment = false]
|
||||
+- ExpressionStatement[]
|
||||
+- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
+- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "out", @Name = "out", @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
| +- TypeExpression[@CompileTimeConstant = false, @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
| +- ClassType[@FullyQualified = false, @SimpleName = "System"]
|
||||
+- ArgumentList[@Empty = false, @Size = 1]
|
||||
+- MethodCall[@CompileTimeConstant = false, @Image = "greeting", @MethodName = "greeting", @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
+- ArgumentList[@Empty = true, @Size = 0]
|
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @see <a href="https://openjdk.org/jeps/445">JEP 445: Unnamed Classes and Instance Main Methods (Preview)</a>
|
||||
*/
|
||||
|
||||
String greeting = "Hello, World!";
|
||||
|
||||
void main() {
|
||||
System.out.println(greeting);
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
+- CompilationUnit[@PackageName = ""]
|
||||
+- FieldDeclaration[@EffectiveVisibility = Visibility.V_PACKAGE, @Static = false, @Visibility = Visibility.V_PACKAGE]
|
||||
| +- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"]
|
||||
| +- ClassType[@FullyQualified = false, @SimpleName = "String"]
|
||||
| +- VariableDeclarator[@Initializer = true, @Name = "greeting"]
|
||||
| +- VariableId[@ArrayType = false, @EffectiveVisibility = Visibility.V_PACKAGE, @EnumConstant = false, @ExceptionBlockParameter = false, @Field = true, @Final = false, @ForLoopVariable = false, @ForeachVariable = false, @FormalParameter = false, @LambdaParameter = false, @LocalVariable = false, @Name = "greeting", @PatternBinding = false, @RecordComponent = false, @ResourceDeclaration = false, @TypeInferred = false, @Visibility = Visibility.V_PACKAGE]
|
||||
| +- StringLiteral[@CompileTimeConstant = true, @ConstValue = "Hello, World!", @Empty = false, @Image = "\"Hello, World!\"", @Length = 13, @LiteralText = "\"Hello, World!\"", @ParenthesisDepth = 0, @Parenthesized = false, @TextBlock = false]
|
||||
+- MethodDeclaration[@Abstract = false, @Arity = 0, @EffectiveVisibility = Visibility.V_PACKAGE, @Final = false, @Image = "main", @MainMethod = true, @Name = "main", @Overridden = false, @Static = false, @Varargs = false, @Visibility = Visibility.V_PACKAGE, @Void = true]
|
||||
+- ModifierList[@EffectiveModifiers = "{}", @ExplicitModifiers = "{}"]
|
||||
+- VoidType[]
|
||||
+- FormalParameters[@Empty = true, @Size = 0]
|
||||
+- Block[@Empty = false, @Size = 1, @containsComment = false]
|
||||
+- ExpressionStatement[]
|
||||
+- MethodCall[@CompileTimeConstant = false, @Image = "println", @MethodName = "println", @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
+- FieldAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "out", @Name = "out", @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
| +- TypeExpression[@CompileTimeConstant = false, @ParenthesisDepth = 0, @Parenthesized = false]
|
||||
| +- ClassType[@FullyQualified = false, @SimpleName = "System"]
|
||||
+- ArgumentList[@Empty = false, @Size = 1]
|
||||
+- VariableAccess[@AccessType = AccessType.READ, @CompileTimeConstant = false, @Image = "greeting", @Name = "greeting", @ParenthesisDepth = 0, @Parenthesized = false]
|
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
import static java.lang.StringTemplate.RAW;
|
||||
import static java.util.FormatProcessor.FMT;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* @see <a href="https://openjdk.org/jeps/430">JEP 430: String Templates (Preview)</a> (Java 21)
|
||||
* @see <a href="https://openjdk.org/jeps/459">JEP 459: String Templates (Second Preview)</a> (Java 22)
|
||||
*/
|
||||
class Jep459_StringTemplates {
|
||||
record Request(String date, String time, String ipAddress) {}
|
||||
|
||||
static void STRTemplateProcessor() {
|
||||
// Embedded expressions can be strings
|
||||
String firstName = "Bill";
|
||||
String lastName = "Duck";
|
||||
String fullName = STR."\{firstName} \{lastName}";
|
||||
// | "Bill Duck"
|
||||
String sortName = STR."\{lastName}, \{firstName}";
|
||||
// | "Duck, Bill"
|
||||
|
||||
// Embedded expressions can perform arithmetic
|
||||
int x = 10, y = 20;
|
||||
String s1 = STR."\{x} + \{y} = \{x + y}";
|
||||
// | "10 + 20 = 30"
|
||||
|
||||
// Embedded expressions can invoke methods and access fields
|
||||
String s2 = STR."You have a \{getOfferType()} waiting for you!";
|
||||
// | "You have a gift waiting for you!"
|
||||
Request req = new Request("2022-03-25", "15:34", "8.8.8.8");
|
||||
String t = STR."Access at \{req.date} \{req.time} from \{req.ipAddress}";
|
||||
//| "Access at 2022-03-25 15:34 from 8.8.8.8"
|
||||
|
||||
String filePath = "tmp.dat";
|
||||
File file = new File(filePath);
|
||||
String old = "The file " + filePath + " " + (file.exists() ? "does" : "does not") + " exist";
|
||||
String msg = STR."The file \{filePath} \{file.exists() ? "does" : "does not"} exist";
|
||||
// | "The file tmp.dat does exist" or "The file tmp.dat does not exist"
|
||||
|
||||
// spread over multiple lines
|
||||
String time = STR."The time is \{
|
||||
// The java.time.format package is very useful
|
||||
DateTimeFormatter
|
||||
.ofPattern("HH:mm:ss")
|
||||
.format(LocalTime.now())
|
||||
} right now";
|
||||
// | "The time is 12:34:56 right now"
|
||||
|
||||
// Left to right
|
||||
// Embedded expressions can be postfix increment expressions
|
||||
int index = 0;
|
||||
String data = STR."\{index++}, \{index++}, \{index++}, \{index++}";
|
||||
// | "0, 1, 2, 3"
|
||||
|
||||
// Embedded expression is a (nested) template expression
|
||||
String[] fruit = { "apples", "oranges", "peaches" };
|
||||
String s3 = STR."\{fruit[0]}, \{STR."\{fruit[1]}, \{fruit[2]}"}";
|
||||
// | "apples, oranges, peaches"
|
||||
String s4 = STR."\{fruit[0]}, \{
|
||||
STR."\{fruit[1]}, \{fruit[2]}"
|
||||
}";
|
||||
}
|
||||
|
||||
static String getOfferType() { return "_getOfferType_"; }
|
||||
|
||||
static void multilineTemplateExpressions() {
|
||||
String title = "My Web Page";
|
||||
String text = "Hello, world";
|
||||
String html = STR."""
|
||||
<html>
|
||||
<head>
|
||||
<title>\{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>\{text}</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
/*
|
||||
| """
|
||||
| <html>
|
||||
| <head>
|
||||
| <title>My Web Page</title>
|
||||
| </head>
|
||||
| <body>
|
||||
| <p>Hello, world</p>
|
||||
| </body>
|
||||
| </html>
|
||||
| """
|
||||
*/
|
||||
|
||||
String name = "Joan Smith";
|
||||
String phone = "555-123-4567";
|
||||
String address = "1 Maple Drive, Anytown";
|
||||
String json = STR."""
|
||||
{
|
||||
"name": "\{name}",
|
||||
"phone": "\{phone}",
|
||||
"address": "\{address}"
|
||||
}
|
||||
""";
|
||||
/*
|
||||
| """
|
||||
| {
|
||||
| "name": "Joan Smith",
|
||||
| "phone": "555-123-4567",
|
||||
| "address": "1 Maple Drive, Anytown"
|
||||
| }
|
||||
| """
|
||||
*/
|
||||
|
||||
record Rectangle(String name, double width, double height) {
|
||||
double area() {
|
||||
return width * height;
|
||||
}
|
||||
}
|
||||
Rectangle[] zone = new Rectangle[] {
|
||||
new Rectangle("Alfa", 17.8, 31.4),
|
||||
new Rectangle("Bravo", 9.6, 12.4),
|
||||
new Rectangle("Charlie", 7.1, 11.23),
|
||||
};
|
||||
String table = STR."""
|
||||
Description Width Height Area
|
||||
\{zone[0].name} \{zone[0].width} \{zone[0].height} \{zone[0].area()}
|
||||
\{zone[1].name} \{zone[1].width} \{zone[1].height} \{zone[1].area()}
|
||||
\{zone[2].name} \{zone[2].width} \{zone[2].height} \{zone[2].area()}
|
||||
Total \{zone[0].area() + zone[1].area() + zone[2].area()}
|
||||
""";
|
||||
/*
|
||||
| """
|
||||
| Description Width Height Area
|
||||
| Alfa 17.8 31.4 558.92
|
||||
| Bravo 9.6 12.4 119.03999999999999
|
||||
| Charlie 7.1 11.23 79.733
|
||||
| Total 757.693
|
||||
| """
|
||||
*/
|
||||
}
|
||||
|
||||
static void FMTTemplateProcessor() {
|
||||
record Rectangle(String name, double width, double height) {
|
||||
double area() {
|
||||
return width * height;
|
||||
}
|
||||
};
|
||||
Rectangle[] zone = new Rectangle[] {
|
||||
new Rectangle("Alfa", 17.8, 31.4),
|
||||
new Rectangle("Bravo", 9.6, 12.4),
|
||||
new Rectangle("Charlie", 7.1, 11.23),
|
||||
};
|
||||
String table = FMT."""
|
||||
Description Width Height Area
|
||||
%-12s\{zone[0].name} %7.2f\{zone[0].width} %7.2f\{zone[0].height} %7.2f\{zone[0].area()}
|
||||
%-12s\{zone[1].name} %7.2f\{zone[1].width} %7.2f\{zone[1].height} %7.2f\{zone[1].area()}
|
||||
%-12s\{zone[2].name} %7.2f\{zone[2].width} %7.2f\{zone[2].height} %7.2f\{zone[2].area()}
|
||||
\{" ".repeat(28)} Total %7.2f\{zone[0].area() + zone[1].area() + zone[2].area()}
|
||||
""";
|
||||
/*
|
||||
| """
|
||||
| Description Width Height Area
|
||||
| Alfa 17.80 31.40 558.92
|
||||
| Bravo 9.60 12.40 119.04
|
||||
| Charlie 7.10 11.23 79.73
|
||||
| Total 757.69
|
||||
| """
|
||||
*/
|
||||
}
|
||||
|
||||
static void ensuringSafety() {
|
||||
String name = "Joan";
|
||||
StringTemplate st = RAW."My name is \{name}";
|
||||
String info = STR.process(st);
|
||||
}
|
||||
|
||||
record User(String firstName, int accountNumber) {}
|
||||
|
||||
static void literalsInsideTemplateExpressions() {
|
||||
String s1 = STR."Welcome to your account";
|
||||
// | "Welcome to your account"
|
||||
User user = new User("Lisa", 12345);
|
||||
String s2 = STR."Welcome, \{user.firstName()}, to your account \{user.accountNumber()}";
|
||||
// | "Welcome, Lisa, to your account 12345"
|
||||
}
|
||||
|
||||
static void emptyEmbeddedExpression() {
|
||||
String s1=STR."Test \{ }";
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user