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

Qute - loop section - introduce the {#else} block #18127

Merged
merged 1 commit into from
Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 19 additions & 6 deletions docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -518,19 +518,19 @@ A section helper that defines the logic of a section can "execute" any of the bl
[[loop_section]]
==== Loop Section

The loop section makes it possible to iterate over an instance of `Iterable`, `Map` 's entry set, `Stream` and an `Integer`.
The loop section makes it possible to iterate over an instance of `Iterable`, `Iterator`, array, `Map` 's entry set, `Stream` and an `Integer`.
It has two flavors.
The first one is using the `each` name alias.
The first one is using the `each` name and `it` is an implicit alias for the iteration element.

[source]
----
{#each items}
{it.name} <1>
{/each}
----
<1> `it` is an implicit alias. `name` is resolved against the current iteration element.
<1> `name` is resolved against the current iteration element.

The other form is using the `for` name alias and can specify the alias used to reference the iteration element:
The other form is using the `for` name and can specify the alias used to reference the iteration element:

[source]
----
Expand All @@ -547,7 +547,9 @@ It's also possible to access the iteration metadata inside the loop:
{count}. {it.name} <1>
{/each}
----
<1> `count` represents one-based index. Metadata also include zero-based `index`, `hasNext`, `odd` and `even`.
<1> `count` represents one-based index.

TIP: The iteration metadata also include zero-based `index`, `hasNext`, `odd` and `even` properties.

The `for` statement also works with integers, starting from 1. In the example below, considering that `total = 3`:

Expand All @@ -558,13 +560,24 @@ The `for` statement also works with integers, starting from 1. In the example be
{/for}
----

The output will be:
And the output will be:

[source]
----
1:2:3:
----

A loop section may define the `{#else}` block that is executed when there are no items to iterate:

[source]
----
{#for item in items}
{item.name}
{#else}
No items.
{/for}
----

[[if_section]]
==== If Section

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import static io.quarkus.qute.Parameter.EMPTY;

import io.quarkus.qute.SectionHelperFactory.SectionInitContext;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand All @@ -20,13 +22,18 @@
public class LoopSectionHelper implements SectionHelper {

private static final String DEFAULT_ALIAS = "it";
private static final String ELSE = "else";
private static final String ALIAS = "alias";
private static final String ITERABLE = "iterable";

private final String alias;
private final Expression iterable;
private final SectionBlock elseBlock;

LoopSectionHelper(String alias, Expression iterable) {
this.alias = Parameter.EMPTY.equals(alias) ? DEFAULT_ALIAS : alias;
this.iterable = Objects.requireNonNull(iterable);
LoopSectionHelper(SectionInitContext context) {
this.alias = context.getParameterOrDefault(ALIAS, DEFAULT_ALIAS);
this.iterable = Objects.requireNonNull(context.getExpression(ITERABLE));
this.elseBlock = context.getBlock(ELSE);
}

@SuppressWarnings("unchecked")
Expand All @@ -47,7 +54,12 @@ public CompletionStage<ResultNode> resolve(SectionResolutionContext context) {
results.add(nextElement(iterator.next(), idx++, iterator.hasNext(), context));
}
if (results.isEmpty()) {
return ResultNode.NOOP;
// Execute the {#else} block if present
if (elseBlock != null) {
return context.execute(elseBlock, context.resolutionContext());
} else {
return ResultNode.NOOP;
}
}
if (results.size() == 1) {
return results.get(0);
Expand Down Expand Up @@ -129,9 +141,7 @@ public static class Factory implements SectionHelperFactory<LoopSectionHelper> {

public static final String HINT_ELEMENT = "<loop-element>";
public static final String HINT_PREFIX = "<loop#";
private static final String ALIAS = "alias";
private static final String IN = "in";
private static final String ITERABLE = "iterable";

@Override
public List<String> getDefaultAliases() {
Expand All @@ -147,9 +157,13 @@ public ParametersInfo getParameters() {
.build();
}

public List<String> getBlockLabels() {
return Collections.singletonList(ELSE);
}

@Override
public LoopSectionHelper initialize(SectionInitContext context) {
return new LoopSectionHelper(context.getParameter(ALIAS), context.getExpression(ITERABLE));
return new LoopSectionHelper(context);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,34 @@ default public Map<String, String> getParameters() {
return getBlocks().get(0).parameters;
}

/**
*
* @return {@code true} if the main block declares a parameter of the given name
*/
default public boolean hasParameter(String name) {
return getParameters().containsKey(name);
}

/**
*
* @return the parameter, or null/{@link Parameter.EMPTY} if the main block does not declare a parameter of the given
* name
*/
default public String getParameter(String name) {
return getParameters().get(name);
}

/**
*
* @param name
* @param defaultValue
* @return the param or the default value if not specified
*/
default public String getParameterOrDefault(String name, String defaultValue) {
String param = getParameter(name);
return param == null || Parameter.EMPTY.equals(param) ? defaultValue : param;
}

/**
* Note that the expression must be registered in the {@link SectionHelperFactory#initializeBlock(Scope, BlockInfo)}
* first.
Expand All @@ -144,6 +164,20 @@ default public String getParameter(String name) {

public List<SectionBlock> getBlocks();

/**
*
* @param label
* @return the first block with the given label, or {code null} if no such exists
*/
default SectionBlock getBlock(String label) {
for (SectionBlock block : getBlocks()) {
if (label.equals(block.label)) {
return block;
}
}
return null;
}

public Engine getEngine();

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -184,4 +185,11 @@ void testScope() {
assertTrue(result.contains("hellllllo"), result);
}

@Test
public void testElseBlock() {
Engine engine = Engine.builder().addDefaults().build();
assertEquals("No items.",
engine.parse("{#for i in items}{item}{#else}No items.{/for}").data("items", Collections.emptyList()).render());
}

}