Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.7.0 backports 1 #38354

Merged
merged 42 commits into from
Jan 24, 2024
Merged
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
87859df
Make sure we can do a GET with a CSRF token cookie and still obtain t…
FroMage Jan 9, 2024
9e64ae4
Make the route build item truly final
cescoffier Jan 17, 2024
c7a6e11
Recommend quarkus.jib.jvm-additional-arguments rather than quarkus.ji…
yrodiere Jan 17, 2024
c5d69eb
Improve locales IT
geoand Jan 17, 2024
add5596
Document how Keycloak Admin Client and Dev Service can use the same p…
sberyozkin Jan 17, 2024
b9c0b45
Javadoc edits for quarkus-oidc.adoc
rolfedh Jan 17, 2024
1069081
Qute: fix UserTagSectionHelper.Arguments.asHtmlAttributes()
mkouba Jan 17, 2024
d239f57
Align startup exception wrapping in Hibernate Reactive on Hibernate ORM
yrodiere Jan 10, 2024
91243eb
Add runtime configuration property quarkus.datasource.active
yrodiere Dec 7, 2023
059f9c3
Test runtime configuration property quarkus.datasource.active
yrodiere Jan 9, 2024
c4dee66
Fixed deprecation warnings for Gradle 9 in QuarkusPlugin
ribafish Jan 17, 2024
9768101
Update qute-reference.adoc
Anton-Vasilev Jan 17, 2024
94b6044
Bump org.mockito:mockito-core from 5.8.0 to 5.9.0
dependabot[bot] Jan 17, 2024
202306e
Bump resteasy-microprofile.version from 2.1.4.Final to 2.1.5.Final
dependabot[bot] Jan 17, 2024
ad10922
Skip test truststores creation with -Dquickly*
gsmet Jan 18, 2024
720a359
Make RoutingContext available during SecurityIdentity augmentation
michalvavrik Jan 18, 2024
917bf46
Improve the names of the locales IT modules
geoand Jan 18, 2024
5dafde7
Actually make the library usable by Quarkus applications
geoand Jan 18, 2024
e3ec37f
Make sure the Locales IT modules are always included by GIB
geoand Jan 18, 2024
2d9dc10
Upgrade to Hibernate ORM 6.4.2.Final
yrodiere Jan 18, 2024
06fba9f
ArC: consolidate handling of transitive interceptor bindings
Ladicek Jan 11, 2024
eeed96e
Bump Keycloak version to 23.0.4
sberyozkin Jan 18, 2024
a531cd0
Migrate Security WebAuth guide to Hiberante ORM
michalvavrik Jan 19, 2024
52a480f
DevUI: Show source editor when config file is empty
gastaldi Jan 19, 2024
a31df37
Bump io.smallrye.reactive:mutiny-bom from 2.5.3 to 2.5.4
dependabot[bot] Jan 19, 2024
d44ae03
Upgrade to Mutiny 2.5.4
jponge Jan 19, 2024
a4ae5ae
Remove wrong LGPL headers within some classes of the Hibernate ORM ex…
Sanne Jan 19, 2024
fdce564
fix entity-manager retrieval in spring-data-repos
fladdimir Jan 22, 2024
bb81d01
Qute: improvements and fixes of UserTagSectionHelper.Arguments
mkouba Jan 18, 2024
6b170c4
Fixes #38247 incorrect rel=self web link
bpasson Jan 18, 2024
9074dfc
Update text
bpasson Jan 18, 2024
7015328
Update text
bpasson Jan 18, 2024
9943301
Solved review comments
bpasson Jan 18, 2024
e59221d
Support using tars and docker and daemon as base image for Jib
geoand Jan 22, 2024
b36c9d8
Upgrade sshd from 2.10.0 to 2.12.0
rsvoboda Jan 22, 2024
d828a24
Improve datasource tracing
brunobat Jan 23, 2024
8bd951a
fix: bump dockerfiles and api server url in integration tests
iocanel Jan 23, 2024
02ea779
Fix command line arguments being squashed
MaciejDromin Jan 5, 2024
ccc23f4
Fix quarkus dev broken for command mode arguments
MaciejDromin Dec 29, 2023
be0ecce
Minor adjustments
MaciejDromin Dec 30, 2023
da170ce
Unblock SmallRye Health exposed routes
xstefank Jan 10, 2024
f9c960a
Bump to Netty 4.1.106.Final
cescoffier Jan 23, 2024
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
Prev Previous commit
Next Next commit
Qute: improvements and fixes of UserTagSectionHelper.Arguments
- always skip the artificial "it" arg
- strip quotes from the defaulted key for a single word string literal value-only argument; {#tag "foo"} becomes {#tag foo="foo"}
- add Arguments#skipIdenticalKeyValue(),  Arguments#filterIdenticalKeyValue() and Arguments#skipIt()
- resolves #38280

(cherry picked from commit 4bd0b0b)
mkouba authored and gsmet committed Jan 23, 2024
commit bb81d018af77ef79ec5736817acc237a828982cb
22 changes: 15 additions & 7 deletions docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
@@ -1152,18 +1152,26 @@ In this case, just add `_isolated=false` or `_unisolated` argument to the call s

===== Arguments

Named arguments can be accessed directly in a tag template.
The first argument does not have to define a name but can be accessed using the `it` alias.
Furthermore, arguments metadata are accessible in a tag using the `_args` alias.
Named arguments can be accessed directly in the tag template.
However, the first argument does not need to define a name and it can be accessed using the `it` alias.
Furthermore, if an argument does not have a name defined and the value is a single identifier, such as `foo`, then the name is defaulted to the value identifier, e.g. `{#myTag foo /}` becomes `{#myTag foo=foo /}`.
In other words, the argument value `foo` is resolved and can be accessed using `{foo}` in the tag template.

NOTE: If an argument does not have a name and the value is a single word string literal , such as `"foo"`, then the name is defaulted and quotation marks are removed, e.g. `{#myTag "foo" /}` becomes `{#myTag foo="foo" /}`.

`io.quarkus.qute.UserTagSectionHelper.Arguments` metadata are accessible in a tag using the `_args` alias.

* `_args.size` - returns the actual number of arguments passed to a tag
* `_args.empty` - returns `true` if no arguments are passed
* `_args.get(String name)` - returns the argument value of the given name
* `_args.empty`/`_args.isEmpty` - returns `true` if no arguments are passed
* `_args.get(String name)` - returns the argument value of the given name or `null`
* `_args.filter(String...)` - returns the arguments matching the given names
* `_args.filterIdenticalKeyValue` - returns the arguments with the name equal to the value; typically `foo` from `{#test foo="foo" bar=true}` or `{#test "foo" bar=true /}`
* `_args.skip(String...)` - returns only the arguments that do not match the given names
* `_args.asHtmlAttributes` - renders the arguments as HTML attributes; e.g. `foo="true" bar="false"` (the arguments are sorted by name in alphabetical order)
* `_args.skipIdenticalKeyValue` - returns only the arguments with the name not equal to the value; typically `bar` from `{#test foo="foo" bar=true /}`
* `_args.skipIt` - returns all arguments except for the first unnamed argument; typically `bar` from `{#test foo bar=true /}`
* `_args.asHtmlAttributes` - renders the arguments as HTML attributes; e.g. `foo="true" readonly="readonly"`; the arguments are sorted by name in alphabetical order and the `'`, `"`, `<`, `>`, `&` characters are escaped

`_args` is also iterable: `{#each _args}{it.key}={it.value}{/each}`.
`_args` is also iterable of `java.util.Map.Entry`: `{#each _args}{it.key}={it.value}{/each}`.

For example, we can call the user tag defined below with `{#test 'Martin' readonly=true /}`.

Original file line number Diff line number Diff line change
@@ -7,7 +7,9 @@
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.regex.Pattern;

import io.quarkus.qute.Template.Fragment;

@@ -110,7 +112,7 @@ public MissingEndTagStrategy missingEndTagStrategy() {
}

@Override
protected boolean ignoreParameterInit(String key, String value) {
protected boolean ignoreParameterInit(Supplier<String> firstParamSupplier, String key, String value) {
return key.equals(TEMPLATE)
// {#include foo _isolated=true /}
|| key.equals(ISOLATED)
@@ -147,6 +149,7 @@ static abstract class AbstractIncludeFactory<T extends SectionHelper> implements
static final String ISOLATED = "_isolated";
static final String UNISOLATED = "_unisolated";
static final String IGNORE_FRAGMENTS = "_ignoreFragments";
static final Pattern WHITESPACE = Pattern.compile("\\s");

@Override
public boolean treatUnknownSectionsAsBlocks() {
@@ -161,6 +164,7 @@ void addDefaultParams(ParametersInfo.Builder builder) {
builder
.addParameter(Parameter.builder(ISOLATED).defaultValue(isolatedDefaultValue()).optional()
.valuePredicate(ISOLATED::equals).build())
.addParameter(Parameter.builder(UNISOLATED).optional().valuePredicate(UNISOLATED::equals).build())
.addParameter(Parameter.builder(IGNORE_FRAGMENTS).defaultValue(Boolean.FALSE.toString()).optional()
.valuePredicate(IGNORE_FRAGMENTS::equals).build())
.build();
@@ -172,13 +176,7 @@ public Scope initializeBlock(Scope outerScope, BlockInfo block) {
for (Entry<String, String> entry : block.getParameters().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (ignoreParameterInit(key, value)) {
continue;
} else if (useDefaultedKey(key, value)) {
// As "order" in {#include foo order /}
key = value;
}
block.addExpression(key, value);
handleParam(key, value, () -> block.getParameter(0), (k, v) -> block.addExpression(k, v));
}
return outerScope;
} else {
@@ -228,7 +226,7 @@ public T initialize(SectionInitContext context) {
ignoreFragments = true;
continue;
}
handleParamInit(key, value, context, params);
handleParam(key, value, () -> context.getParameter(0), (k, v) -> params.put(k, context.getExpression(k)));
}
}

@@ -312,13 +310,19 @@ protected String getFragmentId(String templateId, SectionInitContext context) {
return null;
}

protected void handleParamInit(String key, String value, SectionInitContext context, Map<String, Expression> params) {
if (ignoreParameterInit(key, value)) {
protected void handleParam(String key, String value, Supplier<String> firstParamSupplier,
BiConsumer<String, String> paramConsumer) {
if (ignoreParameterInit(firstParamSupplier, key, value)) {
return;
} else if (useDefaultedKey(key, value)) {
key = value;
if (LiteralSupport.isStringLiteral(value)) {
// {#include "foo" /} => {#include foo="foo" /}
key = value.substring(1, value.length() - 1);
} else {
key = value;
}
}
params.put(key, context.getExpression(key));
paramConsumer.accept(key, value);
}

protected boolean useDefaultedKey(String key, String value) {
@@ -331,10 +335,10 @@ protected boolean useDefaultedKey(String key, String value) {
}

protected boolean isSinglePart(String value) {
return Expressions.splitParts(value).size() == 1;
return Expressions.splitParts(value).size() == 1 && !WHITESPACE.matcher(value).find();
}

protected boolean ignoreParameterInit(String key, String value) {
protected boolean ignoreParameterInit(Supplier<String> firstParamSupplier, String key, String value) {
return key.equals(IGNORE_FRAGMENTS);
}

Original file line number Diff line number Diff line change
@@ -23,8 +23,7 @@ static Object getLiteralValue(String literal) {
if (literal == null || literal.isEmpty()) {
return value;
}
char firstChar = literal.charAt(0);
if (isStringLiteralSeparator(firstChar) && literal.charAt(literal.length() - 1) == firstChar) {
if (isStringLiteral(literal)) {
value = literal.substring(1, literal.length() - 1);
} else if (literal.equals("true")) {
value = Boolean.TRUE;
@@ -33,6 +32,7 @@ static Object getLiteralValue(String literal) {
} else if (literal.equals("null")) {
value = null;
} else {
char firstChar = literal.charAt(0);
if (Character.isDigit(firstChar) || firstChar == '-' || firstChar == '+') {
if (INTEGER_LITERAL_PATTERN.matcher(literal).matches()) {
try {
@@ -77,4 +77,12 @@ static boolean isStringLiteralSeparator(char character) {
return character == '"' || character == '\'';
}

static boolean isStringLiteral(String value) {
if (value == null || value.isEmpty()) {
return false;
}
char firstChar = value.charAt(0);
return isStringLiteralSeparator(firstChar) && value.charAt(value.length() - 1) == firstChar;
}

}
Original file line number Diff line number Diff line change
@@ -707,42 +707,24 @@ private void processParams(String tag, String label, Iterator<String> iter, Sect
LOGGER.debugf(builder.toString());
}

List<String> parametersPositions = new ArrayList<>(paramValues.size());

// Process named params first
for (Iterator<String> it = paramValues.iterator(); it.hasNext();) {
String param = it.next();
for (String param : paramValues) {
int equalsPosition = getFirstDeterminingEqualsCharPosition(param);
if (equalsPosition != -1) {
// Named param
params.put(param.substring(0, equalsPosition), param.substring(equalsPosition + 1,
param.length()));
it.remove();
String val = param.substring(equalsPosition + 1, param.length());
params.put(param.substring(0, equalsPosition), val);
parametersPositions.add(val);
} else {
parametersPositions.add(null);
}
}

Predicate<String> included = params::containsKey;
// Then process positional params
if (actualSize < factoryParams.size()) {
// The number of actual params is less than factory params
// We need to choose the best fit for positional params
for (String param : paramValues) {
Parameter found = findFactoryParameter(param, factoryParams, included, true);
if (found != null) {
params.put(found.name, param);
}
}
} else {
// The number of actual params is greater or equals to factory params
int generatedIdx = 0;
for (String param : paramValues) {
// Positional param
Parameter found = findFactoryParameter(param, factoryParams, included, false);
if (found != null) {
params.put(found.name, param);
} else {
params.put("" + generatedIdx++, param);
}
}
}
// When the number of actual params is less than factory params then we need to choose the best fit for positional params
processPositionalParams(paramValues, parametersPositions, factoryParams, params, actualSize < factoryParams.size());

// Use the default values if needed
for (Parameter param : factoryParams) {
@@ -767,8 +749,26 @@ private void processParams(String tag, String label, Iterator<String> iter, Sect
.build();
}

for (Entry<String, String> e : params.entrySet()) {
block.addParameter(e.getKey(), e.getValue());
params.entrySet().forEach(block::addParameter);
block.setParametersPositions(parametersPositions);
}

private void processPositionalParams(List<String> paramValues, List<String> parametersPositions,
List<Parameter> factoryParams, Map<String, String> params, boolean noDefaultValueTakesPrecedence) {
int generatedIdx = 0;
int idx = 0;
Predicate<String> included = params::containsKey;
for (String param : paramValues) {
if (parametersPositions.isEmpty() || parametersPositions.get(idx) == null) {
Parameter found = findFactoryParameter(param, factoryParams, included, noDefaultValueTakesPrecedence);
if (found != null) {
params.put(found.name, param);
} else {
params.put("" + generatedIdx++, param);
}
parametersPositions.set(idx, param);
}
idx++;
}
}

Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;

@@ -29,6 +30,10 @@ public final class SectionBlock implements WithOrigin, ErrorInitializer {
public final String label;
/**
* An unmodifiable ordered map of parsed parameters.
* <p>
* Note that the order does not necessary reflect the original positions of the parameters but the parsing order.
*
* @see SectionHelperFactory#getParameters()
*/
public final Map<String, String> parameters;

@@ -42,21 +47,33 @@ public final class SectionBlock implements WithOrigin, ErrorInitializer {
*/
List<TemplateNode> nodes;

private final List<String> positionalParameters;

public SectionBlock(Origin origin, String id, String label, Map<String, String> parameters,
Map<String, Expression> expressions,
List<TemplateNode> nodes) {
List<TemplateNode> nodes, List<String> positionalParameters) {
this.origin = origin;
this.id = id;
this.label = label;
this.parameters = parameters;
this.expressions = expressions;
this.nodes = ImmutableList.copyOf(nodes);
this.positionalParameters = positionalParameters;
}

public boolean isEmpty() {
return nodes.isEmpty();
}

/**
*
* @param position
* @return the parameter for the specified position, or {@code null} if no such parameter exists
*/
public String getParameter(int position) {
return positionalParameters.get(position);
}

List<Expression> getExpressions() {
List<Expression> expressions = new ArrayList<>();
expressions.addAll(this.expressions.values());
@@ -230,6 +247,7 @@ static class Builder implements BlockInfo {
private Origin origin;
private String label;
private Map<String, String> parameters;
private List<String> parametersPositions = List.of();
private final List<TemplateNode> nodes;
private Map<String, Expression> expressions;
private final Parser parser;
@@ -257,11 +275,16 @@ SectionBlock.Builder setLabel(String label) {
return this;
}

SectionBlock.Builder addParameter(String name, String value) {
SectionBlock.Builder addParameter(Entry<String, String> entry) {
if (parameters == null) {
parameters = new LinkedHashMap<>();
}
parameters.put(name, value);
parameters.put(entry.getKey(), entry.getValue());
return this;
}

SectionBlock.Builder setParametersPositions(List<String> parametersPositions) {
this.parametersPositions = Collections.unmodifiableList(parametersPositions);
return this;
}

@@ -279,6 +302,11 @@ public Map<String, String> getParameters() {
return parameters == null ? Collections.emptyMap() : Collections.unmodifiableMap(parameters);
}

@Override
public String getParameter(int position) {
return parametersPositions.get(position);
}

public String getLabel() {
return label;
}
@@ -310,7 +338,7 @@ SectionBlock build() {
} else {
expressions = Collections.unmodifiableMap(expressions);
}
return new SectionBlock(origin, id, label, parameters, expressions, nodes);
return new SectionBlock(origin, id, label, parameters, expressions, nodes, parametersPositions);
}
}

Original file line number Diff line number Diff line change
@@ -159,9 +159,12 @@ interface BlockInfo extends ParserDelegate, WithOrigin {
String getLabel();

/**
* Undeclared params with default values are included.
* An unmodifiable ordered map of parsed parameters.
* <p>
* Note that the order does not necessary reflect the original positions of the parameters but the parsing order.
*
* @return the map of parameters
* @see SectionHelperFactory#getParameters()
*/
Map<String, String> getParameters();

@@ -173,6 +176,14 @@ default boolean hasParameter(String name) {
return getParameters().containsKey(name);
}

/**
*
* @param position
* @return the parameter for the specified position, or {@code null} if no such parameter exists
* @see SectionBlock#getParameter(int)
*/
String getParameter(int position);

/**
* Parse and register an expression for the specified parameter.
* <p>
@@ -197,6 +208,7 @@ public interface SectionInitContext extends ParserDelegate {
/**
*
* @return the parameters of the main block
* @see SectionBlock#parameters
*/
default public Map<String, String> getParameters() {
return getBlocks().get(0).parameters;
@@ -219,6 +231,15 @@ default public String getParameter(String name) {
return getParameters().get(name);
}

/**
*
* @return the parameter for the specified position
* @see SectionBlock#getParameter(int)
*/
default public String getParameter(int position) {
return getBlocks().get(0).getParameter(position);
}

/**
*
* @param name
Loading