diff --git a/pom.xml b/pom.xml
index d725b50..b023484 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,7 @@
1.13.0
2.0.0-SNAPSHOT
- 1.5.0-SNAPSHOT
+ 1.5.0
4.13.1
@@ -221,6 +221,15 @@
org.apache.maven.plugins
maven-javadoc-plugin
${version.javadoc.plugin}
+
+ all,-missing
+
+ **/EfxLexer.java
+ **/EfxParser.java
+ **/EfxListener.java
+ **/EfxBaseListener.java
+
+
org.apache.maven.plugins
diff --git a/src/main/java/eu/europa/ted/efx/interfaces/MarkupGenerator.java b/src/main/java/eu/europa/ted/efx/interfaces/MarkupGenerator.java
index 7c2c4bd..b32d9fb 100644
--- a/src/main/java/eu/europa/ted/efx/interfaces/MarkupGenerator.java
+++ b/src/main/java/eu/europa/ted/efx/interfaces/MarkupGenerator.java
@@ -38,35 +38,40 @@
*/
public interface MarkupGenerator {
+
/**
* Given a body (main content) and a set of fragments, this method returns the
- * full content of the target template file.
+ * full content of the
+ * target template file.
*
- * @deprecated This method is deprecated and will be removed in future versions.
- * Use {@link #composeOutputFile(List, List, List)} instead.
- * We are keeping the method temporarily to prevent build errors.
- * This method is being deprecated as of version 2.0.0-alpha.4 and
- * will be removed before version 2.0.0 is released
- *
- * @param content the body (main content) of the template.
+ * @param globals the global variables and functions to be included in the
+ * template file.
+ * @param mainSection the main section (body) of the template.
+ * @param summarySection the summary section of the template.
+ * @param navigationSection the navigation section of the template.
* @param fragments the fragments to be included in the template file.
* @return the full content of the target template file.
*/
- @Deprecated(since = "2.0.0-alpha.4", forRemoval = true)
- Markup composeOutputFile(final List content, final List fragments);
+ Markup composeOutputFile(List globals, final List mainSection, final List summarySection, final List navigationSection, final List fragments);
/**
* Given a body (main content) and a set of fragments, this method returns the
- * full content of the
- * target template file.
+ * full content of the target template file.
*
- * @param globals the global variables and functions to be included in the
- * template file.
+ * @deprecated This method is deprecated and will be removed in future versions.
+ * Use {@link #composeOutputFile(List, List, List, List, List)} instead.
+ * We are keeping the method temporarily to prevent build errors.
+ * This method is being deprecated as of version 2.0.0-alpha.6 and
+ * will be removed before version 2.0.0 is released
+ *
* @param content the body (main content) of the template.
* @param fragments the fragments to be included in the template file.
* @return the full content of the target template file.
*/
- Markup composeOutputFile(List globals, final List content, final List fragments);
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup composeOutputFile(final List content, final List fragments) {
+ return this.composeOutputFile(List.of(), content, List.of(), List.of(), fragments);
+ }
/**
* Renders the markup necessary to declare and initialize the variable making it
@@ -116,10 +121,16 @@ public interface MarkupGenerator {
* template.
*
* @param variableExpression the expression to be evaluated and rendered.
+ * @param translatorContext additional context information provided by the template translator.
* @return the template code that dereferences the expression in the target
* template.
*/
- Markup renderVariableExpression(final Expression variableExpression);
+ Markup renderVariableExpression(final Expression variableExpression, final TranslatorContext translatorContext);
+
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup renderVariableExpression(final Expression variableExpression) {
+ return this.renderVariableExpression(variableExpression, TranslatorContext.DEFAULT);
+ }
/**
* Given a label key (which will eventually, at runtime, be dereferenced to a
@@ -127,11 +138,18 @@ public interface MarkupGenerator {
* method returns the template code that renders this label in the target
* template language.
*
- * @param key the label key to be dereferenced.
- * @return the template code that renders the label in the target template
- * language.
+ * @param key the label key to be dereferenced.
+ * @param translatorContext additional context information provided by the template
+ * translator.
+ * @return the template code that renders the label in the target template
+ * language.
*/
- Markup renderLabelFromKey(final StringExpression key);
+ Markup renderLabelFromKey(final StringExpression key, TranslatorContext translatorContext);
+
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup renderLabelFromKey(final StringExpression key) {
+ return this.renderLabelFromKey(key, TranslatorContext.DEFAULT);
+ }
/**
* Given a label key (which will eventually, at runtime, be dereferenced to a
@@ -142,10 +160,16 @@ public interface MarkupGenerator {
* @param key the label key to be dereferenced.
* @param quantity a numeric quantity used to decide if the label needs to be
* pluralized.
+ * @param translatorContext additional context information provided by the template translator.
* @return the template code that renders the label in the target template
* language.
*/
- Markup renderLabelFromKey(final StringExpression key, final NumericExpression quantity);
+ Markup renderLabelFromKey(final StringExpression key, final NumericExpression quantity, TranslatorContext translatorContext);
+
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup renderLabelFromKey(final StringExpression key, final NumericExpression quantity) {
+ return this.renderLabelFromKey(key, quantity, TranslatorContext.DEFAULT);
+ }
/**
* Given an expression (which will eventually, at runtime, be evaluated to a
@@ -155,10 +179,16 @@ public interface MarkupGenerator {
* this label in the target template language.
*
* @param expression the expression that returns the label key.
+ * @param translatorContext additional context information provided by the template translator.
* @return the template code that renders the label in the target template
* language.
*/
- Markup renderLabelFromExpression(final Expression expression);
+ Markup renderLabelFromExpression(final Expression expression, TranslatorContext translatorContext);
+
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup renderLabelFromExpression(final Expression expression) {
+ return this.renderLabelFromExpression(expression, TranslatorContext.DEFAULT);
+ }
/**
* Given an expression (which will eventually, at runtime, be evaluated to a
@@ -170,10 +200,16 @@ public interface MarkupGenerator {
* @param expression the expression that returns the label key.
* @param quantity a numeric quantity used to decide if the label needs to be
* pluralized.
+ * @param translatorContext additional context information provided by the template translator.
* @return the template code that renders the label in the target template
* language.
*/
- Markup renderLabelFromExpression(final Expression expression, final NumericExpression quantity);
+ Markup renderLabelFromExpression(final Expression expression, final NumericExpression quantity, TranslatorContext translatorContext);
+
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup renderLabelFromExpression(final Expression expression, final NumericExpression quantity) {
+ return this.renderLabelFromExpression(expression, quantity, TranslatorContext.DEFAULT);
+ }
/**
* Given a string of free text, this method returns the template code that adds
@@ -183,9 +219,27 @@ public interface MarkupGenerator {
* {@link #escapeSpecialCharacters(String)} and then pass the escaped text to this method.
*
* @param freeText the free text to be rendered.
+ * @param translatorContext additional context information provided by the template translator.
* @return the template code that adds this text in the target template.
*/
- Markup renderFreeText(final String freeText);
+ Markup renderFreeText(final String freeText, TranslatorContext translatorContext);
+
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup renderFreeText(final String freeText) {
+ return this.renderFreeText(freeText, TranslatorContext.DEFAULT);
+ }
+
+ /**
+ * Given a label and a URL, this method returns the template code that renders
+ * a hyperlink in the target template language.
+ *
+ * @param label the label to be displayed for the hyperlink.
+ * @param url the URL to be linked to.
+ * @param translatorContext additional context information provided by the template translator.
+ * @return the template code that renders the hyperlink in the target template language.
+ */
+ Markup renderHyperlink(final Markup label, final StringExpression url, TranslatorContext translatorContext);
+
/**
* Returns the markup that represents a line break in the target template.
@@ -196,7 +250,12 @@ public interface MarkupGenerator {
*
* @return the markup that represents a line break in the target template.
*/
- Markup renderLineBreak();
+ Markup renderLineBreak(TranslatorContext translatorContext);
+
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup renderLineBreak() {
+ return this.renderLineBreak(TranslatorContext.DEFAULT);
+ }
/**
* Given a fragment name (identifier) and some pre-rendered content, this method
@@ -205,7 +264,7 @@ public interface MarkupGenerator {
* * @deprecated This method is deprecated and will be removed in future versions.
* Use {@link #composeFragmentDefinition(String, String, Set, Markup, Markup, Set)} instead.
* We are keeping the method temporarily to prevent build errors.
- * This method is being deprecated as of version 2.0.0-alpha.4 and
+ * This method is being deprecated as of version 2.0.0-alpha.6 and
* will be removed before version 2.0.0 is released
* The default implementation provided here only throws
* UnsupportedOperationException to prevent accidental use of this
@@ -217,7 +276,7 @@ public interface MarkupGenerator {
* @param parameters the parameters of the fragment.
* @return the code that encapsulates the fragment in the target template.
*/
- @Deprecated(since = "2.0.0-alpha.4", forRemoval = true)
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
default Markup composeFragmentDefinition(final String name, String number, Markup content,
Set parameters) {
throw new UnsupportedOperationException(
@@ -235,34 +294,38 @@ default Markup composeFragmentDefinition(final String name, String number, Marku
* @param content the content of the fragment.
* @param children the children of the fragment.
* @param parameters the parameters of the fragment.
+ * @param translatorContext additional context information provided by the template translator.
* @return the code that encapsulates the fragment in the target template.
*/
Markup composeFragmentDefinition(final String name, String number, Set conditionals, Markup content, Markup children,
- Set parameters);
+ Set parameters, TranslatorContext translatorContext);
+
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup composeFragmentDefinition(final String name, String number, Set conditionals,
+ Markup content, Markup children,
+ Set parameters) {
+ return composeFragmentDefinition(name, number, conditionals, content, children, parameters,
+ TranslatorContext.DEFAULT);
+ }
+
/**
* Given a fragment name (identifier), and an evaluation context, this method
* returns the code that invokes the fragment.
- * * @deprecated This method is deprecated and will be removed in future versions.
- * Use {@link #renderFragmentInvocation(String, PathExpression, Set)} instead.
- * We are keeping the method temporarily to prevent build errors.
- * This method is being deprecated as of version 2.0.0-alpha.4 and
- * will be removed before version 2.0.0 is released.
- * The default implementation provided here only throws
- * UnsupportedOperationException to prevent accidental use of this method.
- * . The EfxTemplateTranslator will not call this method.
- *
+ *
+ * The EfxTemplateTranslator will call this method to generate the
+ * fragment invocation code and then will pass the generated Markup to the
+ * {@link #renderContextLoop(PathExpression, Markup, Set)}.
+ *
+ * In EFX to XSLT translation, this method would generate the xsl:call-template
+ * that will invoke the fragment.
+ *
* @param name the name of the fragment.
- * @param context the context of the fragment.
- * @param variables the variables of the fragment.
+ * @param arguments the arguments of the fragment.
+ * @param translatorContext additional context information provided by the template translator.
* @return the code that invokes (uses) the fragment.
*/
- @Deprecated(since = "2.0.0-alpha.4", forRemoval = true)
- default Markup renderFragmentInvocation(final String name, final PathExpression context,
- final Set> variables) {
- throw new UnsupportedOperationException(
- "This method is deprecated and will be removed in future versions. Use renderFragmentInvocation(String, Set) instead.");
- }
+ Markup renderFragmentInvocation(final String name, final Set arguments, TranslatorContext translatorContext);
/**
* Given a fragment name (identifier), and an evaluation context, this method
@@ -275,16 +338,48 @@ default Markup renderFragmentInvocation(final String name, final PathExpression
* In EFX to XSLT translation, this method would generate the xsl:call-template
* that will invoke the fragment.
*
+ * @deprecated This method is deprecated and will be removed in future versions.
+ * Use {@link #renderFragmentInvocation(String, Set, TranslatorContext)}
+ * instead.
+ *
* @param name the name of the fragment.
* @param arguments the arguments of the fragment.
* @return the code that invokes (uses) the fragment.
*/
- Markup renderFragmentInvocation(final String name, final Set arguments);
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default public Markup renderFragmentInvocation(final String name, final Set arguments) {
+ return this.renderFragmentInvocation(name, arguments, TranslatorContext.DEFAULT);
+ }
+
+ /**
+ * Given a fragment name (identifier), and an evaluation context, this method
+ * returns the code that invokes the fragment.
+ * @deprecated This method is deprecated and will be removed in future versions.
+ * Use {@link #renderFragmentInvocation(String, PathExpression, Set)} instead.
+ * We are keeping the method temporarily to prevent build errors.
+ * This method is being deprecated as of version 2.0.0-alpha.6 and
+ * will be removed before version 2.0.0 is released.
+ * The default implementation provided here only throws
+ * UnsupportedOperationException to prevent accidental use of this method.
+ * . The EfxTemplateTranslator will not call this method.
+ *
+ * @param name the name of the fragment.
+ * @param context the context of the fragment.
+ * @param variables the variables of the fragment.
+ * @return the code that invokes (uses) the fragment.
+ */
+ @Deprecated(since = "2.0.0-alpha.6", forRemoval = true)
+ default Markup renderFragmentInvocation(final String name, final PathExpression context,
+ final Set> variables) {
+ throw new UnsupportedOperationException(
+ "This method is deprecated and will be removed in future versions. Use renderFragmentInvocation(String, Set) instead.");
+ }
+
/**
* Given an evaluation context, and some pre-rendered content, this method returns the code that
* iterates over the context and repeats the content for each item in the context.
- * As of version 2.0.0-alpha.4, the method composeFragmentInvocation(String, Set)
+ * As of version 2.0.0-alpha.6, the method composeFragmentInvocation(String, Set)
* has been split into two methods:
* 1. {@link #renderFragmentInvocation(String, Set)}
* for rendering the fragment invocation itself, which is the used by the
diff --git a/src/main/java/eu/europa/ted/efx/interfaces/TemplateSection.java b/src/main/java/eu/europa/ted/efx/interfaces/TemplateSection.java
new file mode 100644
index 0000000..8a38ed0
--- /dev/null
+++ b/src/main/java/eu/europa/ted/efx/interfaces/TemplateSection.java
@@ -0,0 +1,13 @@
+package eu.europa.ted.efx.interfaces;
+
+/**
+ * Enum representing different sections of a template.
+ *
+ * Used in the {@link TranslatorContext} to communicate the section of the template
+ * being processed to the {@link MarkupGenerator}.
+ */
+public enum TemplateSection {
+ DEFAULT,
+ SUMMARY,
+ NAVIGATION
+}
\ No newline at end of file
diff --git a/src/main/java/eu/europa/ted/efx/interfaces/TranslatorContext.java b/src/main/java/eu/europa/ted/efx/interfaces/TranslatorContext.java
new file mode 100644
index 0000000..49c7168
--- /dev/null
+++ b/src/main/java/eu/europa/ted/efx/interfaces/TranslatorContext.java
@@ -0,0 +1,36 @@
+package eu.europa.ted.efx.interfaces;
+
+/**
+ * Used to pass context information from the translator to the {@link MarkupGenerator}.
+ */
+public class TranslatorContext {
+
+ TemplateSection currentSection;
+
+ /**
+ * Gets the current section of the template being processed.
+ *
+ * @return the current section
+ */
+ public TemplateSection getCurrentSection() {
+ return currentSection;
+ }
+
+ public void setCurrentSection(TemplateSection currentSection) {
+ this.currentSection = currentSection;
+ }
+
+ /**
+ * Default instance of {@link TranslatorContext} with the section set to {@link TemplateSection#DEFAULT}.
+ * This is used throughout the code for EFX-1 processing where only the default section is relevant.
+ */
+ public static final TranslatorContext DEFAULT = new TranslatorContext();
+
+ public TranslatorContext() {
+ this(TemplateSection.DEFAULT);
+ }
+
+ public TranslatorContext(TemplateSection currentSection) {
+ this.currentSection = currentSection;
+ }
+}
diff --git a/src/main/java/eu/europa/ted/efx/model/Context.java b/src/main/java/eu/europa/ted/efx/model/Context.java
index b24d017..0403f2f 100644
--- a/src/main/java/eu/europa/ted/efx/model/Context.java
+++ b/src/main/java/eu/europa/ted/efx/model/Context.java
@@ -50,6 +50,10 @@ public NodeContext(final String nodeId, final PathExpression absolutePath,
super(nodeId, absolutePath, relativePath);
}
+ public NodeContext(final String nodeId, final PathExpression absolutePath, final Variable variable) {
+ super(nodeId, absolutePath, variable);
+ }
+
public NodeContext(final String nodeId, final PathExpression absolutePath) {
super(nodeId, absolutePath);
}
diff --git a/src/main/java/eu/europa/ted/efx/model/expressions/path/NodePathExpression.java b/src/main/java/eu/europa/ted/efx/model/expressions/path/NodePathExpression.java
index 9fe125d..b81591f 100644
--- a/src/main/java/eu/europa/ted/efx/model/expressions/path/NodePathExpression.java
+++ b/src/main/java/eu/europa/ted/efx/model/expressions/path/NodePathExpression.java
@@ -9,4 +9,8 @@ public class NodePathExpression extends PathExpression.Impl {
public NodePathExpression(final String script) {
super(script, EfxDataType.Node.class);
}
+
+ public static NodePathExpression empty() {
+ return new NodePathExpression("");
+ }
}
\ No newline at end of file
diff --git a/src/main/java/eu/europa/ted/efx/model/templates/ContentBlock.java b/src/main/java/eu/europa/ted/efx/model/templates/ContentBlock.java
index d664e31..3935793 100644
--- a/src/main/java/eu/europa/ted/efx/model/templates/ContentBlock.java
+++ b/src/main/java/eu/europa/ted/efx/model/templates/ContentBlock.java
@@ -14,6 +14,7 @@
import eu.europa.ted.efx.interfaces.Argument;
import eu.europa.ted.efx.interfaces.MarkupGenerator;
import eu.europa.ted.efx.interfaces.Parameter;
+import eu.europa.ted.efx.interfaces.TranslatorContext;
import eu.europa.ted.efx.model.Context;
import eu.europa.ted.efx.model.variables.ParsedArgument;
import eu.europa.ted.efx.model.variables.ParsedArguments;
@@ -34,9 +35,9 @@ public class ContentBlock {
protected final ParsedParameters parameters;
protected final ParsedArguments arguments;
- private ContentBlock() {
+ private ContentBlock(String id) {
this.parent = null;
- this.id = "block";
+ this.id = id;
this.indentationLevel = -1;
this.conditionals = new Conditionals();
this.content = new Markup("");
@@ -66,8 +67,8 @@ public ContentBlock(final ContentBlock parent, final String id, final int number
new ParsedArguments(variables));
}
- public static ContentBlock newRootBlock() {
- return new ContentBlock();
+ public static ContentBlock newRootBlock(String id) {
+ return new ContentBlock(id);
}
// #region Add Children
@@ -233,29 +234,29 @@ protected Set getAllArguments() {
// #region Render -----------------------------------------------------------
- public Markup renderChildren(MarkupGenerator markupGenerator) {
+ public Markup renderChildren(MarkupGenerator markupGenerator, TranslatorContext translatorContext) {
StringBuilder stringBuilder = new StringBuilder();
for (ContentBlock child : this.children) {
- stringBuilder.append('\n').append(child.renderInvocation(markupGenerator).script);
+ stringBuilder.append('\n').append(child.renderInvocation(markupGenerator, translatorContext).script);
}
return new Markup(stringBuilder.toString());
}
- public List renderDefinition(MarkupGenerator markupGenerator) {
+ public List renderDefinition(MarkupGenerator markupGenerator, TranslatorContext translatorContext) {
Set params = this.getAllParameters().stream()
.map(param -> new Parameter.Impl(param.name, markupGenerator.getEfxDataTypeEquivalent(param.dataType)))
.collect(Collectors.toCollection(LinkedHashSet::new));
List templates = new ArrayList<>();
templates.add(markupGenerator.composeFragmentDefinition(this.id, this.getOutlineNumber(),
this.conditionals.stream().collect(Collectors.toCollection(LinkedHashSet::new)),
- this.content, this.renderChildren(markupGenerator), params));
+ this.content, this.renderChildren(markupGenerator, translatorContext), params, translatorContext));
for (ContentBlock child : this.children) {
- templates.addAll(child.renderDefinition(markupGenerator));
+ templates.addAll(child.renderDefinition(markupGenerator, translatorContext));
}
return templates;
}
- public Markup renderInvocation(MarkupGenerator markupGenerator) {
+ public Markup renderInvocation(MarkupGenerator markupGenerator, TranslatorContext translatorContext) {
Set args = new LinkedHashSet<>();
if (this.parent != null) {
args.addAll(parent.getAllArguments().stream()
@@ -264,7 +265,7 @@ public Markup renderInvocation(MarkupGenerator markupGenerator) {
}
args.addAll(this.getOwnArguments().stream().map(a -> new Argument.Impl(a.name, markupGenerator.getEfxDataTypeEquivalent(a.dataType), a.value))
.collect(Collectors.toCollection(LinkedHashSet::new)));
- var invocation = markupGenerator.renderFragmentInvocation(this.id, args);
+ var invocation = markupGenerator.renderFragmentInvocation(this.id, args, translatorContext);
return markupGenerator.renderContextLoop(this.context.relativePath(), invocation, args);
}
diff --git a/src/main/java/eu/europa/ted/efx/model/templates/TemplateInvocation.java b/src/main/java/eu/europa/ted/efx/model/templates/TemplateInvocation.java
index 9eeb75b..4907aa4 100644
--- a/src/main/java/eu/europa/ted/efx/model/templates/TemplateInvocation.java
+++ b/src/main/java/eu/europa/ted/efx/model/templates/TemplateInvocation.java
@@ -8,6 +8,7 @@
import eu.europa.ted.efx.interfaces.Argument;
import eu.europa.ted.efx.interfaces.MarkupGenerator;
+import eu.europa.ted.efx.interfaces.TranslatorContext;
import eu.europa.ted.efx.model.Context;
import eu.europa.ted.efx.model.variables.Variables;
@@ -26,7 +27,7 @@ public TemplateInvocation(final ContentBlock parent, final TemplateDefinition te
* Template invocations do not have own content or child content to render.
*/
@Override
- public List renderDefinition(MarkupGenerator markupGenerator) {
+ public List renderDefinition(MarkupGenerator markupGenerator, TranslatorContext translatorContext) {
return new ArrayList() {
};
}
@@ -36,10 +37,10 @@ public List renderDefinition(MarkupGenerator markupGenerator) {
* should net have access to local variables.
*/
@Override
- public Markup renderInvocation(MarkupGenerator markupGenerator) {
+ public Markup renderInvocation(MarkupGenerator markupGenerator, TranslatorContext translatorContext) {
Set arguments = this.getOwnArguments().stream().map(a -> new Argument.Impl(a.name, markupGenerator.getEfxDataTypeEquivalent(a.dataType), a.value))
.collect(Collectors.toCollection(LinkedHashSet::new));
- var content = markupGenerator.renderFragmentInvocation(this.id, arguments);
+ var content = markupGenerator.renderFragmentInvocation(this.id, arguments, translatorContext);
return markupGenerator.renderContextLoop(this.context.relativePath(), content, arguments);
}
}
\ No newline at end of file
diff --git a/src/main/java/eu/europa/ted/efx/sdk1/EfxExpressionTranslatorV1.java b/src/main/java/eu/europa/ted/efx/sdk1/EfxExpressionTranslatorV1.java
index dde510e..ad2c0bc 100644
--- a/src/main/java/eu/europa/ted/efx/sdk1/EfxExpressionTranslatorV1.java
+++ b/src/main/java/eu/europa/ted/efx/sdk1/EfxExpressionTranslatorV1.java
@@ -10,8 +10,8 @@
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
-import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
@@ -190,10 +190,6 @@ protected static String getFieldId(FieldReferenceContext ctx) {
return getFieldId(ctx.absoluteFieldReference());
}
- if (ctx.fieldReferenceWithFieldContextOverride() != null) {
- return getFieldId(ctx.fieldReferenceWithFieldContextOverride().fieldReferenceWithPredicate());
- }
-
if (ctx.fieldReferenceInOtherNotice() != null) {
return getFieldId(ctx.fieldReferenceInOtherNotice());
}
diff --git a/src/main/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1.java b/src/main/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1.java
index 6f1f86b..9cf4481 100644
--- a/src/main/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1.java
+++ b/src/main/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1.java
@@ -22,6 +22,7 @@
import eu.europa.ted.efx.interfaces.MarkupGenerator;
import eu.europa.ted.efx.interfaces.ScriptGenerator;
import eu.europa.ted.efx.interfaces.SymbolResolver;
+import eu.europa.ted.efx.interfaces.TranslatorContext;
import eu.europa.ted.efx.model.Context;
import eu.europa.ted.efx.model.Context.FieldContext;
import eu.europa.ted.efx.model.Context.NodeContext;
@@ -97,7 +98,7 @@ private enum Indent {
*/
MarkupGenerator markup;
- final ContentBlock rootBlock = ContentBlock.newRootBlock();
+ final ContentBlock rootBlock = ContentBlock.newRootBlock("block");
/**
* The block stack is used to keep track of the indentation of template lines and adjust the EFX
@@ -201,8 +202,8 @@ public void exitTemplateFile(TemplateFileContext ctx) {
List templateCalls = new ArrayList<>();
List templates = new ArrayList<>();
for (ContentBlock block : this.rootBlock.getChildren()) {
- templateCalls.add(block.renderInvocation(markup));
- templates.addAll(block.renderDefinition(markup));
+ templateCalls.add(block.renderInvocation(markup, TranslatorContext.DEFAULT));
+ templates.addAll(block.renderDefinition(markup, TranslatorContext.DEFAULT));
}
Markup file = this.markup.composeOutputFile(templateCalls, templates);
this.stack.push(file);
@@ -217,7 +218,7 @@ public void exitTextTemplate(TextTemplateContext ctx) {
Markup template =
ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
String text = ctx.textBlock() != null ? ctx.textBlock().getText() : "";
- this.stack.push(this.markup.renderFreeText(this.markup.escapeSpecialCharacters(text)).join(template));
+ this.stack.push(this.markup.renderFreeText(this.markup.escapeSpecialCharacters(text), TranslatorContext.DEFAULT).join(template));
}
@Override
@@ -233,7 +234,7 @@ public void exitExpressionTemplate(ExpressionTemplateContext ctx) {
Markup template =
ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
Expression expression = this.stack.pop(Expression.class);
- this.stack.push(this.markup.renderVariableExpression(expression).join(template));
+ this.stack.push(this.markup.renderVariableExpression(expression, TranslatorContext.DEFAULT).join(template));
}
@@ -280,7 +281,7 @@ private void exitStandardLabelReference(StandardLabelReferenceContext ctx, Strin
this.stack.push(this.markup.renderLabelFromKey(this.script.composeStringConcatenation(
List.of(assetType, this.script.getStringLiteralFromUnquotedString("|"), labelType,
- this.script.getStringLiteralFromUnquotedString("|"), assetId))));
+ this.script.getStringLiteralFromUnquotedString("|"), assetId)), TranslatorContext.DEFAULT));
}
/**
@@ -312,7 +313,7 @@ private void exitStandardLabelReference(StandardLabelReferenceContext ctx, Strin
this.script.getStringLiteralFromUnquotedString("|"),
new StringExpression(loopVariable.referenceExpression.getScript()))),
StringSequenceExpression.class),
- StringSequenceExpression.class)));
+ StringSequenceExpression.class), TranslatorContext.DEFAULT));
}
@@ -324,7 +325,7 @@ public void exitShorthandBtLabelReference(ShorthandBtLabelReferenceContext ctx)
this.stack.push(this.markup.renderLabelFromKey(this.script.composeStringConcatenation(
List.of(this.script.getStringLiteralFromUnquotedString(ASSET_TYPE_BT),
this.script.getStringLiteralFromUnquotedString("|"), labelType,
- this.script.getStringLiteralFromUnquotedString("|"), assetId))));
+ this.script.getStringLiteralFromUnquotedString("|"), assetId)), TranslatorContext.DEFAULT));
}
@Override
@@ -340,7 +341,7 @@ public void exitShorthandFieldLabelReference(ShorthandFieldLabelReferenceContext
List.of(this.script.getStringLiteralFromUnquotedString(ASSET_TYPE_FIELD),
this.script.getStringLiteralFromUnquotedString("|"), labelType,
this.script.getStringLiteralFromUnquotedString("|"),
- this.script.getStringLiteralFromUnquotedString(fieldId)))));
+ this.script.getStringLiteralFromUnquotedString(fieldId))), TranslatorContext.DEFAULT));
}
}
@@ -378,7 +379,7 @@ private void shorthandIndirectLabelReference(final String fieldId) {
this.script.getStringLiteralFromUnquotedString("|"),
this.script.getStringLiteralFromUnquotedString(fieldId))),
StringSequenceExpression.class),
- StringSequenceExpression.class)));
+ StringSequenceExpression.class), TranslatorContext.DEFAULT));
break;
case "code":
case "internal-code":
@@ -398,7 +399,7 @@ private void shorthandIndirectLabelReference(final String fieldId) {
this.script.getStringLiteralFromUnquotedString("."),
new StringExpression(loopVariable.referenceExpression.getScript()))),
StringSequenceExpression.class),
- StringSequenceExpression.class)));
+ StringSequenceExpression.class), TranslatorContext.DEFAULT));
break;
default:
throw InvalidUsageException.shorthandRequiresCodeOrIndicator(fieldId, fieldType);
@@ -425,7 +426,7 @@ public void exitShorthandLabelReferenceFromContext(ShorthandLabelReferenceFromCo
this.script.getStringLiteralFromUnquotedString("|"),
this.script.getStringLiteralFromUnquotedString(labelType),
this.script.getStringLiteralFromUnquotedString("|"),
- this.script.getStringLiteralFromUnquotedString(this.efxContext.symbol())))));
+ this.script.getStringLiteralFromUnquotedString(this.efxContext.symbol()))), TranslatorContext.DEFAULT));
}
} else if (this.efxContext.isNodeContext()) {
this.stack.push(this.markup.renderLabelFromKey(this.script.composeStringConcatenation(
@@ -433,7 +434,7 @@ public void exitShorthandLabelReferenceFromContext(ShorthandLabelReferenceFromCo
this.script.getStringLiteralFromUnquotedString("|"),
this.script.getStringLiteralFromUnquotedString(labelType),
this.script.getStringLiteralFromUnquotedString("|"),
- this.script.getStringLiteralFromUnquotedString(this.efxContext.symbol())))));
+ this.script.getStringLiteralFromUnquotedString(this.efxContext.symbol()))), TranslatorContext.DEFAULT));
}
}
diff --git a/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java b/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java
index f299a35..169678f 100644
--- a/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java
+++ b/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java
@@ -28,6 +28,8 @@
import eu.europa.ted.efx.interfaces.MarkupGenerator;
import eu.europa.ted.efx.interfaces.ScriptGenerator;
import eu.europa.ted.efx.interfaces.SymbolResolver;
+import eu.europa.ted.efx.interfaces.TemplateSection;
+import eu.europa.ted.efx.interfaces.TranslatorContext;
import eu.europa.ted.efx.model.Context;
import eu.europa.ted.efx.model.Context.FieldContext;
import eu.europa.ted.efx.model.Context.NodeContext;
@@ -64,7 +66,6 @@
import eu.europa.ted.efx.model.variables.Template;
import eu.europa.ted.efx.model.variables.Variable;
import eu.europa.ted.efx.model.variables.Variables;
-import eu.europa.ted.efx.sdk2.EfxExpressionTranslatorV2.ExpressionPreprocessor;
import eu.europa.ted.efx.sdk2.EfxParser.AssetIdContext;
import eu.europa.ted.efx.sdk2.EfxParser.AssetTypeContext;
import eu.europa.ted.efx.sdk2.EfxParser.BooleanFunctionDeclarationContext;
@@ -79,6 +80,8 @@
import eu.europa.ted.efx.sdk2.EfxParser.DateParameterDeclarationContext;
import eu.europa.ted.efx.sdk2.EfxParser.DateVariableInitializerContext;
import eu.europa.ted.efx.sdk2.EfxParser.DictionaryDeclarationContext;
+import eu.europa.ted.efx.sdk2.EfxParser.DictionaryIndexClauseContext;
+import eu.europa.ted.efx.sdk2.EfxParser.DictionaryKeyClauseContext;
import eu.europa.ted.efx.sdk2.EfxParser.DurationFunctionDeclarationContext;
import eu.europa.ted.efx.sdk2.EfxParser.DurationParameterDeclarationContext;
import eu.europa.ted.efx.sdk2.EfxParser.DurationVariableInitializerContext;
@@ -88,6 +91,13 @@
import eu.europa.ted.efx.sdk2.EfxParser.InvokeTemplateContext;
import eu.europa.ted.efx.sdk2.EfxParser.LabelTemplateContext;
import eu.europa.ted.efx.sdk2.EfxParser.LabelTypeContext;
+import eu.europa.ted.efx.sdk2.EfxParser.LinkedExpressionBlockContext;
+import eu.europa.ted.efx.sdk2.EfxParser.LinkedExpressionTemplateContext;
+import eu.europa.ted.efx.sdk2.EfxParser.LinkedLabelBlockContext;
+import eu.europa.ted.efx.sdk2.EfxParser.LinkedLabelTemplateContext;
+import eu.europa.ted.efx.sdk2.EfxParser.LinkedTextBlockContext;
+import eu.europa.ted.efx.sdk2.EfxParser.LinkedTextTemplateContext;
+import eu.europa.ted.efx.sdk2.EfxParser.NavigationSectionContext;
import eu.europa.ted.efx.sdk2.EfxParser.NumericFunctionDeclarationContext;
import eu.europa.ted.efx.sdk2.EfxParser.NumericParameterDeclarationContext;
import eu.europa.ted.efx.sdk2.EfxParser.NumericVariableInitializerContext;
@@ -103,6 +113,7 @@
import eu.europa.ted.efx.sdk2.EfxParser.StringFunctionDeclarationContext;
import eu.europa.ted.efx.sdk2.EfxParser.StringParameterDeclarationContext;
import eu.europa.ted.efx.sdk2.EfxParser.StringVariableInitializerContext;
+import eu.europa.ted.efx.sdk2.EfxParser.SummarySectionContext;
import eu.europa.ted.efx.sdk2.EfxParser.TemplateDefinitionContext;
import eu.europa.ted.efx.sdk2.EfxParser.TemplateDeclarationContext;
import eu.europa.ted.efx.sdk2.EfxParser.TemplateFileContext;
@@ -114,7 +125,6 @@
import eu.europa.ted.efx.sdk2.EfxParser.TimeVariableInitializerContext;
import eu.europa.ted.efx.sdk2.EfxParser.WhenDisplayTemplateContext;
import eu.europa.ted.efx.sdk2.EfxParser.WhenInvokeTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxTemplateTranslatorV2.TemplatePreprocessor;
/**
* The EfxTemplateTranslator extends the {@link EfxExpressionTranslatorV2} to provide additional
@@ -154,7 +164,12 @@ private enum Indent {
*/
MarkupGenerator markup;
- final ContentBlock rootBlock = ContentBlock.newRootBlock();
+ TranslatorContext translatorContext = TranslatorContext.DEFAULT;
+
+ final ContentBlock bodySectionRoot = ContentBlock.newRootBlock("body");
+ final ContentBlock summarySectionRoot = ContentBlock.newRootBlock("summary");
+ final ContentBlock navigationSectionRoot = ContentBlock.newRootBlock("nav");
+ ContentBlock rootBlock = bodySectionRoot;
/**
* The block stack is used to keep track of the indentation of template lines and adjust the EFX
@@ -368,16 +383,24 @@ public void exitTemplateFile(TemplateFileContext ctx) {
}
}
- List templateCalls = new ArrayList<>();
- List templates = new ArrayList<>();
- for (ContentBlock block : this.rootBlock.getChildren()) {
+ List fragments = new ArrayList<>();
+ List mainSection = renderSection(TemplateSection.DEFAULT, bodySectionRoot, fragments);
+ List summarySection = renderSection(TemplateSection.SUMMARY, summarySectionRoot, fragments);
+ List navigationSection = renderSection(TemplateSection.NAVIGATION, navigationSectionRoot, fragments);
+ Markup file = this.markup.composeOutputFile(globals, mainSection, summarySection, navigationSection, fragments);
+ this.stack.push(file);
+ }
+
+ private List renderSection(TemplateSection section, ContentBlock sectionRoot, List templates) {
+ List markupList = new ArrayList<>();
+ this.translatorContext.setCurrentSection(section);
+ for (ContentBlock block : sectionRoot.getChildren()) {
if (!(block instanceof TemplateDefinition)) {
- templateCalls.add(block.renderInvocation(markup));
+ markupList.add(block.renderInvocation(markup, this.translatorContext));
}
- templates.addAll(block.renderDefinition(markup));
+ templates.addAll(block.renderDefinition(markup, this.translatorContext));
}
- Markup file = this.markup.composeOutputFile(globals, templateCalls, templates);
- this.stack.push(file);
+ return markupList;
}
// #endregion Template File -------------------------------------------------
@@ -386,26 +409,44 @@ public void exitTemplateFile(TemplateFileContext ctx) {
@Override
public void exitTextTemplate(TextTemplateContext ctx) {
- Markup template =
- ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
+ Markup template = ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
String text = ctx.textBlock() != null ? ctx.textBlock().getText() : "";
- this.stack.push(this.markup.renderFreeText(this.markup.escapeSpecialCharacters(text)).join(template));
+ this.stack.push(this.markup.renderFreeText(this.markup.escapeSpecialCharacters(text), this.translatorContext).join(template));
+ }
+
+ @Override
+ public void exitLinkedTextTemplate(LinkedTextTemplateContext ctx) {
+ Markup template = ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
+ Markup text = this.stack.pop(Markup.class);
+ this.stack.push(text.join(template));
}
@Override
public void exitLabelTemplate(LabelTemplateContext ctx) {
- Markup template =
- ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
+ Markup template = ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
Markup label = ctx.labelBlock() != null ? this.stack.pop(Markup.class) : Markup.empty();
this.stack.push(label.join(template));
}
+ @Override
+ public void exitLinkedLabelTemplate(LinkedLabelTemplateContext ctx) {
+ Markup template = ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
+ Markup label = this.stack.pop(Markup.class);
+ this.stack.push(label.join(template));
+ }
+
@Override
public void exitExpressionTemplate(ExpressionTemplateContext ctx) {
- Markup template =
- ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
+ Markup template = ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
Expression expression = this.stack.pop(Expression.class);
- this.stack.push(this.markup.renderVariableExpression(expression).join(template));
+ this.stack.push(this.markup.renderVariableExpression(expression, this.translatorContext).join(template));
+ }
+
+ @Override
+ public void exitLinkedExpressionTemplate(LinkedExpressionTemplateContext ctx) {
+ Markup template = ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
+ Markup link = this.stack.pop(Markup.class);
+ this.stack.push(link.join(template));
}
// #region New in EFX-2: Secondary templates --------------------------------
@@ -413,7 +454,7 @@ public void exitExpressionTemplate(ExpressionTemplateContext ctx) {
@Override
public void exitSecondaryTemplate(SecondaryTemplateContext ctx) {
Markup template = ctx.templateFragment() != null ? this.stack.pop(Markup.class) : Markup.empty();
- this.stack.push(this.markup.renderLineBreak().join(template));
+ this.stack.push(this.markup.renderLineBreak(this.translatorContext).join(template));
}
// #endregion New in EFX-2: Secondary templates -----------------------------
@@ -467,7 +508,7 @@ private void exitStandardLabelReference(StandardLabelReferenceContext ctx, Strin
this.stack.push(this.markup.renderLabelFromKey(this.script.composeStringConcatenation(
List.of(assetType, this.script.getStringLiteralFromUnquotedString("|"), labelType,
- this.script.getStringLiteralFromUnquotedString("|"), assetId)), quantity));
+ this.script.getStringLiteralFromUnquotedString("|"), assetId)), quantity, this.translatorContext));
}
/**
@@ -499,7 +540,7 @@ private void exitStandardLabelReference(StandardLabelReferenceContext ctx, Strin
this.script.getStringLiteralFromUnquotedString("|"),
new StringExpression(loopVariable.referenceExpression.getScript()))),
StringSequenceExpression.class),
- StringSequenceExpression.class)));
+ StringSequenceExpression.class), this.translatorContext));
}
@@ -512,7 +553,7 @@ public void exitShorthandBtLabelReference(ShorthandBtLabelReferenceContext ctx)
this.stack.push(this.markup.renderLabelFromKey(this.script.composeStringConcatenation(
List.of(this.script.getStringLiteralFromUnquotedString(ASSET_TYPE_BT),
this.script.getStringLiteralFromUnquotedString("|"), labelType,
- this.script.getStringLiteralFromUnquotedString("|"), assetId)), quantity));
+ this.script.getStringLiteralFromUnquotedString("|"), assetId)), quantity, this.translatorContext));
}
@Override
@@ -529,7 +570,7 @@ public void exitShorthandFieldLabelReference(ShorthandFieldLabelReferenceContext
List.of(this.script.getStringLiteralFromUnquotedString(ASSET_TYPE_FIELD),
this.script.getStringLiteralFromUnquotedString("|"), labelType,
this.script.getStringLiteralFromUnquotedString("|"),
- this.script.getStringLiteralFromUnquotedString(fieldId))), quantity));
+ this.script.getStringLiteralFromUnquotedString(fieldId))), quantity, this.translatorContext));
}
}
@@ -570,7 +611,7 @@ private void shorthandIndirectLabelReference(final String fieldId, final Numeric
this.script.getStringLiteralFromUnquotedString("|"),
this.script.getStringLiteralFromUnquotedString(fieldId))),
StringSequenceExpression.class),
- StringSequenceExpression.class), quantity));
+ StringSequenceExpression.class), quantity, this.translatorContext));
break;
case "code":
case "internal-code":
@@ -591,7 +632,7 @@ private void shorthandIndirectLabelReference(final String fieldId, final Numeric
new StringExpression(loopVariable.referenceExpression.getScript()))),
StringSequenceExpression.class),
StringSequenceExpression.class),
- quantity));
+ quantity, this.translatorContext));
break;
default:
throw InvalidUsageException.shorthandRequiresCodeOrIndicator(fieldId, fieldType);
@@ -622,7 +663,7 @@ public void exitShorthandLabelReferenceFromContext(ShorthandLabelReferenceFromCo
this.script.getStringLiteralFromUnquotedString("|"),
this.script.getStringLiteralFromUnquotedString(labelType),
this.script.getStringLiteralFromUnquotedString("|"),
- this.script.getStringLiteralFromUnquotedString(this.efxContext.symbol()))), quantity));
+ this.script.getStringLiteralFromUnquotedString(this.efxContext.symbol()))), quantity, this.translatorContext));
}
} else if (this.efxContext.isNodeContext()) {
this.stack.push(this.markup.renderLabelFromKey(this.script.composeStringConcatenation(
@@ -630,7 +671,7 @@ public void exitShorthandLabelReferenceFromContext(ShorthandLabelReferenceFromCo
this.script.getStringLiteralFromUnquotedString("|"),
this.script.getStringLiteralFromUnquotedString(labelType),
this.script.getStringLiteralFromUnquotedString("|"),
- this.script.getStringLiteralFromUnquotedString(this.efxContext.symbol()))), quantity));
+ this.script.getStringLiteralFromUnquotedString(this.efxContext.symbol()))), quantity, this.translatorContext));
}
}
@@ -675,9 +716,10 @@ public void exitAssetId(AssetIdContext ctx) {
public void exitComputedLabelReference(ComputedLabelReferenceContext ctx) {
NumericExpression quantity = ctx.pluraliser() != null ? this.stack.pop(NumericExpression.class) : NumericExpression.empty();
StringExpression expression = this.stack.pop(StringExpression.class);
- this.stack.push(this.markup.renderLabelFromExpression(expression, quantity));
+ this.stack.push(this.markup.renderLabelFromExpression(expression, quantity, this.translatorContext));
}
+
@Override
public void exitDictionaryDeclaration(DictionaryDeclarationContext ctx) {
String name = ctx.dictionaryName.getText();
@@ -688,6 +730,16 @@ public void exitDictionaryDeclaration(DictionaryDeclarationContext ctx) {
this.stack.declareGlobalIdentifier(new Dictionary(name, match, key));
}
+ @Override
+ public void exitDictionaryIndexClause(DictionaryIndexClauseContext ctx) {
+ this.efxContext.pushFieldContext(getFieldId(ctx.fieldContext()));
+ }
+
+ @Override
+ public void exitDictionaryKeyClause(DictionaryKeyClauseContext ctx) {
+ this.efxContext.pop();
+ }
+
// #endregion New in EFX-2 --------------------------------------------------
// #endregion Label Blocks #{...} -------------------------------------------
@@ -785,11 +837,15 @@ public void exitContextDeclaration(ContextDeclarationContext ctx) {
if (ctx.contextVariableInitializer().fieldContext() != null) {
String fieldId = getFieldId(ctx.contextVariableInitializer().fieldContext());
this.exitFieldContextDeclaration(fieldId, contextPath, contextVariable);
+ } else if (ctx.contextVariableInitializer().nodeContext() != null) {
+ String nodeId = getNodeId(ctx.contextVariableInitializer().nodeContext());
+ assert nodeId != null : "We should have been able to locate the NodeId declared as context.";
+ this.exitNodeContextDeclaration(nodeId, contextPath, contextVariable);
}
} else if (ctx.nodeContext() != null) {
String nodeId = getNodeId(ctx.nodeContext());
assert nodeId != null : "We should have been able to locate the NodeId declared as context.";
- this.exitNodeContextDeclaration(nodeId, contextPath);
+ this.exitNodeContextDeclaration(nodeId, contextPath, null);
}
break;
}
@@ -802,7 +858,7 @@ private void exitSameContextDeclaration() {
if (currentContext.isFieldContext()) {
this.exitFieldContextDeclaration(symbol, contextPath, null);
} else if (currentContext.isNodeContext()) {
- this.exitNodeContextDeclaration(symbol, contextPath);
+ this.exitNodeContextDeclaration(symbol, contextPath, currentContext.variable());
}
}
@@ -813,25 +869,30 @@ private void exitParentContextDeclaration() {
if (parentContext.isFieldContext()) {
this.exitFieldContextDeclaration(symbol, contextPath, null);
} else if (parentContext.isNodeContext()) {
- this.exitNodeContextDeclaration(symbol, contextPath);
+ this.exitNodeContextDeclaration(symbol, contextPath, parentContext.variable());
}
}
private void exitRootContextDeclaration() {
PathExpression contextPath = new NodePathExpression("/*");
String symbol = "ND-Root";
- this.exitNodeContextDeclaration(symbol, contextPath);
+ this.exitNodeContextDeclaration(symbol, contextPath, null);
}
private void exitFieldContextDeclaration(String fieldId, PathExpression contextPath, Variable contextVariable) {
this.efxContext.push(new FieldContext(fieldId, contextPath, contextVariable));
if (contextVariable != null) {
this.stack.declareIdentifier(contextVariable);
+ this.efxContext.declareContextVariable(contextVariable.name, new FieldContext(fieldId, contextPath, contextVariable));
}
}
- private void exitNodeContextDeclaration(String nodeId, PathExpression contextPath) {
- this.efxContext.push(new NodeContext(nodeId, contextPath));
+ private void exitNodeContextDeclaration(String nodeId, PathExpression contextPath, Variable contextVariable) {
+ this.efxContext.push(new NodeContext(nodeId, contextPath, contextVariable));
+ if (contextVariable != null) {
+ this.stack.declareIdentifier(contextVariable);
+ this.efxContext.declareContextVariable(contextVariable.name, new NodeContext(nodeId, contextPath, contextVariable));
+ }
}
private Variable getContextVariable(ContextVariableInitializerContext ctx,
@@ -885,7 +946,7 @@ public void exitInvokeTemplate(InvokeTemplateContext ctx) {
final Set args = arguments.stream()
.map(a -> new Argument.Impl(a.name, this.markup.getEfxDataTypeEquivalent(a.dataType), a.value))
.collect(Collectors.toCollection(LinkedHashSet::new));
- this.stack.push(this.markup.renderFragmentInvocation(templateName, args));
+ this.stack.push(this.markup.renderFragmentInvocation(templateName, args, this.translatorContext));
}
// #endregion Conditional template blocks ---------------------------------
@@ -941,6 +1002,31 @@ public void exitTemplateVariableDeclaration(TemplateVariableDeclarationContext a
// #endregion Variable Initializers -----------------------------------------
+ // #region Hyperlinks -------------------------------------------------------
+
+ @Override
+ public void exitLinkedTextBlock(LinkedTextBlockContext ctx) {
+ var url = this.stack.pop(StringExpression.class);
+ var text = this.markup.renderFreeText(ctx.textBlock().getText(), this.translatorContext);
+ this.stack.push(this.markup.renderHyperlink(text, url, this.translatorContext));
+ }
+
+ @Override
+ public void exitLinkedLabelBlock(LinkedLabelBlockContext ctx) {
+ var url = this.stack.pop(StringExpression.class);
+ var text = this.stack.pop(Markup.class);
+ this.stack.push(this.markup.renderHyperlink(text, url, this.translatorContext));
+ }
+
+ @Override
+ public void exitLinkedExpressionBlock(LinkedExpressionBlockContext ctx) {
+ var url = this.stack.pop(StringExpression.class);
+ var text = this.markup.renderVariableExpression(this.stack.pop(Expression.class), this.translatorContext);
+ this.stack.push(this.markup.renderHyperlink(text, url, this.translatorContext));
+ }
+
+ // #endregion Hyperlinks ----------------------------------------------------
+
// #endregion New in EFX-2 --------------------------------------------------
@Override
@@ -993,6 +1079,36 @@ private void exitParameterDeclaration(String parameterName, Class extends Type
}
// #endregion Parameter Declarations ----------------------------------------
+
+ @Override
+ public void enterSummarySection(SummarySectionContext ctx) {
+ this.rootBlock = this.summarySectionRoot;
+ while (!this.blockStack.isEmpty()) {
+ this.blockStack.pop();
+ }
+ this.translatorContext.setCurrentSection(TemplateSection.SUMMARY);
+ }
+
+ @Override
+ public void exitSummarySection(SummarySectionContext ctx) {
+
+ this.translatorContext.setCurrentSection(TemplateSection.DEFAULT);
+ }
+
+ @Override
+ public void enterNavigationSection(NavigationSectionContext ctx) {
+ this.rootBlock = this.navigationSectionRoot;
+ while (!this.blockStack.isEmpty()) {
+ this.blockStack.pop();
+ }
+ this.translatorContext.setCurrentSection(TemplateSection.NAVIGATION);
+ }
+
+ @Override
+ public void exitNavigationSection(NavigationSectionContext ctx) {
+ this.translatorContext.setCurrentSection(TemplateSection.DEFAULT);
+ }
+
// #region Template lines --------------------------------------------------
@Override
@@ -1103,7 +1219,7 @@ private Context relativizeContext(Context childContext, Context parentContext) {
this.symbols.getRelativePath(childContext.absolutePath(), parentContextAbsolutePath), childContext.variable());
}
- assert childContext.isNodeContext() : "Child context should be either a FieldContext NodeContext.";
+ assert childContext.isNodeContext() : "Child context should be either a FieldContext or a NodeContext.";
return new NodeContext(childContext.symbol(), childContext.absolutePath(),
this.symbols.getRelativePath(childContext.absolutePath(), parentContextAbsolutePath));
@@ -1185,13 +1301,21 @@ public void exitTemplateVariableDeclaration(TemplateVariableDeclarationContext a
@Override
public void exitContextDeclaration(ContextDeclarationContext ctx) {
final var initializer = ctx.contextVariableInitializer();
- if (initializer == null || initializer.fieldContext() == null) {
+ if (initializer == null) {
return; // No context variable initializer, nothing to do during pre-processing.
}
- final String fieldId = getFieldId(initializer.fieldContext());
- var fieldType = FieldTypes.fromString(this.symbols.getTypeOfField(fieldId));
- this.stack.declareIdentifier(new Variable(initializer.variableName.getText(), PathExpression.instantiate("", fieldType),
- PathExpression.instantiate("", fieldType), PathExpression.instantiate("", fieldType)));
+ if (initializer.fieldContext() != null) {
+ final String fieldId = getFieldId(initializer.fieldContext());
+ var fieldType = FieldTypes.fromString(this.symbols.getTypeOfField(fieldId));
+ this.stack.declareIdentifier(
+ new Variable(initializer.variableName.getText(), PathExpression.instantiate("", fieldType),
+ PathExpression.instantiate("", fieldType), PathExpression.instantiate("", fieldType)));
+ }
+ if (initializer.nodeContext() != null) {
+ this.stack.declareIdentifier(new Variable(initializer.variableName.getText(), NodePathExpression.empty(),
+ NodePathExpression.empty(), NodePathExpression.empty()));
+ }
+ return;
}
@Override
@@ -1299,7 +1423,7 @@ private void exitTemplateLine(final int indentLevel) {
@Override
public void exitDictionaryDeclaration(DictionaryDeclarationContext ctx) {
var dictionaryName = ctx.dictionaryName.getText();
- var fieldId = getFieldId(ctx.fieldContext());
+ var fieldId = getFieldId(ctx.index.fieldContext());
var field = this.symbols.getAbsolutePathOfField(fieldId);
this.stack.declareGlobalIdentifier(new Dictionary(dictionaryName, field, StringExpression.empty()));
}
diff --git a/src/test/java/eu/europa/ted/efx/mock/MarkupGeneratorMock.java b/src/test/java/eu/europa/ted/efx/mock/MarkupGeneratorMock.java
index 1c3fb7f..be99529 100644
--- a/src/test/java/eu/europa/ted/efx/mock/MarkupGeneratorMock.java
+++ b/src/test/java/eu/europa/ted/efx/mock/MarkupGeneratorMock.java
@@ -1,6 +1,5 @@
package eu.europa.ted.efx.mock;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -10,6 +9,7 @@
import eu.europa.ted.efx.interfaces.Argument;
import eu.europa.ted.efx.interfaces.MarkupGenerator;
import eu.europa.ted.efx.interfaces.Parameter;
+import eu.europa.ted.efx.interfaces.TranslatorContext;
import eu.europa.ted.efx.model.expressions.Expression;
import eu.europa.ted.efx.model.expressions.path.PathExpression;
import eu.europa.ted.efx.model.expressions.scalar.NumericExpression;
@@ -28,9 +28,11 @@ public class MarkupGeneratorMock implements MarkupGenerator {
Map.entry(EfxDataType.Number.class, new Markup("decimal")), //
Map.entry(EfxDataType.Date.class, new Markup("date")), //
Map.entry(EfxDataType.Time.class, new Markup("time")), //
- Map.entry(EfxDataType.Duration.class, new Markup("duration")) //
+ Map.entry(EfxDataType.Duration.class, new Markup("duration")), //
+ Map.entry(EfxDataType.Node.class, new Markup("context")) //
);
+
@Override
public Markup renderVariableDeclaration(Class extends EfxDataType> dataType, String variableName,
Expression initialiser) {
@@ -49,17 +51,17 @@ public Markup renderFunctionDeclaration(Class extends EfxDataType> type, Strin
}
@Override
- public Markup renderVariableExpression(Expression valueReference) {
+ public Markup renderVariableExpression(Expression valueReference, TranslatorContext translatorContext) {
return new Markup(String.format("eval(%s)", valueReference.getScript()));
}
@Override
- public Markup renderLabelFromKey(StringExpression key) {
- return this.renderLabelFromKey(key, NumericExpression.empty());
+ public Markup renderLabelFromKey(StringExpression key, TranslatorContext translatorContext) {
+ return this.renderLabelFromKey(key, NumericExpression.empty(), translatorContext);
}
@Override
- public Markup renderLabelFromKey(StringExpression key, NumericExpression quantity) {
+ public Markup renderLabelFromKey(StringExpression key, NumericExpression quantity, TranslatorContext translatorContext) {
if (quantity.isEmpty()) {
return new Markup(String.format("label(%s)", key.getScript()));
}
@@ -67,12 +69,12 @@ public Markup renderLabelFromKey(StringExpression key, NumericExpression quantit
}
@Override
- public Markup renderLabelFromExpression(Expression expression) {
- return this.renderLabelFromExpression(expression, NumericExpression.empty());
+ public Markup renderLabelFromExpression(Expression expression, TranslatorContext translatorContext) {
+ return this.renderLabelFromExpression(expression, NumericExpression.empty(), TranslatorContext.DEFAULT);
}
@Override
- public Markup renderLabelFromExpression(Expression expression, NumericExpression quantity) {
+ public Markup renderLabelFromExpression(Expression expression, NumericExpression quantity, TranslatorContext translatorContext) {
if (quantity.isEmpty()) {
return new Markup(String.format("label(%s)", expression.getScript()));
}
@@ -80,10 +82,15 @@ public Markup renderLabelFromExpression(Expression expression, NumericExpression
}
@Override
- public Markup renderFreeText(String freeText) {
+ public Markup renderFreeText(String freeText, TranslatorContext translatorContext) {
return new Markup(String.format("text('%s')", freeText));
}
+ @Override
+ public Markup renderHyperlink(Markup label, StringExpression url, TranslatorContext translatorContext) {
+ return new Markup(String.format("hyperlink(%s, %s)", label.script, url.getScript()));
+ }
+
@Override
public String escapeSpecialCharacters(String text) {
if (text == null) {
@@ -100,13 +107,13 @@ public String escapeSpecialCharacters(String text) {
}
@Override
- public Markup renderLineBreak() {
+ public Markup renderLineBreak(TranslatorContext translatorContext) {
return new Markup("line-break()");
}
@Override
public Markup composeFragmentDefinition(String name, String number, Set conditionals,
- Markup content, Markup children, Set parameters) {
+ Markup content, Markup children, Set parameters, TranslatorContext translatorContext) {
String contents = "";
if (conditionals != null && !conditionals.isEmpty()) {
@@ -144,7 +151,7 @@ public Markup renderContextLoop(PathExpression context, final Markup content,
}
@Override
- public Markup renderFragmentInvocation(String name, Set arguments) {
+ public Markup renderFragmentInvocation(String name, Set arguments, TranslatorContext translatorContext) {
return new Markup(String.format("call(%s(%s))", name,
arguments.stream()
.map(arg -> String.format("%s:%s=%s", arg.getType(), arg.getName(), arg.getValue()))
@@ -152,16 +159,25 @@ public Markup renderFragmentInvocation(String name, Set arguments) {
}
@Override
- public Markup composeOutputFile(List body, List templates) {
- return this.composeOutputFile(new ArrayList(), body, templates);
+ public Markup composeOutputFile(List globals, List body, List summary, List navigation, final List fragments) {
+ var renderedGlobals = renderSection("GLOBALS", globals);
+ var renderedTemplates = renderSection("TEMPLATES", fragments);
+ var renderedMain = renderSection("MAIN", body);
+ var renderedSummary = renderSection("SUMMARY", summary);
+ var renderedNavigation = renderSection("NAV", navigation);
+
+ return new Markup(String.format("%1$s%6$s%2$s%6$s%3$s%6$s%4$s%6$s%5$s",
+ renderedGlobals,
+ renderedTemplates,
+ renderedMain,
+ renderedSummary,
+ renderedNavigation, "\n").trim());
}
- @Override
- public Markup composeOutputFile(List globals, List body, List templates) {
- return new Markup(String.format("%1$s%4$s%2$s%4$s%3$s",
- globals.stream().map(t -> t.script).collect(Collectors.joining("\n")),
- templates.stream().map(t -> t.script).collect(Collectors.joining("\n")),
- body.stream().map(t -> t.script).collect(Collectors.joining("\n")), "\n").trim());
+ private String renderSection(String heading, List markupList) {
+ return markupList.size() > 0
+ ? String.format("%1$s:\n%2$s", heading, markupList.stream().map(t -> t.script).collect(Collectors.joining("\n")))
+ : "";
}
@Override
@@ -174,5 +190,4 @@ public Markup renderDictionaryDeclaration(final String name, final PathExpressio
final StringExpression key) {
return new Markup(String.format("let %s index %s by %s;", name, match.getScript(), key.getScript()));
}
-
}
diff --git a/src/test/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1Test.java b/src/test/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1Test.java
index 02153f6..cf92627 100644
--- a/src/test/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1Test.java
+++ b/src/test/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1Test.java
@@ -17,7 +17,7 @@ protected String getSdkVersion() {
@Test
void testTemplateLineNoIdent() {
assertEquals(
- "let block01() -> { text('foo') }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { text('foo') }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} foo"));
}
@@ -26,11 +26,13 @@ void testTemplateLineNoIdent() {
*/
@Test
void testTemplateLineOutline_Autogenerated() {
- assertEquals(lines("let block01() -> { #1: text('foo')",
+ assertEquals(lines("TEMPLATES:",
+ "let block01() -> { #1: text('foo')",
"for-each(../..).call(block0101()) }",
"let block0101() -> { #1.1: text('bar')",
"for-each(PathNode/NumberField).call(block010101()) }",
"let block010101() -> { text('foo') }",
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())"), //
translateTemplate(lines("{BT-00-Text} foo", "\t{ND-Root} bar", "\t\t{BT-00-Number} foo")));
}
@@ -40,11 +42,13 @@ void testTemplateLineOutline_Autogenerated() {
*/
@Test
void testTemplateLineOutline_Explicit() {
- assertEquals(lines("let block01() -> { #2: text('foo')",
+ assertEquals(lines("TEMPLATES:",
+ "let block01() -> { #2: text('foo')",
"for-each(../..).call(block0101()) }",
"let block0101() -> { #2.3: text('bar')",
"for-each(PathNode/NumberField).call(block010101()) }",
"let block010101() -> { text('foo') }",
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())"), //
translateTemplate(
lines("2{BT-00-Text} foo", "\t3{ND-Root} bar", "\t\t{BT-00-Number} foo")));
@@ -56,11 +60,13 @@ void testTemplateLineOutline_Explicit() {
*/
@Test
void testTemplateLineOutline_Mixed() {
- assertEquals(lines("let block01() -> { #2: text('foo')",
+ assertEquals(lines("TEMPLATES:",
+ "let block01() -> { #2: text('foo')",
"for-each(../..).call(block0101()) }",
"let block0101() -> { #2.1: text('bar')",
"for-each(PathNode/NumberField).call(block010101()) }",
"let block010101() -> { text('foo') }",
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())"), //
translateTemplate(lines("2{BT-00-Text} foo", "\t{ND-Root} bar", "\t\t{BT-00-Number} foo")));
}
@@ -71,11 +77,13 @@ void testTemplateLineOutline_Mixed() {
*/
@Test
void testTemplateLineOutline_Suppressed() {
- assertEquals(lines("let block01() -> { #2: text('foo')",
+ assertEquals(lines("TEMPLATES:",
+ "let block01() -> { #2: text('foo')",
"for-each(../..).call(block0101()) }",
"let block0101() -> { text('bar')",
"for-each(PathNode/NumberField).call(block010101()) }",
"let block010101() -> { text('foo') }",
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())"), //
translateTemplate(
lines("2{BT-00-Text} foo", "\t0{ND-Root} bar", "\t\t{BT-00-Number} foo")));
@@ -88,11 +96,12 @@ void testTemplateLineOutline_Suppressed() {
@Test
void testTemplateLineOutline_SuppressedAtParent() {
// Outline is ignored if the line has no children
- assertEquals(lines("let block01() -> { text('foo')",
+ assertEquals(lines("TEMPLATES:", "let block01() -> { text('foo')",
"for-each(../..).call(block0101()) }",
"let block0101() -> { #1: text('bar')",
"for-each(PathNode/NumberField).call(block010101()) }",
"let block010101() -> { text('foo') }",
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())"), //
translateTemplate(lines("0{BT-00-Text} foo", "\t{ND-Root} bar", "\t\t{BT-00-Number} foo")));
}
@@ -105,8 +114,10 @@ void testTemplateLineFirstIndented() {
@Test
void testTemplateLineIdentTab() {
assertEquals(
- lines("let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }", //
+ lines("TEMPLATES:",
+ "let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }", //
"let block0101() -> { text('bar') }", //
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())"), //
translateTemplate(lines("{BT-00-Text} foo", "\t{BT-00-Text} bar")));
}
@@ -114,8 +125,10 @@ void testTemplateLineIdentTab() {
@Test
void testTemplateLineIdentSpaces() {
assertEquals(
- lines("let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }", //
+ lines("TEMPLATES:",
+ "let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }", //
"let block0101() -> { text('bar') }", //
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())"), //
translateTemplate(lines("{BT-00-Text} foo", " {BT-00-Text} bar")));
}
@@ -137,9 +150,11 @@ void testTemplateLineIdentMixedSpaceThenTab() {
@Test
void testTemplateLineIdentLower() {
assertEquals(
- lines("let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }",
+ lines("TEMPLATES:",
+ "let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }",
"let block0101() -> { text('bar') }",
"let block02() -> { text('code') }",
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())",
"for-each(/*/PathNode/CodeField).call(block02())"),
translateTemplate(lines("{BT-00-Text} foo", "\t{BT-00-Text} bar", "{BT-00-Code} code")));
@@ -155,9 +170,11 @@ void testTemplateLineIdentUnexpected() {
@Test
void testTemplateLine_VariableScope() {
assertEquals(
- lines("let block01() -> { #1: eval(for $x in ./normalize-space(text()) return $x)", //
+ lines("TEMPLATES:",
+ "let block01() -> { #1: eval(for $x in ./normalize-space(text()) return $x)", //
"for-each(.).call(block0101()) }", //
"let block0101() -> { eval(for $x in ./normalize-space(text()) return $x) }", //
+ "MAIN:",
"for-each(/*/PathNode/TextField).call(block01())"), //
translateTemplate(lines("{BT-00-Text} ${for text:$x in BT-00-Text return $x}",
" {BT-00-Text} ${for text:$x in BT-00-Text return $x}")));
@@ -169,35 +186,35 @@ void testTemplateLine_VariableScope() {
@Test
void testStandardLabelReference() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{field|name|BT-00-Text}"));
}
@Test
void testStandardLabelReference_UsingLabelTypeAsAssetId() {
assertEquals(
- "let block01() -> { label(concat('auxiliary', '|', 'text', '|', 'value')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('auxiliary', '|', 'text', '|', 'value')) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{auxiliary|text|value}"));
}
@Test
void testStandardLabelReference_WithAssetIdIterator() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in for $t in ./normalize-space(text()) return $t return concat('field', '|', 'name', '|', $item))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(distinct-values(for $item in for $t in ./normalize-space(text()) return $t return concat('field', '|', 'name', '|', $item))) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{field|name|${for text:$t in BT-00-Text return $t}}"));
}
@Test
void testShorthandBtLabelReference() {
assertEquals(
- "let block01() -> { label(concat('business-term', '|', 'name', '|', 'BT-00')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('business-term', '|', 'name', '|', 'BT-00')) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{name|BT-00}"));
}
@Test
void testShorthandFieldLabelReference() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{name|BT-00-Text}"));
}
@@ -210,42 +227,42 @@ void testShorthandBtLabelReference_MissingLabelType() {
@Test
void testShorthandIndirectLabelReferenceForIndicator() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../IndicatorField return concat('indicator', '|', 'when', '-', $item, '|', 'BT-00-Indicator'))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(distinct-values(for $item in ../IndicatorField return concat('indicator', '|', 'when', '-', $item, '|', 'BT-00-Indicator'))) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{BT-00-Indicator}"));
}
@Test
void testShorthandIndirectLabelReferenceForCode() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../CodeField/normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(distinct-values(for $item in ../CodeField/normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{BT-00-Code}"));
}
@Test
void testShorthandIndirectLabelReferenceForInternalCode() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../InternalCodeField/normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(distinct-values(for $item in ../InternalCodeField/normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{BT-00-Internal-Code}"));
}
@Test
void testShorthandIndirectLabelReferenceForCodeAttribute() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../CodeField/@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(distinct-values(for $item in ../CodeField/@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{BT-00-CodeAttribute}"));
}
@Test
void testShorthandIndirectLabelReferenceForCodeAttribute_WithSameAttributeInContext() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/CodeField/@attribute).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(distinct-values(for $item in ../@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nMAIN:\nfor-each(/*/PathNode/CodeField/@attribute).call(block01())",
translateTemplate("{BT-00-CodeAttribute} #{BT-00-CodeAttribute}"));
}
@Test
void testShorthandIndirectLabelReferenceForCodeAttribute_WithSameElementInContext() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ./@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(distinct-values(for $item in ./@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nMAIN:\nfor-each(/*/PathNode/CodeField).call(block01())",
translateTemplate("{BT-00-Code} #{BT-00-CodeAttribute}"));
}
@@ -264,14 +281,14 @@ void testShorthandIndirectLabelReferenceForAttribute() {
@Test
void testShorthandLabelReferenceFromContext_WithValueLabelTypeAndIndicatorField() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Indicator')) }\nfor-each(/*/PathNode/IndicatorField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Indicator')) }\nMAIN:\nfor-each(/*/PathNode/IndicatorField).call(block01())",
translateTemplate("{BT-00-Indicator} #{name}"));
}
@Test
void testShorthandLabelReferenceFromContext_WithValueLabelTypeAndCodeField() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Code')) }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Code')) }\nMAIN:\nfor-each(/*/PathNode/CodeField).call(block01())",
translateTemplate("{BT-00-Code} #{name}"));
}
@@ -284,7 +301,7 @@ void testShorthandLabelReferenceFromContext_WithValueLabelTypeAndTextField() {
@Test
void testShorthandLabelReferenceFromContext_WithOtherLabelType() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{name}"));
}
@@ -297,14 +314,14 @@ void testShorthandLabelReferenceFromContext_WithUnknownLabelType() {
@Test
void testShorthandLabelReferenceFromContext_WithNodeContext() {
assertEquals(
- "let block01() -> { label(concat('node', '|', 'name', '|', 'ND-Root')) }\nfor-each(/*).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('node', '|', 'name', '|', 'ND-Root')) }\nMAIN:\nfor-each(/*).call(block01())",
translateTemplate("{ND-Root} #{name}"));
}
@Test
void testShorthandIndirectLabelReferenceFromContextField() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ./normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(distinct-values(for $item in ./normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nMAIN:\nfor-each(/*/PathNode/CodeField).call(block01())",
translateTemplate("{BT-00-Code} #value"));
}
@@ -318,14 +335,14 @@ void testShorthandIndirectLabelReferenceFromContextField_WithNodeContext() {
@Test
void testShorthandFieldValueReferenceFromContextField() {
- assertEquals("let block01() -> { eval(./normalize-space(text())) }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ assertEquals("TEMPLATES:\nlet block01() -> { eval(./normalize-space(text())) }\nMAIN:\nfor-each(/*/PathNode/CodeField).call(block01())",
translateTemplate("{BT-00-Code} $value"));
}
@Test
void testShorthandFieldValueReferenceFromContextField_WithText() {
assertEquals(
- "let block01() -> { text('blah ')label(distinct-values(for $item in ./normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item)))text(' ')text('blah ')eval(./normalize-space(text()))text(' ')text('blah') }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { text('blah ')label(distinct-values(for $item in ./normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item)))text(' ')text('blah ')eval(./normalize-space(text()))text(' ')text('blah') }\nMAIN:\nfor-each(/*/PathNode/CodeField).call(block01())",
translateTemplate("{BT-00-Code} blah #value blah $value blah"));
}
@@ -340,24 +357,24 @@ void testShorthandFieldValueReferenceFromContextField_WithNodeContext() {
@Test
void testNestedExpression() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', ./normalize-space(text()))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('field', '|', 'name', '|', ./normalize-space(text()))) }\nMAIN:\nfor-each(/*/PathNode/TextField).call(block01())",
translateTemplate("{BT-00-Text} #{field|name|${BT-00-Text}}"));
}
@Test
void testEndOfLineComments() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'))text(' ')text('blah blah') }\nfor-each(/*).call(block01())",
+ "TEMPLATES:\nlet block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'))text(' ')text('blah blah') }\nMAIN:\nfor-each(/*).call(block01())",
translateTemplate("{ND-Root} #{name|BT-00-Text} blah blah // comment blah blah"));
}
@Test
void testImplicitFormatting_Dates() {
- assertEquals("let block01() -> { eval(for $item in PathNode/StartDateField/xs:date(text()) return format-date($item, '[D01]/[M01]/[Y0001]')) }\nfor-each(/*).call(block01())", translateTemplate("{ND-Root} ${BT-00-StartDate}"));
+ assertEquals("TEMPLATES:\nlet block01() -> { eval(for $item in PathNode/StartDateField/xs:date(text()) return format-date($item, '[D01]/[M01]/[Y0001]')) }\nMAIN:\nfor-each(/*).call(block01())", translateTemplate("{ND-Root} ${BT-00-StartDate}"));
}
@Test
void testImplicitFormatting_Times() {
- assertEquals("let block01() -> { eval(for $item in PathNode/StartTimeField/xs:time(text()) return format-time($item, '[H01]:[m01] [Z]')) }\nfor-each(/*).call(block01())", translateTemplate("{ND-Root} ${BT-00-StartTime}"));
+ assertEquals("TEMPLATES:\nlet block01() -> { eval(for $item in PathNode/StartTimeField/xs:time(text()) return format-time($item, '[H01]:[m01] [Z]')) }\nMAIN:\nfor-each(/*).call(block01())", translateTemplate("{ND-Root} ${BT-00-StartTime}"));
}
}
diff --git a/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java b/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java
index 9806c4c..b030c4e 100644
--- a/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java
+++ b/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java
@@ -22,9 +22,11 @@ protected String getSdkVersion() {
void testTemplateDefinition_InvokeTemplate() {
assertEquals(
lines(
+ "TEMPLATES:",
"let some-template(string:content) -> { text('Content: ')eval($content) }",
- "let block02() -> { call(some-template(string:content='test')) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(some-template(string:content='test')) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:some-template(text:$content) display Content: ${$content};",
"invoke some-template('test');")));
@@ -34,10 +36,12 @@ void testTemplateDefinition_InvokeTemplate() {
void testTemplateDefinition_NestedInvokeTemplate() {
assertEquals(
lines(
+ "TEMPLATES:",
"let other-template(string:value) -> { text('Value: ')eval($value) }",
"let invoke-template(string:param) -> { call(other-template(string:value=$param)) }",
- "let block03() -> { call(invoke-template(string:param='test')) }",
- "for-each(/*).call(block03())"),
+ "let body03() -> { call(invoke-template(string:param='test')) }",
+ "MAIN:",
+ "for-each(/*).call(body03())"),
translateTemplate(lines(
"let template:other-template(text:$value) display Value: ${$value};",
"let template:invoke-template(text:$param) invoke other-template($param);",
@@ -48,9 +52,11 @@ void testTemplateDefinition_NestedInvokeTemplate() {
void testTemplateDefinition_ChooseTemplate() {
assertEquals(
lines(
+ "TEMPLATES:",
"let conditional-template(boolean:condition, string:value) -> { choose { when $condition: text('Condition met: ')eval($value), when $condition = false(): text('Condition not met'), otherwise: text('Unknown condition: ')eval($condition) } }",
- "let block02() -> { call(conditional-template(boolean:condition=true(), string:value='test')) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(conditional-template(boolean:condition=true(), string:value='test')) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:conditional-template(indicator:$condition, text:$value)",
"when $condition display Condition met: ${$value}",
@@ -63,9 +69,11 @@ void testTemplateDefinition_ChooseTemplate() {
void testTemplateDefinition_ParameterValidation() {
assertEquals(
lines(
+ "TEMPLATES:",
"let param-validation-template(string:param1, decimal:param2) -> { text('Valid parameters') }",
- "let block02() -> { call(param-validation-template(string:param1='test', decimal:param2=42)) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(param-validation-template(string:param1='test', decimal:param2=42)) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:param-validation-template(text:$param1, number:$param2) display Valid parameters;",
"invoke param-validation-template('test', 42);")));
@@ -79,9 +87,11 @@ void testTemplateDefinition_ParameterValidation() {
void testTemplateDeclaration_NoParameters() {
assertEquals(
lines(
+ "TEMPLATES:",
"let simple-template() -> { text('Hello World') }",
- "let block02() -> { call(simple-template()) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(simple-template()) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:simple-template() display Hello World;",
"invoke simple-template();")));
@@ -91,9 +101,11 @@ void testTemplateDeclaration_NoParameters() {
void testTemplateDeclaration_SingleParameter() {
assertEquals(
lines(
+ "TEMPLATES:",
"let greeting-template(string:name) -> { text('Hello ')eval($name) }",
- "let block02() -> { call(greeting-template(string:name='World')) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(greeting-template(string:name='World')) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:greeting-template(text:$name) display Hello ${$name};",
"invoke greeting-template('World');")));
@@ -103,9 +115,11 @@ void testTemplateDeclaration_SingleParameter() {
void testTemplateDeclaration_MultipleParameters() {
assertEquals(
lines(
+ "TEMPLATES:",
"let complex-template(string:first, decimal:second, boolean:third) -> { text('Values: ')eval($first)text(', ')eval($second)text(', ')eval($third) }",
- "let block02() -> { call(complex-template(string:first='test', decimal:second=42, boolean:third=true())) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(complex-template(string:first='test', decimal:second=42, boolean:third=true())) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:complex-template(text:$first, number:$second, indicator:$third) display Values: ${$first}, ${$second}, ${$third};",
"invoke complex-template('test', 42, TRUE);")));
@@ -115,9 +129,11 @@ void testTemplateDeclaration_MultipleParameters() {
void testTemplateDeclaration_AllParameterTypes() {
assertEquals(
lines(
+ "TEMPLATES:",
"let all-types-template(string:str, decimal:num, boolean:bool, date:dt, time:tm, duration:dur) -> { text('Params: ')eval($str)text(', ')eval($num)text(', ')eval($bool)text(', ')eval(for $item in $dt return format-date($item, '[D01]/[M01]/[Y0001]'))text(', ')eval(for $item in $tm return format-time($item, '[H01]:[m01] [Z]'))text(', ')eval($dur) }",
- "let block02() -> { call(all-types-template(string:str='text', decimal:num=123, boolean:bool=true(), date:dt=xs:date('2023-01-01'), time:tm=xs:time('12:00:00'), duration:dur=xs:dayTimeDuration('P1D'))) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(all-types-template(string:str='text', decimal:num=123, boolean:bool=true(), date:dt=xs:date('2023-01-01'), time:tm=xs:time('12:00:00'), duration:dur=xs:dayTimeDuration('P1D'))) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:all-types-template(text:$str, number:$num, indicator:$bool, date:$dt, time:$tm, measure:$dur) display Params: ${$str}, ${$num}, ${$bool}, ${$dt}, ${$tm}, ${$dur};",
"invoke all-types-template('text', 123, TRUE, date('2023-01-01'), time('12:00:00'), day-time-duration('P1D'));")));
@@ -127,9 +143,11 @@ void testTemplateDeclaration_AllParameterTypes() {
void testTemplateDeclaration_SpecialCharactersInName() {
assertEquals(
lines(
+ "TEMPLATES:",
"let template-with-dashes(string:param) -> { text('Template: ')eval($param) }",
- "let block02() -> { call(template-with-dashes(string:param='test')) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(template-with-dashes(string:param='test')) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:template-with-dashes(text:$param) display Template: ${$param};",
"invoke template-with-dashes('test');")));
@@ -142,42 +160,66 @@ void testTemplateDeclaration_SpecialCharactersInName() {
@Test
void testTemplateFragment_TextAndExpression() {
assertEquals(
- "let block01() -> { text('Value is: ')eval(./normalize-space(text())) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Value is: ')eval(./normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} Value is: ${BT-00-Text}"));
}
@Test
void testTemplateFragment_TextAndLabel() {
assertEquals(
- "let block01() -> { text('Field: ')label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Field: ')label(concat('field', '|', 'name', '|', 'BT-00-Text')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} Field: #{field|name|BT-00-Text}"));
}
@Test
void testTemplateFragment_MixedContent() {
assertEquals(
- "let block01() -> { text('Label: ')label(concat('field', '|', 'name', '|', 'BT-00-Text'))text(' Value: ')eval(./normalize-space(text()))text(' End') }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Label: ')label(concat('field', '|', 'name', '|', 'BT-00-Text'))text(' Value: ')eval(./normalize-space(text()))text(' End') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} Label: #{field|name|BT-00-Text} Value: ${BT-00-Text} End"));
}
@Test
void testTemplateFragment_OnlyExpression() {
assertEquals(
- "let block01() -> { eval(./normalize-space(text())) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { eval(./normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} ${BT-00-Text}"));
}
@Test
void testTemplateFragment_OnlyLabel() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{field|name|BT-00-Text}"));
}
@Test
void testTemplateFragment_MultipleExpressions() {
assertEquals(
- "let block01() -> { eval(./number())text(' + ')eval(./number())text(' = ')eval(./number() + ./number()) }\nfor-each(/*/PathNode/NumberField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { eval(./number())text(' + ')eval(./number())text(' = ')eval(./number() + ./number()) }",
+ "MAIN:",
+ "for-each(/*/PathNode/NumberField).call(body01())"),
translateTemplate("{BT-00-Number} ${BT-00-Number} + ${BT-00-Number} = ${BT-00-Number + BT-00-Number}"));
}
@@ -186,56 +228,119 @@ void testTemplateFragment_MultipleExpressions() {
@Test
void testTextBlock_SimpleText() {
assertEquals(
- "let block01() -> { text('Simple text content') }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Simple text content') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("display Simple text content;"));
}
@Test
void testTextBlock_WithWhitespace() {
assertEquals(
- "let block01() -> { text('Text with spaces') }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Text with spaces') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("display Text with spaces ;"));
}
@Test
void testTextBlock_WithSpecialCharacters() {
assertEquals(
- "let block01() -> { text('Text with special chars: <>&"'') }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Text with special chars: <>&"'') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("display Text with special chars: <>&\"';"));
}
@Test
void testTextBlock_MultipleTextBlocks() {
assertEquals(
- "let block01() -> { text('First block ')eval('')text(' Second block') }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('First block ')eval('')text(' Second block') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("display First block ${''} Second block;"));
}
@Test
void testTextBlock_WithNewlines() {
assertEquals(
- "let block01() -> { text('Line 1')line-break()text('Line 2')line-break()text('Line 3') }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Line 1')line-break()text('Line 2')line-break()text('Line 3') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("display Line 1 \\nLine 2\\n Line 3;"));
}
@Test
void testTextBlock_WithEscapedCharacters() {
assertEquals(
- "let block01() -> { text('Text with quotes: "' and backslash: \\\\') }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Text with quotes: "' and backslash: \\\\') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("display Text with quotes: \"' and backslash: \\\\;"));
}
// #endregion textBlock -----------------------------------------------------
+ // #region linkedTextBlock --------------------------------------------------
+
+ @Test
+ void testLinkedTextBlock() {
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('here is a ')hyperlink(text('Link'), 'http://example.com')text('. How about it?') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
+ translateTemplate("DISPLAY here is a Link@{'http://example.com'}. How about it?;"));
+ }
+
+ @Test
+ void testLinkedLabelBlock() {
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('here is a linked label: ')hyperlink(label(concat('field', '|', 'name', '|', 'BT-00-Text')), 'http://example.com')text('. How about it?') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
+ translateTemplate("DISPLAY here is a linked label: #{field|name|BT-00-Text}@{'http://example.com'}. How about it?;"));
+ }
+
+ @Test
+ void testLinkedExpressionBlock() {
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('here is a ')hyperlink(eval('multi word link'), 'http://example.com')text('. How about it?') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
+ translateTemplate("DISPLAY here is a ${'multi word link'}@{'http://example.com'}. How about it?;"));
+ }
+
+ // #endregion linkedTextBlock -----------------------------------------------
+
// #endregion templateFragment ----------------------------------------------
@Test
void testTemplate_ComplexNesting() {
assertEquals(
lines(
+ "TEMPLATES:",
"let complex-template(string:field) -> { text('name ')eval($field)text(' label ')label(concat('field', '|', 'name', '|', $field)) }",
- "let block02() -> { call(complex-template(string:field='BT-00-Text')) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(complex-template(string:field='BT-00-Text')) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:complex-template(text:$field) display name ${$field} label #{field|name|${$field}};",
"invoke complex-template('BT-00-Text');")));
@@ -249,14 +354,17 @@ void testTemplate_ComplexNesting() {
void testGlobals_VariableDeclaration() {
assertEquals(
lines(
+ "GLOBALS:",
"string:t3='a'",
"decimal:n1=12",
"string:t1=$t3",
"string:test(string:p1, decimal:p2) -> { concat($p1, $t1) }",
"decimal:n2=$n1 + 1",
"string:t4=udf:test($t3, 22)",
- "let block01(string:t2) -> { eval(PathNode/TextField/normalize-space(text())) }",
- "for-each(/*).call(block01(string:t2=udf:test($t3, 99)))"), //
+ "TEMPLATES:",
+ "let body01(string:t2) -> { eval(PathNode/TextField/normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*).call(body01(string:t2=udf:test($t3, 99)))"), //
translateTemplate(lines(
"// comment", //
"let text:$t3='a'; // comment",
@@ -273,23 +381,26 @@ void testGlobals_VariableDeclaration() {
void testGlobals_NamedTemplates() {
assertEquals(
lines(
+ "GLOBALS:",
"string:t3='a'",
"decimal:n1=12",
"string:t1=$t3",
"string:test(string:p1, decimal:p2) -> { concat($p1, $t1) }",
"decimal:n2=$n1 + 1",
"string:t4=udf:test($t3, 22)",
+ "TEMPLATES:",
"let some-template(string:text1, string:text2) -> { eval(/*/PathNode/TextField/normalize-space(text()))text(' dokimi; ')eval($text2)text(' ;')",
"for-each(/*/PathNode/NumberField).call(some-template01(string:text1=$text1, string:text2=$text2)) }",
"let some-template01(string:text1, string:text2) -> { eval(../TextField/normalize-space(text())) }",
- "let block02(string:ctx2) -> { #2: eval(./normalize-space(text()))text(' lala')",
- "for-each(../NumberField).call(block0201(string:ctx2=$ctx2, decimal:ctx3=.))",
- "for-each(.).call(block0202(string:ctx2=$ctx2, string:ctx=., string:t2=udf:test($t3, 99)))",
- "for-each(.).call(block0203(string:ctx2=$ctx2, string:ctx4=., string:t5=udf:test($t3, 99))) }",
- "let block0201(string:ctx2, decimal:ctx3) -> { eval(../TextField/normalize-space(text())) }",
- "let block0202(string:ctx2, string:ctx, string:t2) -> { call(some-template(string:text1=$ctx, string:text2=$t2)) }",
- "let block0203(string:ctx2, string:ctx4, string:t5) -> { eval(./normalize-space(text())) }",
- "for-each(/*/PathNode/TextField).call(block02(string:ctx2=.))"),
+ "let body02(string:ctx2) -> { #2: eval(./normalize-space(text()))text(' lala')",
+ "for-each(../NumberField).call(body0201(string:ctx2=$ctx2, decimal:ctx3=.))",
+ "for-each(.).call(body0202(string:ctx2=$ctx2, string:ctx=., string:t2=udf:test($t3, 99)))",
+ "for-each(.).call(body0203(string:ctx2=$ctx2, string:ctx4=., string:t5=udf:test($t3, 99))) }",
+ "let body0201(string:ctx2, decimal:ctx3) -> { eval(../TextField/normalize-space(text())) }",
+ "let body0202(string:ctx2, string:ctx, string:t2) -> { call(some-template(string:text1=$ctx, string:text2=$t2)) }",
+ "let body0203(string:ctx2, string:ctx4, string:t5) -> { eval(./normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body02(string:ctx2=.))"),
translateTemplate(lines(
"// comment", //
"let text:$t3='a';// comment",
@@ -310,11 +421,13 @@ void testGlobals_NamedTemplates() {
void testInvokeTemplate_Nesting() {
assertEquals(
lines(
+ "TEMPLATES:",
"let some-template(string:t) -> { text('--')eval($t)text('--') }",
- "let block02(string:ctx1, string:tx) -> { #1: call(some-template(string:t=$tx))",
- "for-each(../StartDateField).call(block0201(string:ctx1=$ctx1, string:tx=$tx)) }",
- "let block0201(string:ctx1, string:tx) -> { text('Nested content allowed') }",
- "for-each(/*/PathNode/TextField).call(block02(string:ctx1=., string:tx='++'))"), //
+ "let body02(string:ctx1, string:tx) -> { #1: call(some-template(string:t=$tx))",
+ "for-each(../StartDateField).call(body0201(string:ctx1=$ctx1, string:tx=$tx)) }",
+ "let body0201(string:ctx1, string:tx) -> { text('Nested content allowed') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body02(string:ctx1=., string:tx='++'))"), //
translateTemplate(lines(
"let template:some-template(text:$t) display --${$t}--;",
"with context:$ctx1 = BT-00-Text, text:$tx='++' invoke some-template($tx);",
@@ -325,6 +438,7 @@ void testInvokeTemplate_Nesting() {
void testGlobals_NoTemplateLines() {
assertEquals(
lines(
+ "GLOBALS:",
"string:t3='a'",
"decimal:n1=12",
"string:t1=$t3",
@@ -345,9 +459,12 @@ void testGlobals_NoTemplateLines() {
void testGlobals_DictionaryDeclaration() {
assertEquals(
lines(
- "let dic index /*/PathNode/NumberField by /*/PathNode/TextField/normalize-space(text());",
- "let block01() -> { eval(key('dic', 'key')) }",
- "for-each(/*).call(block01())"),
+ "GLOBALS:",
+ "let dic index /*/PathNode/NumberField by ../TextField/normalize-space(text());",
+ "TEMPLATES:",
+ "let body01() -> { eval(key('dic', 'key')) }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate(lines(
"let $dic index BT-00-Number by BT-00-Text;",
"display ${$dic['key']};")));
@@ -358,9 +475,13 @@ void testGlobals_DictionaryDeclaration() {
@Test
void testDisplayTemplate() {
assertEquals(
- lines("string:t='test'",
- "let block01() -> { text('this is a ')eval($t) }",
- "for-each(/*).call(block01())"),
+ lines(
+ "GLOBALS:",
+ "string:t='test'",
+ "TEMPLATES:",
+ "let body01() -> { text('this is a ')eval($t) }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate(lines(
"let text:$t = 'test';",
"display this is a ${$t};")));
@@ -371,7 +492,11 @@ void testDisplayTemplate() {
@Test
void testTemplateLine_NoIndentation() {
assertEquals(
- "let block01() -> { text('foo') }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('foo') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} foo"));
}
@@ -380,13 +505,20 @@ void testTemplateLine_NoIndentation() {
*/
@Test
void testTemplateLine_AutogeneratedOutline() {
- assertEquals(lines("let block01() -> { #1: text('Implicit 1 (shown)')",
- "for-each(../..).call(block0101()) }",
- "let block0101() -> { #1.1: text('Implicit 1.1 (shown)')",
- "for-each(PathNode/NumberField).call(block010101()) }",
- "let block010101() -> { text('Implicit 1.1.1 (hidden)') }",
- "for-each(/*/PathNode/TextField).call(block01())"), //
- translateTemplate(lines("{BT-00-Text} Implicit 1 (shown)", "\t{ND-Root} Implicit 1.1 (shown)", "\t\t{BT-00-Number} Implicit 1.1.1 (hidden)")));
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { #1: text('Implicit 1 (shown)')",
+ "for-each(../..).call(body0101()) }",
+ "let body0101() -> { #1.1: text('Implicit 1.1 (shown)')",
+ "for-each(PathNode/NumberField).call(body010101()) }",
+ "let body010101() -> { text('Implicit 1.1.1 (hidden)') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"), //
+ translateTemplate(lines(
+ "{BT-00-Text} Implicit 1 (shown)",
+ "\t{ND-Root} Implicit 1.1 (shown)",
+ "\t\t{BT-00-Number} Implicit 1.1.1 (hidden)")));
}
/**
@@ -395,14 +527,20 @@ void testTemplateLine_AutogeneratedOutline() {
*/
@Test
void testTemplateLine_ExplicitOutline() {
- assertEquals(lines("let block01() -> { #2: text('foo')",
- "for-each(../..).call(block0101()) }",
- "let block0101() -> { #2.3: text('bar')",
- "for-each(PathNode/NumberField).call(block010101()) }",
- "let block010101() -> { text('foo') }",
- "for-each(/*/PathNode/TextField).call(block01())"), //
- translateTemplate(
- lines("2 {BT-00-Text} foo", "\t3{ND-Root} bar", "\t\t{BT-00-Number} foo")));
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { #2: text('foo')",
+ "for-each(../..).call(body0101()) }",
+ "let body0101() -> { #2.3: text('bar')",
+ "for-each(PathNode/NumberField).call(body010101()) }",
+ "let body010101() -> { text('foo') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"), //
+ translateTemplate(lines(
+ "2 {BT-00-Text} foo",
+ "\t3{ND-Root} bar",
+ "\t\t{BT-00-Number} foo")));
}
/**
@@ -412,13 +550,20 @@ void testTemplateLine_ExplicitOutline() {
*/
@Test
void testTemplateLine_MixedOutline() {
- assertEquals(lines("let block01() -> { #2: text('foo')",
- "for-each(../..).call(block0101()) }",
- "let block0101() -> { #2.1: text('bar')",
- "for-each(PathNode/NumberField).call(block010101()) }",
- "let block010101() -> { text('foo') }",
- "for-each(/*/PathNode/TextField).call(block01())"), //
- translateTemplate(lines("2{BT-00-Text} foo", "\t{ND-Root} bar", "\t\t{BT-00-Number} foo")));
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { #2: text('foo')",
+ "for-each(../..).call(body0101()) }",
+ "let body0101() -> { #2.1: text('bar')",
+ "for-each(PathNode/NumberField).call(body010101()) }",
+ "let body010101() -> { text('foo') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"), //
+ translateTemplate(lines(
+ "2{BT-00-Text} foo",
+ "\t{ND-Root} bar",
+ "\t\t{BT-00-Number} foo")));
}
/**
@@ -428,14 +573,20 @@ void testTemplateLine_MixedOutline() {
*/
@Test
void testTemplateLine_SuppressedOutline() {
- assertEquals(lines("let block01() -> { #2: text('foo')",
- "for-each(../..).call(block0101()) }",
- "let block0101() -> { text('bar')",
- "for-each(PathNode/NumberField).call(block010101()) }",
- "let block010101() -> { text('foo') }",
- "for-each(/*/PathNode/TextField).call(block01())"), //
- translateTemplate(
- lines("2{BT-00-Text} foo", "\t0{ND-Root} bar", "\t\t{BT-00-Number} foo")));
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { #2: text('foo')",
+ "for-each(../..).call(body0101()) }",
+ "let body0101() -> { text('bar')",
+ "for-each(PathNode/NumberField).call(body010101()) }",
+ "let body010101() -> { text('foo') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"), //
+ translateTemplate(lines(
+ "2{BT-00-Text} foo",
+ "\t0{ND-Root} bar",
+ "\t\t{BT-00-Number} foo")));
}
/**
@@ -447,82 +598,111 @@ void testTemplateLine_SuppressedOutline() {
@Test
void testTemplateLine_SuppressedOutlineAtParent() {
// Outline is ignored if the line has no children
- assertEquals(lines("let block01() -> { text('foo')",
- "for-each(../..).call(block0101()) }",
- "let block0101() -> { #1: text('bar')",
- "for-each(PathNode/NumberField).call(block010101()) }",
- "let block010101() -> { text('foo') }",
- "for-each(/*/PathNode/TextField).call(block01())"), //
- translateTemplate(lines("0{BT-00-Text} foo", "\t{ND-Root} bar", "\t\t{BT-00-Number} foo")));
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('foo')",
+ "for-each(../..).call(body0101()) }",
+ "let body0101() -> { #1: text('bar')",
+ "for-each(PathNode/NumberField).call(body010101()) }",
+ "let body010101() -> { text('foo') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"), //
+ translateTemplate(lines(
+ "0{BT-00-Text} foo",
+ "\t{ND-Root} bar",
+ "\t\t{BT-00-Number} foo")));
}
@Test
void testTemplateLine_IndentationWithTabs() {
assertEquals(
- lines("let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }", //
- "let block0101() -> { text('bar') }", //
- "for-each(/*/PathNode/TextField).call(block01())"), //
- translateTemplate(lines("{BT-00-Text} foo", "\t{BT-00-Text} bar")));
+ lines("TEMPLATES:", //
+ "let body01() -> { #1: text('foo')", "for-each(.).call(body0101()) }", //
+ "let body0101() -> { text('bar') }", //
+ "MAIN:", //
+ "for-each(/*/PathNode/TextField).call(body01())"), //
+ translateTemplate(lines(
+ "{BT-00-Text} foo",
+ "\t{BT-00-Text} bar")));
}
@Test
void testTemplateLine_IndentationWithSpaces() {
assertEquals(
- lines("let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }", //
- "let block0101() -> { text('bar') }", //
- "for-each(/*/PathNode/TextField).call(block01())"), //
+ lines("TEMPLATES:", //
+ "let body01() -> { #1: text('foo')", "for-each(.).call(body0101()) }", //
+ "let body0101() -> { text('bar') }", //
+ "MAIN:", //
+ "for-each(/*/PathNode/TextField).call(body01())"), //
translateTemplate(lines("{BT-00-Text} foo", " {BT-00-Text} bar")));
}
@Test
void testTemplateLine_LowerIndentation() {
assertEquals(
- lines("let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }",
- "let block0101() -> { text('bar') }",
- "let block02() -> { text('code') }",
- "for-each(/*/PathNode/TextField).call(block01())",
- "for-each(/*/PathNode/CodeField).call(block02())"),
- translateTemplate(lines("{BT-00-Text} foo", "\t{BT-00-Text} bar", "{BT-00-Code} code")));
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { #1: text('foo')", "for-each(.).call(body0101()) }",
+ "let body0101() -> { text('bar') }",
+ "let body02() -> { text('code') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())",
+ "for-each(/*/PathNode/CodeField).call(body02())"),
+ translateTemplate(lines(
+ "{BT-00-Text} foo",
+ "\t{BT-00-Text} bar",
+ "{BT-00-Code} code")));
}
@Test
void testTemplateLine_LineJoining() {
assertEquals(
- lines("let block01() -> { #1: text('foo')", "for-each(.).call(block0101()) }",
- "let block0101() -> { text('bar joined more') }",
- "let block02() -> { text('code') }",
- "for-each(/*/PathNode/TextField).call(block01())",
- "for-each(/*/PathNode/CodeField).call(block02())"),
- translateTemplate(lines("{BT-00-Text} foo", "\t{BT-00-Text} bar \\ \n joined \\\n\\\nmore",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { #1: text('foo')", "for-each(.).call(body0101()) }",
+ "let body0101() -> { text('bar joined more') }",
+ "let body02() -> { text('code') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())",
+ "for-each(/*/PathNode/CodeField).call(body02())"),
+ translateTemplate(lines(
+ "{BT-00-Text} foo",
+ "\t{BT-00-Text} bar \\ \n joined \\\n\\\nmore",
"{BT-00-Code} code")));
}
@Test
void testTemplateLine_VariableScope() {
assertEquals(
- lines("let block01() -> { #1: eval(for $x in ./normalize-space(text()) return $x)", //
- "for-each(.).call(block0101()) }", //
- "let block0101() -> { eval(for $x in ./normalize-space(text()) return $x) }", //
- "for-each(/*/PathNode/TextField).call(block01())"), //
- translateTemplate(lines("{BT-00-Text} ${for text:$x in BT-00-Text return $x}",
+ lines(
+ "TEMPLATES:", //
+ "let body01() -> { #1: eval(for $x in ./normalize-space(text()) return $x)", //
+ "for-each(.).call(body0101()) }", //
+ "let body0101() -> { eval(for $x in ./normalize-space(text()) return $x) }", //
+ "MAIN:", //
+ "for-each(/*/PathNode/TextField).call(body01())"), //
+ translateTemplate(lines(
+ "{BT-00-Text} ${for text:$x in BT-00-Text return $x}",
" {BT-00-Text} ${for text:$x in BT-00-Text return $x}")));
-
}
@Test
void testTemplateLine_ContextVariable() {
assertEquals(
lines(
- "let block01(string:xyz, string:ctx, string:t) -> { #1: eval(for $x in ./normalize-space(text()) return concat($x, $t))", //
- "for-each(.).call(block0101(string:xyz=$xyz, string:ctx=$ctx, string:t=$t, string:t2='test'))", //
- "for-each(.).call(block0102(string:xyz=$xyz, string:ctx=$ctx, string:t=$t, string:t2='test3')) }", //
- "let block0101(string:xyz, string:ctx, string:t, string:t2) -> { #1.1: eval(for $y in ./normalize-space(text()) return concat($y, $t, $t2))", //
- "for-each(.).call(block010101(string:xyz=$xyz, string:ctx=$ctx, string:t=$t, string:t2=$t2))", //
- "for-each(.).call(block010102(string:xyz=$xyz, string:ctx=$ctx, string:t=$t, string:t2=$t2)) }", //
- "let block010101(string:xyz, string:ctx, string:t, string:t2) -> { eval(for $z in ./normalize-space(text()) return concat($z, $t, $ctx)) }", //
- "let block010102(string:xyz, string:ctx, string:t, string:t2) -> { eval(for $z in ./normalize-space(text()) return concat($z, $t, $ctx)) }", //
- "let block0102(string:xyz, string:ctx, string:t, string:t2) -> { eval(for $z in ./normalize-space(text()) return concat($z, $t2, $ctx)) }", //
- "for-each(/*/PathNode/TextField).call(block01(string:xyz='a', string:ctx=., string:t=./normalize-space(text())))"), //
+ "TEMPLATES:",
+ "let body01(string:xyz, string:ctx, string:t) -> { #1: eval(for $x in ./normalize-space(text()) return concat($x, $t))", //
+ "for-each(.).call(body0101(string:xyz=$xyz, string:ctx=$ctx, string:t=$t, string:t2='test'))", //
+ "for-each(.).call(body0102(string:xyz=$xyz, string:ctx=$ctx, string:t=$t, string:t2='test3')) }", //
+ "let body0101(string:xyz, string:ctx, string:t, string:t2) -> { #1.1: eval(for $y in ./normalize-space(text()) return concat($y, $t, $t2))", //
+ "for-each(.).call(body010101(string:xyz=$xyz, string:ctx=$ctx, string:t=$t, string:t2=$t2))", //
+ "for-each(.).call(body010102(string:xyz=$xyz, string:ctx=$ctx, string:t=$t, string:t2=$t2)) }", //
+ "let body010101(string:xyz, string:ctx, string:t, string:t2) -> { eval(for $z in ./normalize-space(text()) return concat($z, $t, $ctx)) }", //
+ "let body010102(string:xyz, string:ctx, string:t, string:t2) -> { eval(for $z in ./normalize-space(text()) return concat($z, $t, $ctx)) }", //
+ "let body0102(string:xyz, string:ctx, string:t, string:t2) -> { eval(for $z in ./normalize-space(text()) return concat($z, $t2, $ctx)) }", //
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01(string:xyz='a', string:ctx=., string:t=./normalize-space(text())))"), //
translateTemplate(lines(
"{text:$xyz='a', context:$ctx = BT-00-Text, text:$t = BT-00-Text} ${for text:$x in BT-00-Text return concat($x, $t)}",
" {BT-00-Text, text:$t2 = 'test'} ${for text:$y in BT-00-Text return concat($y, $t, $t2)}",
@@ -564,21 +744,33 @@ void testTemplateLine_ContextDeclarationShortcuts() {
@Test
void testTemplateLine_SecondaryTemplate() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'))line-break()text('some text') }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'))line-break()text('some text') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{field|name|BT-00-Text} \\n some text"));
}
@Test
void testTemplateLine_LineBreak() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'))line-break() }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'))line-break() }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{field|name|BT-00-Text} \\n"));
}
@Test
void testTemplateLine_EndOfLineComments() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'))text(' blah blah') }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'))text(' blah blah') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("{ND-Root} #{name|BT-00-Text} blah blah // comment blah blah"));
}
@@ -586,12 +778,13 @@ void testTemplateLine_EndOfLineComments() {
void testTemplateLine_Indentation_DeepNesting() {
assertEquals(
lines(
- "let block01() -> { #1: text('Level 1')",
- "for-each(.).call(block0101()) }",
- "let block0101() -> { #1.1: text('Level 2')",
- "for-each(.).call(block010101()) }",
- "let block010101() -> { text('Level 3') }",
- "for-each(/*/PathNode/TextField).call(block01())"),
+ "TEMPLATES:", "let body01() -> { #1: text('Level 1')",
+ "for-each(.).call(body0101()) }",
+ "let body0101() -> { #1.1: text('Level 2')",
+ "for-each(.).call(body010101()) }",
+ "let body010101() -> { text('Level 3') }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate(lines(
"{BT-00-Text} Level 1",
" {BT-00-Text} Level 2",
@@ -600,47 +793,107 @@ void testTemplateLine_Indentation_DeepNesting() {
// #endregion templateLine -------------------------------------------------
+
+ // #region otherSections ----------------------------------------------------
+
+ @Test
+ void testOtherSections_SummarySection() {
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Summary: ')eval(./normalize-space(text())) }",
+ "let summary01() -> { text('Summary: ')eval(./normalize-space(text())) }",
+ "let summary02() -> { text('Summary: ')eval(./normalize-space(text())) }",
+ "let summary03() -> { text('Summary: ')eval(./normalize-space(text())) }",
+ "let nav01() -> { text('Summary: ')eval(./normalize-space(text())) }",
+ "let nav02() -> { text('Summary: ')eval(./normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())",
+ "SUMMARY:",
+ "for-each(/*/PathNode/TextField).call(summary01())",
+ "for-each(/*/PathNode/TextField).call(summary02())",
+ "for-each(/*/PathNode/TextField).call(summary03())",
+ "NAV:",
+ "for-each(/*/PathNode/TextField).call(nav01())",
+ "for-each(/*/PathNode/TextField).call(nav02())"),
+ translateTemplate(lines(
+ "{BT-00-Text} Summary: ${BT-00-Text}",
+ "--- SUMMARY ---",
+ "{BT-00-Text} Summary: ${BT-00-Text}",
+ "{BT-00-Text} Summary: ${BT-00-Text}",
+ "{BT-00-Text} Summary: ${BT-00-Text}",
+ "--- NAVIGATION ---",
+ "{BT-00-Text} Summary: ${BT-00-Text}",
+ "{BT-00-Text} Summary: ${BT-00-Text}")));
+ }
+
+ // #endregion otherSections -------------------------------------------------
+
// #region Labels -----------------------------------------------------------
@Test
void testLabelBlock_StandardLabelReference() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{field|name|BT-00-Text}"));
}
@Test
void testLabelBlock_StandardLabelReferenceWithPluraliser() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'), ../NumberField/number()) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text'), ../NumberField/number()) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{field|name|BT-00-Text;${BT-00-Number}}"));
}
@Test
void testStandardLabelReference_UsingLabelTypeAsAssetId() {
assertEquals(
- "let block01() -> { label(concat('auxiliary', '|', 'text', '|', 'value')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('auxiliary', '|', 'text', '|', 'value')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{auxiliary|text|value}"));
}
@Test
void testLabelBlock_ComputedLabelReference() {
assertEquals(
- "let block01() -> { label(string-join(('field','|','name','|','BT-00-Text'), ', ')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(string-join(('field','|','name','|','BT-00-Text'), ', ')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{${string-join(('field', '|', 'name', '|', 'BT-00-Text'), ', ')}}"));
}
@Test
void testLabelBlock_ShorthandBtLabelReference() {
assertEquals(
- "let block01() -> { label(concat('business-term', '|', 'name', '|', 'BT-00')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('business-term', '|', 'name', '|', 'BT-00')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{name|BT-00}"));
}
@Test
void testLabelBlock_ShorthandFieldLabelReference() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{name|BT-00-Text}"));
}
@@ -653,42 +906,66 @@ void testLabelBlock_ShorthandBtLabelReferenceMissingLabelType() {
@Test
void testLabelBlock_ShorthandIndirectLabelReferenceForIndicator() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../IndicatorField return concat('indicator', '|', 'when', '-', $item, '|', 'BT-00-Indicator'))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(distinct-values(for $item in ../IndicatorField return concat('indicator', '|', 'when', '-', $item, '|', 'BT-00-Indicator'))) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{BT-00-Indicator}"));
}
@Test
void testLabelBlock_ShorthandIndirectLabelReferenceForCode() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../CodeField/normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(distinct-values(for $item in ../CodeField/normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{BT-00-Code}"));
}
@Test
void testLabelBlock_ShorthandIndirectLabelReferenceForInternalCode() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../InternalCodeField/normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(distinct-values(for $item in ../InternalCodeField/normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{BT-00-Internal-Code}"));
}
@Test
void testLabelBlock_ShorthandIndirectLabelReferenceForCodeAttribute() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../CodeField/@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(distinct-values(for $item in ../CodeField/@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{BT-00-CodeAttribute}"));
}
@Test
void testLabelBlock_ShorthandIndirectLabelReferenceForCodeAttributeWithSameAttributeInContext() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ../@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/CodeField/@attribute).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(distinct-values(for $item in ../@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }",
+ "MAIN:",
+ "for-each(/*/PathNode/CodeField/@attribute).call(body01())"),
translateTemplate("{BT-00-CodeAttribute} #{BT-00-CodeAttribute}"));
}
@Test
void testShorthandIndirectLabelReferenceForCodeAttribute_WithSameElementInContext() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ./@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(distinct-values(for $item in ./@attribute return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }",
+ "MAIN:",
+ "for-each(/*/PathNode/CodeField).call(body01())"),
translateTemplate("{BT-00-Code} #{BT-00-CodeAttribute}"));
}
@@ -707,14 +984,22 @@ void testShorthandIndirectLabelReferenceForAttribute() {
@Test
void testShorthandLabelReferenceFromContext_WithValueLabelTypeAndIndicatorField() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Indicator')) }\nfor-each(/*/PathNode/IndicatorField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Indicator')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/IndicatorField).call(body01())"),
translateTemplate("{BT-00-Indicator} #{name}"));
}
@Test
void testShorthandLabelReferenceFromContext_WithValueLabelTypeAndCodeField() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Code')) }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Code')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/CodeField).call(body01())"),
translateTemplate("{BT-00-Code} #{name}"));
}
@@ -727,7 +1012,11 @@ void testShorthandLabelReferenceFromContext_WithValueLabelTypeAndTextField() {
@Test
void testShorthandLabelReferenceFromContext_WithOtherLabelType() {
assertEquals(
- "let block01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('field', '|', 'name', '|', 'BT-00-Text')) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("{BT-00-Text} #{name}"));
}
@@ -740,14 +1029,22 @@ void testShorthandLabelReferenceFromContext_WithUnknownLabelType() {
@Test
void testLabelBlock_ShorthandLabelReferenceFromContext_WithNodeContext() {
assertEquals(
- "let block01() -> { label(concat('node', '|', 'name', '|', 'ND-Root')) }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(concat('node', '|', 'name', '|', 'ND-Root')) }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("{ND-Root} #{name}"));
}
@Test
void testLabelBlock_ShorthandIndirectLabelReferenceFromContextField() {
assertEquals(
- "let block01() -> { label(distinct-values(for $item in ./normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { label(distinct-values(for $item in ./normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item))) }",
+ "MAIN:",
+ "for-each(/*/PathNode/CodeField).call(body01())"),
translateTemplate("{BT-00-Code} #value"));
}
@@ -759,28 +1056,44 @@ void testLabelBlock_ShorthandIndirectLabelReferenceFromContextField_WithNodeCont
@Test
void testLabelBlock_Expression_AssetId() {
assertEquals(
- "let block01(string:assetId) -> { label(concat('field', '|', 'name', '|', $assetId)) }\nfor-each(/*).call(block01(string:assetId='BT-00-Text'))",
+ lines(
+ "TEMPLATES:",
+ "let body01(string:assetId) -> { label(concat('field', '|', 'name', '|', $assetId)) }",
+ "MAIN:",
+ "for-each(/*).call(body01(string:assetId='BT-00-Text'))"),
translateTemplate("{/, text:$assetId='BT-00-Text'} #{field|name|${$assetId}}"));
}
@Test
void testLabelBlock_Expression_LabelType() {
assertEquals(
- "let block01(string:labelType) -> { label(concat('field', '|', $labelType, '|', 'BT-00-Text')) }\nfor-each(/*).call(block01(string:labelType='name'))",
+ lines(
+ "TEMPLATES:",
+ "let body01(string:labelType) -> { label(concat('field', '|', $labelType, '|', 'BT-00-Text')) }",
+ "MAIN:",
+ "for-each(/*).call(body01(string:labelType='name'))"),
translateTemplate("{/, text:$labelType='name'} #{field|${$labelType}|BT-00-Text}"));
}
@Test
void testLabelBlock_Expression_AssetType() {
assertEquals(
- "let block01(string:assetType) -> { label(concat($assetType, '|', 'name', '|', 'BT-00-Text')) }\nfor-each(/*).call(block01(string:assetType='field'))",
+ lines(
+ "TEMPLATES:",
+ "let body01(string:assetType) -> { label(concat($assetType, '|', 'name', '|', 'BT-00-Text')) }",
+ "MAIN:",
+ "for-each(/*).call(body01(string:assetType='field'))"),
translateTemplate("{/, text:$assetType='field'} #{${$assetType}|name|BT-00-Text}"));
}
@Test
void testLabelBlock_Expression_NestedExpressions() {
assertEquals(
- "let block01(string:assetType, string:labelType, string:assetId) -> { label(concat($assetType, '|', $labelType, '|', $assetId)) }\nfor-each(/*).call(block01(string:assetType='field', string:labelType='name', string:assetId='BT-00-Text'))",
+ lines(
+ "TEMPLATES:",
+ "let body01(string:assetType, string:labelType, string:assetId) -> { label(concat($assetType, '|', $labelType, '|', $assetId)) }",
+ "MAIN:",
+ "for-each(/*).call(body01(string:assetType='field', string:labelType='name', string:assetId='BT-00-Text'))"),
translateTemplate(
"{/, text:$assetType='field', text:$labelType='name', text:$assetId='BT-00-Text'} #{${$assetType}|${$labelType}|${$assetId}}"));
}
@@ -792,14 +1105,22 @@ void testLabelBlock_Expression_NestedExpressions() {
@Test
void testExpressionBlock_ShorthandFieldValueReferenceFromContextField() {
assertEquals(
- "let block01() -> { eval(./normalize-space(text())) }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { eval(./normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*/PathNode/CodeField).call(body01())"),
translateTemplate("{BT-00-Code} $value"));
}
@Test
void testExpressionBlock_ShorthandFieldValueReferenceFromContextField_WithText() {
assertEquals(
- "let block01() -> { text('blah ')label(distinct-values(for $item in ./normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item)))text(' blah ')eval(./normalize-space(text()))text(' blah') }\nfor-each(/*/PathNode/CodeField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('blah ')label(distinct-values(for $item in ./normalize-space(text()) return concat('code', '|', 'name', '|', 'main-activity', '.', $item)))text(' blah ')eval(./normalize-space(text()))text(' blah') }",
+ "MAIN:",
+ "for-each(/*/PathNode/CodeField).call(body01())"),
translateTemplate("{BT-00-Code} blah #value blah $value blah"));
}
@@ -812,17 +1133,47 @@ void testExpressionBlock_ShorthandFieldValueReferenceFromContextField_WithNodeCo
// #region contextDeclarationBlock ------------------------------------------
+ @Test
+ void testContextDeclarationBlock_ContextFieldVariable() {
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01(string:ctx) -> { text('Context: ')eval($ctx) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01(string:ctx=.))"),
+ translateTemplate("{context:$ctx = BT-00-Text} Context: ${$ctx}"));
+ }
+
+ @Test
+ void testContextDeclarationBlock_ContextNodeVariable() {
+ assertEquals(
+ lines(
+ "TEMPLATES:",
+ "let body01(context:ctx) -> { text('Context: ')eval($ctx/PathNode/TextField/normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*).call(body01(context:ctx=.))"),
+ translateTemplate("{context:$ctx = ND-Root} Context: ${$ctx::BT-00-Text}"));
+ }
+
@Test
void testContextDeclarationBlock_MultipleVariables() {
assertEquals(
- "let block01(string:var1, decimal:var2) -> { text('Variables: ')eval($var1)text(', ')eval($var2) }\nfor-each(/*/PathNode/TextField).call(block01(string:var1='hello', decimal:var2=42))",
+ lines(
+ "TEMPLATES:",
+ "let body01(string:var1, decimal:var2) -> { text('Variables: ')eval($var1)text(', ')eval($var2) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01(string:var1='hello', decimal:var2=42))"),
translateTemplate("{text:$var1='hello', number:$var2=42, BT-00-Text} Variables: ${$var1}, ${$var2}"));
}
@Test
void testContextDeclarationBlock_VariableBeforeContext() {
assertEquals(
- "let block01(string:prefix) -> { text('Prefix: ')eval($prefix)text(' Value: ')eval(./normalize-space(text())) }\nfor-each(/*/PathNode/TextField).call(block01(string:prefix='test'))",
+ lines(
+ "TEMPLATES:",
+ "let body01(string:prefix) -> { text('Prefix: ')eval($prefix)text(' Value: ')eval(./normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01(string:prefix='test'))"),
translateTemplate("{text:$prefix='test', BT-00-Text} Prefix: ${$prefix} Value: ${BT-00-Text}"));
}
@@ -840,9 +1191,11 @@ void testContextDeclarationBlock_OnlyVariables() {
void testChooseTemplate_WhenBlock_MultipleConditions() {
assertEquals(
lines(
+ "TEMPLATES:",
"let multi-when-template(string:status) -> { choose { when $status = 'active': text('Status: Active'), when $status = 'inactive': text('Status: Inactive'), when $status = 'pending': text('Status: Pending'), otherwise: text('Status: Unknown') } }",
- "let block02() -> { call(multi-when-template(string:status='active')) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(multi-when-template(string:status='active')) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:multi-when-template(text:$status)",
"when $status == 'active' display Status: Active",
@@ -856,9 +1209,11 @@ void testChooseTemplate_WhenBlock_MultipleConditions() {
void testChooseTemplate_WhenBlock_ComplexBooleanExpressions() {
assertEquals(
lines(
+ "TEMPLATES:",
"let complex-when-template(decimal:value) -> { choose { when $value > 0 and $value < 100: text('In range'), when $value <= 0: text('Too low'), otherwise: text('Too high') } }",
- "let block02() -> { call(complex-when-template(decimal:value=50)) }",
- "for-each(/*).call(block02())"),
+ "let body02() -> { call(complex-when-template(decimal:value=50)) }",
+ "MAIN:",
+ "for-each(/*).call(body02())"),
translateTemplate(lines(
"let template:complex-when-template(number:$value)",
"when $value > 0 and $value < 100 display In range",
@@ -871,10 +1226,12 @@ void testChooseTemplate_WhenBlock_ComplexBooleanExpressions() {
void testChooseTemplate_OtherwiseBlock_InvokeTemplate() {
assertEquals(
lines(
+ "TEMPLATES:",
"let fallback-template(string:reason) -> { text('Fallback: ')eval($reason) }",
"let otherwise-invoke-template(boolean:condition, string:reason) -> { choose { when $condition: text('Condition met'), otherwise: call(fallback-template(string:reason=$reason)) } }",
- "let block03() -> { call(otherwise-invoke-template(boolean:condition=false(), string:reason='default')) }",
- "for-each(/*).call(block03())"),
+ "let body03() -> { call(otherwise-invoke-template(boolean:condition=false(), string:reason='default')) }",
+ "MAIN:",
+ "for-each(/*).call(body03())"),
translateTemplate(lines(
"let template:fallback-template(text:$reason) display Fallback: ${$reason};",
"let template:otherwise-invoke-template(indicator:$condition, text:$reason)",
@@ -886,11 +1243,14 @@ void testChooseTemplate_OtherwiseBlock_InvokeTemplate() {
@Test
void testChooseTemplate_WhenOtherwise() {
assertEquals(
- lines("let some-template(string:txt) -> { text('>')eval($txt)text('<') }",
- "let block02(string:t) -> { choose { when 1 > 2: text('foo'), when 2 < 3: text('bar'), when 3 > 3: call(some-template(string:txt='1')), otherwise: text('foo-bar') } }",
- "let block03(string:t) -> { choose { when 1 > 2: text('foo'), when 2 < 3: text('bar'), when 3 > 3: text('foo-bar'), otherwise: call(some-template(string:txt='2')) } }",
- "for-each(/*/PathNode/TextField).call(block02(string:t='test'))",
- "for-each(/*/PathNode/TextField).call(block03(string:t='test'))"),
+ lines(
+ "TEMPLATES:",
+ "let some-template(string:txt) -> { text('>')eval($txt)text('<') }",
+ "let body02(string:t) -> { choose { when 1 > 2: text('foo'), when 2 < 3: text('bar'), when 3 > 3: call(some-template(string:txt='1')), otherwise: text('foo-bar') } }",
+ "let body03(string:t) -> { choose { when 1 > 2: text('foo'), when 2 < 3: text('bar'), when 3 > 3: text('foo-bar'), otherwise: call(some-template(string:txt='2')) } }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body02(string:t='test'))",
+ "for-each(/*/PathNode/TextField).call(body03(string:t='test'))"),
translateTemplate(lines(
"// test",
"let template:some-template(text:$txt) display >${$txt}<;",
@@ -909,9 +1269,13 @@ void testChooseTemplate_WhenOtherwise() {
@Test
void testChooseTemplate_WhenNoOtherwise() {
assertEquals(
- lines("string:t='text'",
- "let block01() -> { choose { when true(): eval(./normalize-space(text()))text(' is a ')eval($t), otherwise nothing } }",
- "for-each(/*/PathNode/TextField[true()]).call(block01())"),
+ lines(
+ "GLOBALS:",
+ "string:t='text'",
+ "TEMPLATES:",
+ "let body01() -> { choose { when true(): eval(./normalize-space(text()))text(' is a ')eval($t), otherwise nothing } }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField[true()]).call(body01())"),
translateTemplate(lines(
"let text:$t = 'text';",
"with BT-00-Text[TRUE] when TRUE display ${BT-00-Text} is a ${$t};")));
@@ -920,9 +1284,13 @@ void testChooseTemplate_WhenNoOtherwise() {
@Test
void testChooseTemplate_WhenNoOtherwiseNoContext() {
assertEquals(
- lines("string:t='test'",
- "let block01() -> { choose { when true(): text('this is a ')eval($t), otherwise nothing } }",
- "for-each(/*).call(block01())"),
+ lines(
+ "GLOBALS:",
+ "string:t='test'",
+ "TEMPLATES:",
+ "let body01() -> { choose { when true(): text('this is a ')eval($t), otherwise nothing } }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate(lines(
"let text:$t = 'test';",
"when TRUE display this is a ${$t};")));
@@ -935,7 +1303,11 @@ void testChooseTemplate_WhenNoOtherwiseNoContext() {
@Test
void testTemplateVariableList_WithAllDataTypes() {
assertEquals(
- "let block01(string:str, decimal:num, boolean:bool, date:dt, time:tm, duration:dur) -> { text('All types: ')eval($str)text(', ')eval($num)text(', ')eval($bool)text(', ')eval(for $item in $dt return format-date($item, '[D01]/[M01]/[Y0001]'))text(', ')eval(for $item in $tm return format-time($item, '[H01]:[m01] [Z]'))text(', ')eval($dur) }\nfor-each(/*).call(block01(string:str='text', decimal:num=42, boolean:bool=true(), date:dt=xs:date('2023-01-01'), time:tm=xs:time('12:00:00'), duration:dur=xs:dayTimeDuration('P1D')))",
+ lines(
+ "TEMPLATES:",
+ "let body01(string:str, decimal:num, boolean:bool, date:dt, time:tm, duration:dur) -> { text('All types: ')eval($str)text(', ')eval($num)text(', ')eval($bool)text(', ')eval(for $item in $dt return format-date($item, '[D01]/[M01]/[Y0001]'))text(', ')eval(for $item in $tm return format-time($item, '[H01]:[m01] [Z]'))text(', ')eval($dur) }",
+ "MAIN:",
+ "for-each(/*).call(body01(string:str='text', decimal:num=42, boolean:bool=true(), date:dt=xs:date('2023-01-01'), time:tm=xs:time('12:00:00'), duration:dur=xs:dayTimeDuration('P1D')))"),
translateTemplate(
"{/, text:$str='text', number:$num=42, indicator:$bool=TRUE, date:$dt=date('2023-01-01'), time:$tm=time('12:00:00'), measure:$dur=day-time-duration('P1D')} All types: ${$str}, ${$num}, ${$bool}, ${$dt}, ${$tm}, ${$dur}"));
}
@@ -943,7 +1315,11 @@ void testTemplateVariableList_WithAllDataTypes() {
@Test
void testTemplateVariableList_ExpressionInitializers() {
assertEquals(
- "let block01(string:computed) -> { text('Computed: ')eval($computed) }\nfor-each(/*/PathNode/TextField).call(block01(string:computed=concat('prefix-', ./normalize-space(text()))))",
+ lines(
+ "TEMPLATES:",
+ "let body01(string:computed) -> { text('Computed: ')eval($computed) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01(string:computed=concat('prefix-', ./normalize-space(text()))))"),
translateTemplate("{BT-00-Text, text:$computed=concat('prefix-', BT-00-Text)} Computed: ${$computed}"));
}
@@ -954,14 +1330,22 @@ void testTemplateVariableList_ExpressionInitializers() {
@Test
void testTemplateLine_OutlineNumber_Only() {
assertEquals(
- "let block01() -> { text('text') }\nfor-each(/*).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('text') }",
+ "MAIN:",
+ "for-each(/*).call(body01())"),
translateTemplate("1 display text;"));
}
@Test
void testTemplateLine_OutlineNumber_WithContext() {
assertEquals(
- "let block01() -> { text('Value: ')eval(./normalize-space(text())) }\nfor-each(/*/PathNode/TextField).call(block01())",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { text('Value: ')eval(./normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*/PathNode/TextField).call(body01())"),
translateTemplate("1 {BT-00-Text} Value: ${BT-00-Text}"));
}
@@ -1054,15 +1438,19 @@ void testTemplateDefinition_InvalidTooFewParameters() {
// #endregion IllegalArgumentException --------------------------------------
@Test
- void testContextulizer_WithPredicate() {
+ void testContextualizer_WithPredicate() {
assertEquals(
- lines("let block01() -> { #1: text('line1: ')eval(.[1 = 1]/normalize-space(text()))",
- "for-each(.[1 = 2]).call(block0101()) }",
- "let block0101() -> { #1.1: text('line2: ')eval(.[1 = 4]/normalize-space(text()))",
- "for-each(.[1 = 4]).call(block010101()) }",
- "let block010101() -> { text('line3: ')eval(.[1 = 5]/normalize-space(text())) }",
- "for-each(/*/SubNode/SubSubNode/SubTextField[0 = 0]).call(block01())"),
- translateTemplate(lines("{BT-01-SubSubNode-Text} line1: ${BT-01-SubSubNode-Text[1==1]}",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { #1: text('line1: ')eval(.[1 = 1]/normalize-space(text()))",
+ "for-each(.[1 = 2]).call(body0101()) }",
+ "let body0101() -> { #1.1: text('line2: ')eval(.[1 = 4]/normalize-space(text()))",
+ "for-each(.[1 = 4]).call(body010101()) }",
+ "let body010101() -> { text('line3: ')eval(.[1 = 5]/normalize-space(text())) }",
+ "MAIN:",
+ "for-each(/*/SubNode/SubSubNode/SubTextField[0 = 0]).call(body01())"),
+ translateTemplate(lines(
+ "{BT-01-SubSubNode-Text} line1: ${BT-01-SubSubNode-Text[1==1]}",
" {BT-01-SubSubNode-Text[1==2]} line2: ${BT-01-SubSubNode-Text[1==4]}",
" {BT-01-SubSubNode-Text[1==4]} line3: ${BT-01-SubSubNode-Text[1==5]}")));
}
@@ -1070,13 +1458,15 @@ void testContextulizer_WithPredicate() {
@Test
void testContextualizer_WithFieldInPredicate() {
assertEquals(
- lines("let block01() -> { #1: text('line1')",
- "for-each(SubTextField).call(block0101()) }",
- "let block0101() -> { text('line2') }",
- "for-each(/*/SubNode[SubTextField]).call(block01())"),
- translateTemplate(lines("{ND-SubNode[BT-01-SubNode-Text is present]} line1",
+ lines(
+ "TEMPLATES:",
+ "let body01() -> { #1: text('line1')",
+ "for-each(SubTextField).call(body0101()) }",
+ "let body0101() -> { text('line2') }",
+ "MAIN:",
+ "for-each(/*/SubNode[SubTextField]).call(body01())"),
+ translateTemplate(lines(
+ "{ND-SubNode[BT-01-SubNode-Text is present]} line1",
" {BT-01-SubNode-Text} line2")));
}
-
-
}
\ No newline at end of file