Skip to content

Commit 5d11231

Browse files
authored
Merge branch 'main' into 47117-recover
2 parents 1d7fc55 + b80a841 commit 5d11231

File tree

7 files changed

+132
-24
lines changed

7 files changed

+132
-24
lines changed

extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/TypeInfos.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,13 @@ public boolean isInfixNotationSupported() {
221221
}
222222

223223
@Override
224-
public boolean isLiteralSeparator(char candidate) {
225-
return candidate == '<' || candidate == '>';
224+
public boolean isLiteralSeparatorStart(char candidate) {
225+
return candidate == '<';
226+
}
227+
228+
@Override
229+
public boolean isLiteralSeparatorEnd(char startSeparator, char candidate) {
230+
return candidate == '>';
226231
}
227232

228233
};

extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/extensions/StringTemplateExtensionsTest.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@ public class StringTemplateExtensionsTest {
1919

2020
@RegisterExtension
2121
static final QuarkusUnitTest config = new QuarkusUnitTest()
22-
.withApplicationRoot(root -> root.addAsResource(
23-
new StringAsset("{str:eval('Hello {name}!')}"),
24-
"templates/hello.txt"));
22+
.withApplicationRoot(root -> root
23+
.addAsResource(
24+
new StringAsset("{str:eval('Hello {name}!')}"),
25+
"templates/hello.txt")
26+
.addAsResource(
27+
// https://github.com/quarkusio/quarkus/issues/47092
28+
// This will trigger value resolver generation for StringBuilder
29+
new StringAsset("{str:builder.append('Qute').append(\" is\").append(' cool!')}"),
30+
"templates/builder.txt"));
2531

2632
@Inject
2733
Engine engine;
@@ -93,6 +99,21 @@ public void testTemplateExtensions() {
9399
assertEquals("Hello fool!",
94100
hello.data("name", "fool")
95101
.render());
102+
103+
// https://github.com/quarkusio/quarkus/issues/47092
104+
assertEquals("Quteiscool!",
105+
engine.parse("{str:builder('Qute').append(\"is\").append(\"cool!\")}")
106+
.render());
107+
assertEquals("Qute's cool!",
108+
engine.parse("{str:builder('Qute').append(\"'s\").append(\" cool!\")}")
109+
.render());
110+
assertEquals("\"Qute\" is cool!",
111+
engine.parse("{str:builder('\"Qute\" ').append('is').append(\" cool!\")}")
112+
.render());
113+
assertEquals("Hello '!",
114+
engine.parse("{str:concat(\"Hello '\",\"!\")}")
115+
.render());
116+
96117
}
97118

98119
}

extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/filters/GracefulShutdownFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313

