Skip to content

Commit

Permalink
Merge pull request quarkusio#11244 from mkouba/bracket-notation
Browse files Browse the repository at this point in the history
Qute: improve bracket notation support, add docs
  • Loading branch information
mkouba committed Aug 6, 2020
2 parents d68036d + 7d2d963 commit 9d2d55f
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 174 deletions.
79 changes: 58 additions & 21 deletions docs/src/main/asciidoc/qute-reference.adoc
Expand Up @@ -184,35 +184,27 @@ In this case, all whitespace characters from a standalone line will be printed t
[[expressions]]
==== Expressions

An expression consists of:

* an optional namespace followed by a colon (`:`),
* one or more parts separated by dot (`.`).

The first part of the expression is always resolved against the <<current_context_object, current context object>>.
If no result is found for the first part it's resolved against the parent context object (if available).
For an expression that starts with a namespace the current context object is found using all the available ``NamespaceResolver``s.
For an expression that does not start with a namespace the current context object is *derived from the position* of the tag.
All other parts are resolved using ``ValueResolver``s against the result of the previous resolution.

For example, expression `{name}` has no namespace and single part - `name`.
The "name" will be resolved using all available value resolvers against the current context object.
However, the expression `{global:colors}` has the namespace `global` and single part - `colors`.
First, all available ``NamespaceResolver``s will be used to find the current context object.
And afterwards value resolvers will be used to resolve "colors" against the context object found.
An expression outputs a value.
It consists of one or more parts separated by dot (dot notation) or square brackets (bracket notation).
In the `object[property_name]` syntax, the `property_name` has to be a non-null <<literals, literal>> value.
An expression could start with an optional namespace followed by a colon (`:`).

.Expressions Example
[source]
----
{name} <1>
{item.name} <2>
{global:colors} <3>
{item['name']} <3>
{global:colors} <4>
----
<1> no namespace, one part -`name`
<2> no namespace, two parts - `item`, `name`
<3> namespace `global`, one part - `colors`
<1> no namespace, one part: `name`
<2> no namespace, two parts: `item`, `name`
<3> equivalent to `{item.name}` but using the bracket notation
<4> namespace `global`, one part: `colors`

An expression part could be a "virtual method" in which case the name can be followed by a list of comma-separated parameters in parentheses:
A part of an expression could be a _virtual method_ in which case the name can be followed by a list of comma-separated parameters in parentheses:

.Virtual Method Example
[source]
----
{item.getLabels(1)} <1>
Expand All @@ -221,6 +213,51 @@ An expression part could be a "virtual method" in which case the name can be fol
<1> no namespace, two parts - `item`, `getLabels(1)`, the second part is a virtual method with name `getLabels` and params `1`
<2> infix notation, translated to `name.or('John')`; no namespace, two parts - `name`, `or('John')`

NOTE: A parameter of a virtual method can be a <<literals, literal>> value.

[[literals]]
===== Supported Literals

|===
|Literal |Examples

|boolean
|`true`, `false`

|null
|`null`

|string
|`'value'`, `"string"`

|integer
|`1`, `-5`

|long
|`1l`, `-5L`

|double
|`1D`, `-5d`

|float
|`1f`, `-5F`

|===

===== Resolution

The first part of the expression is always resolved against the <<current_context_object, current context object>>.
If no result is found for the first part it's resolved against the parent context object (if available).
For an expression that starts with a namespace the current context object is found using all the available ``NamespaceResolver``s.
For an expression that does not start with a namespace the current context object is *derived from the position* of the tag.
All other parts are resolved using ``ValueResolver``s against the result of the previous resolution.

For example, expression `{name}` has no namespace and single part - `name`.
The "name" will be resolved using all available value resolvers against the current context object.
However, the expression `{global:colors}` has the namespace `global` and single part - `colors`.
First, all available ``NamespaceResolver``s will be used to find the current context object.
And afterwards value resolvers will be used to resolve "colors" against the context object found.

[[current_context_object]]
===== Current Context

Expand Down
Expand Up @@ -659,8 +659,15 @@ public String apply(String id) {
Map<ClassInfo, Boolean> implicitClassToMethodUsed = new HashMap<>();

for (Expression expression : injectExpressions) {

String beanName = expression.getParts().get(0).getName();
Expression.Part firstPart = expression.getParts().get(0);
if (firstPart.isVirtualMethod()) {
incorrectExpressions.produce(new IncorrectExpressionBuildItem(expression.toOriginalString(),
"The inject: namespace must be followed by a bean name",
expression.getOrigin().getLine(),
expression.getOrigin().getTemplateGeneratedId()));
continue;
}
String beanName = firstPart.getName();
BeanInfo bean = namedBeans.get(beanName);
if (bean != null) {
if (expression.getParts().size() == 1) {
Expand Down
Expand Up @@ -25,4 +25,7 @@ public interface AppMessages {
@Message("Item name: {item.name}, age: {item.age}")
String itemDetail(Item item);

@Message(key = "dot.test", value = "Dot test!")
String dotTest();

}
Expand Up @@ -69,6 +69,7 @@ public void testResolvers() {
foo.instance().setAttribute(MessageBundles.ATTRIBUTE_LOCALE, Locale.forLanguageTag("cs")).render());
assertEquals("Hallo Welt! Hallo Jachym! Hello you guys! Hello alpha! Hello!",
foo.instance().setAttribute(MessageBundles.ATTRIBUTE_LOCALE, Locale.GERMAN).render());
assertEquals("Dot test!", engine.parse("{msg:['dot.test']}").render());
}

}
Expand Up @@ -6,7 +6,7 @@
/**
* Evaluation context of a specific part of an expression.
*
* @see Expression
* @see Expression.Part
*/
public interface EvalContext {

Expand Down
Expand Up @@ -72,7 +72,7 @@ interface Part {

/**
*
* @return the name of the property or virtual method
* @return the name of a property or virtual method
*/
String getName();

Expand Down

0 comments on commit 9d2d55f

Please sign in to comment.