Skip to content

Commit a6793a3

Browse files
committed
Support for TextBlockLiterals
1 parent 88c07fb commit a6793a3

8 files changed

Lines changed: 252 additions & 27 deletions

File tree

key.core/src/main/java/de/uka/ilkd/key/java/transformations/KeYJavaPipeline.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*/
2121
public class KeYJavaPipeline {
2222
private final TransformationPipelineServices pipelineServices;
23-
private final List<JavaTransformer> steps = new LinkedList<>();
23+
private final List<SimpleJavaTransformer> steps = new LinkedList<>();
2424
private static final Logger LOGGER = LoggerFactory.getLogger(KeYJavaPipeline.class);
2525

2626

@@ -32,12 +32,13 @@ public TransformationPipelineServices getPipelineServices() {
3232
return pipelineServices;
3333
}
3434

35-
public List<JavaTransformer> getSteps() {
35+
public List<SimpleJavaTransformer> getSteps() {
3636
return steps;
3737
}
3838

3939
public static KeYJavaPipeline createDefault(TransformationPipelineServices pipelineServices) {
4040
KeYJavaPipeline p = new KeYJavaPipeline(pipelineServices);
41+
p.add(new TextblockTransformer());
4142
p.add(new EnumClassBuilder(pipelineServices));
4243
p.add(new JMLTransformer(pipelineServices));
4344
p.add(new JmlDocRemoval(pipelineServices));
@@ -56,7 +57,7 @@ public static KeYJavaPipeline createDefault(TransformationPipelineServices pipel
5657
return p;
5758
}
5859

59-
public void add(JavaTransformer step) {
60+
public void add(SimpleJavaTransformer step) {
6061
steps.add(step);
6162
}
6263

@@ -70,7 +71,7 @@ public void apply(Collection<CompilationUnit> compilationUnits) {
7071
.map(it -> it.getPath().toUri())
7172
.orElse(null);
7273

73-
for (JavaTransformer step : steps) {
74+
for (SimpleJavaTransformer step : steps) {
7475
long start = System.currentTimeMillis();
7576
step.apply(compilationUnit);
7677
long stop = System.currentTimeMillis();

key.core/src/main/java/de/uka/ilkd/key/java/transformations/pipeline/JavaTransformer.java

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,23 @@
44
package de.uka.ilkd.key.java.transformations.pipeline;
55

66

7-
87
import com.github.javaparser.ast.CompilationUnit;
98
import com.github.javaparser.ast.Node;
109
import com.github.javaparser.ast.body.TypeDeclaration;
1110
import org.jspecify.annotations.NullMarked;
1211

1312
/**
14-
* The JavaDL requires some implicit fields, that are available in each
15-
* Java class. The name of the implicit fields starts usually with a dollar sign.
16-
* (in the age of recoder it was enclosed in angle brackets).
17-
* <p>
18-
* To access the fields in a uniform way, they are added as usual
19-
* fields to the classes, in particular this allows us to parse them in
20-
* easier.
21-
* For further information see also
22-
* <ul>
23-
* <li>{@link ImplicitFieldAdder}</li>
24-
* <li>{@link CreateObjectBuilder}</li>
25-
* <li>{@link PrepareObjectBuilder}</li>
26-
* </ul>
27-
* <p>
28-
* Performance of these classes was low, so information that is shared between
29-
* all instances of a transformation set has been outsourced to a transformation
30-
* cache.
13+
* {@link SimpleJavaTransformer} with an internal state towards the current
14+
* {@link TransformationPipelineServices}
15+
* and {@link TransformationPipelineServices.TransformerCache}. These fields are set by the
16+
* constructor, and given by
17+
* the {@link de.uka.ilkd.key.java.transformations.KeYJavaPipeline}.
18+
*
19+
* @author weigl
20+
* @see de.uka.ilkd.key.java.transformations.KeYJavaPipeline
3121
*/
3222
@NullMarked
33-
public abstract class JavaTransformer {
23+
public abstract class JavaTransformer implements SimpleJavaTransformer {
3424
/**
3525
* Further services and helper function for this pipeline step.
3626
*/
@@ -47,8 +37,7 @@ public abstract class JavaTransformer {
4737
/**
4838
* creates a transformer for the recoder model
4939
*
50-
* @param services
51-
* the CrossReferenceServiceConfiguration to access
40+
* @param services the CrossReferenceServiceConfiguration to access
5241
* model information
5342
*/
5443
public JavaTransformer(TransformationPipelineServices services) {
@@ -62,8 +51,11 @@ public JavaTransformer(TransformationPipelineServices services) {
6251
* descend in inner classes you have to implement the recursion by
6352
* yourself.
6453
*/
65-
public void apply(TypeDeclaration<?> td) {}
54+
@Override
55+
public void apply(TypeDeclaration<?> td) {
56+
}
6657

58+
@Override
6759
public void apply(CompilationUnit cu) {
6860
for (TypeDeclaration<?> type : cu.getTypes()) {
6961
apply(type);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* This file is part of KeY - https://key-project.org
2+
* KeY is licensed under the GNU General Public License Version 2
3+
* SPDX-License-Identifier: GPL-2.0-only */
4+
package de.uka.ilkd.key.java.transformations.pipeline;
5+
6+
7+
import com.github.javaparser.ast.CompilationUnit;
8+
import com.github.javaparser.ast.Node;
9+
import com.github.javaparser.ast.body.TypeDeclaration;
10+
import org.jspecify.annotations.NullMarked;
11+
12+
/**
13+
* A transformation of Java ASTs in an early loading stage.
14+
* <p>
15+
* A {@link SimpleJavaTransformer} defines a transformation on mutable {@link Node}.
16+
* As an entry an transformer receives the compilation unit. Traversal of it is left to the
17+
* implementation of the transformer. The code should be transformed in-place. Creation of new
18+
* {@link CompilationUnit}
19+
* is not possible, but the creation of new {@link TypeDeclaration}
20+
*
21+
* Implementation should be stateless as instances are reused accross {@link CompilationUnit}s but
22+
* not across
23+
* loading requests.
24+
*
25+
* @author weigl
26+
*/
27+
@NullMarked
28+
public interface SimpleJavaTransformer {
29+
default void apply(TypeDeclaration<?> td) {
30+
}
31+
32+
default void apply(CompilationUnit cu) {
33+
for (TypeDeclaration<?> type : cu.getTypes()) {
34+
apply(type);
35+
for (var m : type.getMembers()) {
36+
if (m instanceof TypeDeclaration<?> ty) {
37+
apply(ty);
38+
}
39+
}
40+
}
41+
}
42+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* This file is part of KeY - https://key-project.org
2+
* KeY is licensed under the GNU General Public License Version 2
3+
* SPDX-License-Identifier: GPL-2.0-only */
4+
package de.uka.ilkd.key.java.transformations.pipeline;
5+
6+
import com.github.javaparser.ast.body.TypeDeclaration;
7+
import com.github.javaparser.ast.expr.StringLiteralExpr;
8+
import com.github.javaparser.ast.expr.TextBlockLiteralExpr;
9+
10+
/**
11+
*
12+
* @author Alexander Weigl
13+
* @version 1 (3/31/26)
14+
*/
15+
public class TextblockTransformer implements SimpleJavaTransformer {
16+
@Override
17+
public void apply(TypeDeclaration<?> td) {
18+
for (var textblock : td.findAll(TextBlockLiteralExpr.class)) {
19+
var s = textblock.getValue().stripIndent().translateEscapes()
20+
.replace("\\\n", "")
21+
.replace("\"", "\\\"");
22+
var literal = new StringLiteralExpr(s);
23+
textblock.replace(literal);
24+
}
25+
}
26+
}

key.core/src/test/java/KeyJavaPipelineTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import de.uka.ilkd.key.java.Services;
1515
import de.uka.ilkd.key.java.transformations.KeYJavaPipeline;
1616
import de.uka.ilkd.key.java.transformations.pipeline.JavaTransformer;
17+
import de.uka.ilkd.key.java.transformations.pipeline.SimpleJavaTransformer;
1718
import de.uka.ilkd.key.java.transformations.pipeline.TransformationPipelineServices;
1819
import de.uka.ilkd.key.nparser.NamespaceBuilder;
1920
import de.uka.ilkd.key.proof.init.JavaProfile;
@@ -78,7 +79,7 @@ public KeYJavaPipeline createPipelineTest(Path testFolder) throws IOException {
7879
var kjp = KeYJavaPipeline.createDefault(tservices);
7980
var kjp2 = new KeYJavaPipeline(tservices);
8081
var cnt = 0;
81-
for (JavaTransformer step : kjp.getSteps()) {
82+
for (SimpleJavaTransformer step : kjp.getSteps()) {
8283
kjp2.add(step);
8384
final var file = testFolder.resolve("actual").resolve(
8485
String.format("%02d_%s", ++cnt, step.getClass().getSimpleName()));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* This file is part of KeY - https://key-project.org
2+
* KeY is licensed under the GNU General Public License Version 2
3+
* SPDX-License-Identifier: GPL-2.0-only */
4+
package de.uka.ilkd.key.java.transformations.pipeline;
5+
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
9+
import org.key_project.util.java.IOUtil;
10+
11+
import com.github.javaparser.JavaParser;
12+
import com.github.javaparser.ParseResult;
13+
import com.github.javaparser.ParserConfiguration;
14+
import com.github.javaparser.ast.CompilationUnit;
15+
import com.google.common.truth.Truth;
16+
import org.junit.jupiter.api.Test;
17+
18+
import static org.junit.jupiter.api.Assertions.assertNotNull;
19+
import static org.junit.jupiter.api.Assertions.fail;
20+
21+
/**
22+
*
23+
* @author Alexander Weigl
24+
* @version 1 (3/31/26)
25+
*/
26+
class TextblockTransformerTest {
27+
@Test
28+
void transform() throws IOException {
29+
final InputStream expected = getClass().getResourceAsStream("Textblock.expected.java");
30+
final InputStream input = getClass().getResourceAsStream("Textblock.java");
31+
assertNotNull(input);
32+
assertNotNull(expected);
33+
34+
JavaParser parser = new JavaParser();
35+
parser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.RAW);
36+
37+
ParseResult<CompilationUnit> result = parser.parse(input);
38+
if (!result.isSuccessful()) {
39+
result.getProblems().forEach(System.err::println);
40+
fail("Parsing of fixture failed.");
41+
}
42+
43+
44+
CompilationUnit cu = result.getResult().get();
45+
46+
TextblockTransformer transformer = new TextblockTransformer();
47+
transformer.apply(cu);
48+
49+
String expectedJava = IOUtil.readFrom(expected);
50+
Truth.assertThat(cu.toString()).isEqualTo(expectedJava);
51+
}
52+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
public class Textblock {
2+
3+
// Expected One large string w/o newlines
4+
String text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
5+
6+
// Expected: strip indentation and new lines. Four spaces before <body>.
7+
String html = "<html>\n <body>\n <p>Hello, world</p>\n </body>\n</html>\n";
8+
9+
// Expected: String with newlines and escaped quotes.
10+
String query = "SELECT \"EMP_ID\", \"LAST_NAME\" FROM \"EMPLOYEE_TB\"\nWHERE \"CITY\" = 'INDIANAPOLIS'\nORDER BY \"EMP_ID\", \"LAST_NAME\";\n";
11+
12+
Object obj = ("function hello() {\n print('\"Hello, world\"');\n}") + "hello();\n";
13+
14+
//expected "line 1\nlines 2\nline 3\n"
15+
String simple = "line 1\nline 2\nline 3\n";
16+
17+
//Expected: newlines and escaped quotes
18+
String code = "String text = \"\"\"\n A text block inside a text block\n\"\"\";\n";
19+
20+
String abc = (" 1 \"\n 2 \"\"\n 3 \"\"\"\n 4 \"\"\"\"\n 5 \"\"\"\"\"\n 6 \"\"\"\"\"\"\n 7 \"\"\"\"\"\"\"\n 8 \"\"\"\"\"\"\"\"\n 9 \"\"\"\"\"\"\"\"\"\n 10 \"\"\"\"\"\"\"\"\"\"\n 11 \"\"\"\"\"\"\"\"\"\"\"\n 12 \"\"\"\"\"\"\"\"\"\"\"\"\n");
21+
22+
String text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
23+
24+
String colors = "red \ngreen \nblue \n";
25+
26+
// attention trailing whitespaces!
27+
String colors = "red\ngreen\nblue\n";
28+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
public class Textblock {
2+
3+
// Expected One large string w/o newlines
4+
String text = """
5+
Lorem ipsum dolor sit amet, consectetur adipiscing \
6+
elit, sed do eiusmod tempor incididunt ut labore \
7+
et dolore magna aliqua.\
8+
""";
9+
10+
// Expected: strip indentation and new lines. Four spaces before <body>.
11+
String html = """
12+
<html>
13+
<body>
14+
<p>Hello, world</p>
15+
</body>
16+
</html>
17+
""";
18+
19+
// Expected: String with newlines and escaped quotes.
20+
String query = """
21+
SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"
22+
WHERE "CITY" = 'INDIANAPOLIS'
23+
ORDER BY "EMP_ID", "LAST_NAME";
24+
""";
25+
26+
Object obj = ("""
27+
function hello() {
28+
print('"Hello, world"');
29+
}""")+
30+
"""
31+
hello();
32+
""";
33+
34+
//expected "line 1\nlines 2\nline 3\n"
35+
String simple = """
36+
line 1
37+
line 2
38+
line 3
39+
""";
40+
41+
//Expected: newlines and escaped quotes
42+
String code =
43+
"""
44+
String text = \"""
45+
A text block inside a text block
46+
\""";
47+
""";
48+
49+
String abc = ("""
50+
1 "
51+
2 ""
52+
3 ""\"
53+
4 ""\""
54+
5 ""\"""
55+
6 ""\"""\"
56+
7 ""\"""\""
57+
8 ""\"""\"""
58+
9 ""\"""\"""\"
59+
10 ""\"""\"""\""
60+
11 ""\"""\"""\"""
61+
12 ""\"""\"""\"""\"
62+
""");
63+
64+
String text = """
65+
Lorem ipsum dolor sit amet, consectetur adipiscing \
66+
elit, sed do eiusmod tempor incididunt ut labore \
67+
et dolore magna aliqua.\
68+
""";
69+
70+
String colors = """
71+
red \s
72+
green\s
73+
blue \s
74+
""";
75+
76+
// attention trailing whitespaces!
77+
String colors = """
78+
red
79+
green
80+
blue
81+
""";
82+
83+
}

0 commit comments

Comments
 (0)