1414
public class GracefulShutdownFilter implements ShutdownListener, Handler<HttpServerRequest> {
1515

16-
private static Logger log = Logger.getLogger(GracefulShutdownFilter.class);
16+
private static final Logger log = Logger.getLogger(GracefulShutdownFilter.class);
1717

1818
private volatile Handler<HttpServerRequest> next;
1919
private volatile boolean running = true;
2020
private final AtomicInteger currentRequestCount = new AtomicInteger();
2121
private final AtomicReference<ShutdownNotification> notification = new AtomicReference<>();
2222

23-
private final Handler<Void> requestDoneHandler = new Handler<Void>() {
23+
private final Handler<Void> requestDoneHandler = new Handler<>() {
2424
@Override
2525
public void handle(Void event) {
2626
int count = currentRequestCount.decrementAndGet();

independent-projects/qute/core/src/main/java/io/quarkus/qute/Expressions.java

+48-9
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public static List<String> splitParts(String value, SplitConfig splitConfig) {
7272
if (value == null || value.isEmpty()) {
7373
return Collections.emptyList();
7474
}
75-
boolean literal = false;
75+
char literal = 0;
7676
char separator = 0;
7777
byte infix = 0;
7878
byte brackets = 0;
@@ -83,7 +83,7 @@ public static List<String> splitParts(String value, SplitConfig splitConfig) {
8383
if (splitConfig.isSeparator(c)) {
8484
// Adjacent separators may be ignored
8585
if (separator == 0 || separator != c) {
86-
if (!literal && brackets == 0 && infix == 0) {
86+
if (literal == 0 && brackets == 0 && infix == 0) {
8787
if (splitConfig.shouldPrependSeparator(c)) {
8888
buffer.append(c);
8989
}
@@ -99,11 +99,15 @@ public static List<String> splitParts(String value, SplitConfig splitConfig) {
9999
}
100100
}
101101
} else {
102-
if (splitConfig.isLiteralSeparator(c)) {
103-
literal = !literal;
102+
if (literal == 0
103+
&& splitConfig.isLiteralSeparatorStart(c)) {
104+
literal = c;
105+
} else if (literal != 0
106+
&& splitConfig.isLiteralSeparatorEnd(literal, c)) {
107+
literal = 0;
104108
}
105109
// Non-separator char
106-
if (!literal) {
110+
if (literal == 0) {
107111
// Not inside a string/type literal
108112
if (brackets == 0 && c == ' ' && splitConfig.isInfixNotationSupported()) {
109113
// Infix supported, blank space and not inside a virtual method
@@ -212,16 +216,27 @@ public boolean isInfixNotationSupported() {
212216
}
213217

214218
@Override
215-
public boolean isLiteralSeparator(char candidate) {
216-
return SplitConfig.super.isLiteralSeparator(candidate) || candidate == '<' || candidate == '>';
219+
public boolean isLiteralSeparatorStart(char candidate) {
220+
return SplitConfig.super.isLiteralSeparatorStart(candidate)
221+
// We need this in order to support things like {@com.foo.Bar<? extends org.acme.Baz, String> bar}
222+
// where a space should not be treated as a separator
223+
|| candidate == '<';
224+
}
225+
226+
@Override
227+
public boolean isLiteralSeparatorEnd(char startSeparator, char candidate) {
228+
if (startSeparator == '<') {
229+
return candidate == '>';
230+
}
231+
return SplitConfig.super.isLiteralSeparatorEnd(startSeparator, candidate);
217232
}
218233

219234
};
220235

221236
private static final SplitConfig TYPE_INFO_SPLIT_CONFIG = new DefaultSplitConfig() {
222237

223238
@Override
224-
public boolean isLiteralSeparator(char candidate) {
239+
public boolean isLiteralSeparatorStart(char candidate) {
225240
return candidate == TYPE_INFO_SEPARATOR || LiteralSupport.isStringLiteralSeparator(candidate);
226241
}
227242
};
@@ -247,12 +262,36 @@ public boolean shouldAppendSeparator(char candidate) {
247262

248263
public interface SplitConfig {
249264

265+
/**
266+
*
267+
* @param candidate
268+
* @return {@code true} if the characted should be treated as a "part" separator
269+
*/
250270
boolean isSeparator(char candidate);
251271

252-
default boolean isLiteralSeparator(char candidate) {
272+
/**
273+
* A "part" separator used inside a literal must be ignored.
274+
*
275+
* @param candidate
276+
* @return {@code true} if the characted should be treated as a "literal" start separator
277+
*/
278+
default boolean isLiteralSeparatorStart(char candidate) {
253279
return LiteralSupport.isStringLiteralSeparator(candidate);
254280
}
255281

282+
/**
283+
*
284+
* @param startSeparator
285+
* @param candidate
286+
* @return {@code true} if the characted should be treated as a "literal" end separator
287+
*/
288+
default boolean isLiteralSeparatorEnd(char startSeparator, char candidate) {
289+
if (isLiteralSeparatorStart(startSeparator)) {
290+
return startSeparator == candidate;
291+
}
292+
return false;
293+
}
294+
256295
default boolean shouldPrependSeparator(char candidate) {
257296
return false;
258297
}

independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java

+28-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.junit.jupiter.api.Test;
2323

24+
import io.quarkus.qute.Expression.Part;
2425
import io.quarkus.qute.TemplateException.Builder;
2526
import io.quarkus.qute.TemplateLocator.TemplateLocation;
2627
import io.quarkus.qute.TemplateNode.Origin;
@@ -460,13 +461,38 @@ public void testSectionParameterWithNestedSingleQuotationMark() {
460461
assertSectionParams(engine, "{#let id=\"'Foo \"}", Map.of("id", "\"'Foo \""));
461462
assertSectionParams(engine, "{#let id=\"'Foo ' \"}", Map.of("id", "\"'Foo ' \""));
462463
assertSectionParams(engine, "{#let id=\"'Foo ' \" bar='baz'}", Map.of("id", "\"'Foo ' \"", "bar", "'baz'"));
463-
assertSectionParams(engine, "{#let my=bad id=(\"'Foo ' \" + 1) bar='baz'}",
464-
Map.of("my", "bad", "id", "(\"'Foo ' \" + 1)", "bar", "'baz'"));
464+
assertSectionParams(engine, "{#let my=bad id=(foo + 1) bar='baz'}",
465+
Map.of("my", "bad", "id", "(foo + 1)", "bar", "'baz'"));
465466
assertSectionParams(engine, "{#let id = 'Foo'}", Map.of("id", "'Foo'"));
466467
assertSectionParams(engine, "{#let id= 'Foo'}", Map.of("id", "'Foo'"));
467468
assertSectionParams(engine, "{#let my = (bad or not) id=1}", Map.of("my", "(bad or not)", "id", "1"));
468469
assertSectionParams(engine, "{#let my= (bad or not) id=1}", Map.of("my", "(bad or not)", "id", "1"));
470+
}
471+
472+
@Test
473+
public void testVirtualMethodWithNestedLiteralSeparator() {
474+
Engine engine = Engine.builder().addDefaults().build();
475+
List<Part> parts = engine.parse("{foo('Bar \"!')}").findExpression(e -> true).getParts();
476+
assertVirtualMethodParam(parts.get(0), "foo", "Bar \"!");
477+
478+
parts = engine.parse("{foo(\"Bar '!\")}").findExpression(e -> true).getParts();
479+
assertVirtualMethodParam(parts.get(0), "foo", "Bar '!");
480+
481+
parts = engine.parse("{foo(\"Bar '!\").baz(1)}").findExpression(e -> true).getParts();
482+
assertVirtualMethodParam(parts.get(0), "foo", "Bar '!");
483+
assertVirtualMethodParam(parts.get(1), "baz", "1");
484+
485+
parts = engine.parse("{str:builder('Qute').append(\"is '\").append(\"cool!\")}").findExpression(e -> true).getParts();
486+
assertVirtualMethodParam(parts.get(0), "builder", "Qute");
487+
assertVirtualMethodParam(parts.get(1), "append", "is '");
488+
assertVirtualMethodParam(parts.get(2), "append", "cool!");
489+
}
469490

491+
private void assertVirtualMethodParam(Part part, String name, String literal) {
492+
assertTrue(part.isVirtualMethod());
493+
assertEquals(name, part.getName());
494+
assertTrue(part.asVirtualMethod().getParameters().get(0).isLiteral());
495+
assertEquals(literal, part.asVirtualMethod().getParameters().get(0).getLiteral().toString());
470496
}
471497

472498
@Test

independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java

+2
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,8 @@ private void matchMethods(String matchName, int matchParamsCount, Collection<Met
894894
} else {
895895
tryCatch.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE, whenRet, invokeRet);
896896
}
897+
// Always return from the matching block so that other matching methods are not used
898+
paramMatchScope.returnVoid();
897899
}
898900
}
899901

independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java

+21-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import java.util.concurrent.CompletionStage;
1414
import java.util.concurrent.TimeUnit;
1515

16+
import org.jboss.jandex.AnnotationInstance;
17+
import org.jboss.jandex.AnnotationValue;
1618
import org.jboss.jandex.ClassInfo;
1719
import org.jboss.jandex.DotName;
1820
import org.jboss.jandex.Index;
@@ -39,15 +41,16 @@ public class SimpleGeneratorTest {
3941
public static void init() throws IOException {
4042
TestClassOutput classOutput = new TestClassOutput();
4143
Index index = index(MyService.class, PublicMyService.class, BaseService.class, MyItem.class, String.class,
42-
CompletionStage.class, List.class, MyEnum.class);
44+
CompletionStage.class, List.class, MyEnum.class, StringBuilder.class);
4345
ClassInfo myServiceClazz = index.getClassByName(DotName.createSimple(MyService.class.getName()));
4446
ValueResolverGenerator generator = ValueResolverGenerator.builder().setIndex(index).setClassOutput(classOutput)
4547
.addClass(myServiceClazz)
46-
.addClass(index.getClassByName(DotName.createSimple(PublicMyService.class.getName())))
47-
.addClass(index.getClassByName(DotName.createSimple(MyItem.class.getName())))
48-
.addClass(index.getClassByName(DotName.createSimple(String.class.getName())))
49-
.addClass(index.getClassByName(DotName.createSimple(List.class.getName())))
50-
.addClass(index.getClassByName(DotName.createSimple(MyEnum.class.getName())))
48+
.addClass(index.getClassByName(PublicMyService.class))
49+
.addClass(index.getClassByName(MyItem.class))
50+
.addClass(index.getClassByName(String.class))
51+
.addClass(index.getClassByName(List.class))
52+
.addClass(index.getClassByName(MyEnum.class))
53+
.addClass(index.getClassByName(StringBuilder.class), stringBuilderTemplateData())
5154
.build();
5255

5356
generator.generate();
@@ -146,6 +149,7 @@ public void testWithEngine() throws Exception {
146149
assertEquals("OK", engine.parse("{#if enum is MyEnum:BAR}OK{/if}").data("enum", MyEnum.BAR).render());
147150
assertEquals("one", engine.parse("{MyEnum:valueOf('ONE').name}").render());
148151
assertEquals("10", engine.parse("{io_quarkus_qute_generator_MyService:getDummy(5)}").render());
152+
assertEquals("foo", engine.parse("{builder.append('foo')}").data("builder", new StringBuilder()).render());
149153
}
150154

151155
@Test
@@ -192,4 +196,15 @@ public static Index index(Class<?>... classes) throws IOException {
192196
return indexer.complete();
193197
}
194198

199+
private static AnnotationInstance stringBuilderTemplateData() {
200+
AnnotationValue ignoreValue = AnnotationValue.createArrayValue(ValueResolverGenerator.IGNORE, new AnnotationValue[] {});
201+
AnnotationValue targetValue = AnnotationValue.createClassValue("target",
202+
Type.create(ValueResolverGenerator.TEMPLATE_DATA, Kind.CLASS));
203+
AnnotationValue propertiesValue = AnnotationValue.createBooleanValue(ValueResolverGenerator.PROPERTIES, false);
204+
AnnotationValue ignoreSuperclassesValue = AnnotationValue.createBooleanValue(ValueResolverGenerator.IGNORE_SUPERCLASSES,
205+
true);
206+
return AnnotationInstance.create(ValueResolverGenerator.TEMPLATE_DATA, null,
207+
new AnnotationValue[] { targetValue, ignoreValue, propertiesValue, ignoreSuperclassesValue });
208+
}
209+
195210
}

0 commit comments

Comments
 (0)