Skip to content

Commit

Permalink
Merge pull request #74 from OP-TED/efx-2
Browse files Browse the repository at this point in the history
The work on EFX-2 features is transfered from the efx-2 branch to develop.
  • Loading branch information
rousso authored Jul 26, 2023
2 parents c5da697 + 82250bd commit 757f2f0
Show file tree
Hide file tree
Showing 14 changed files with 6,260 additions and 72 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>eu.europa.ted.eforms</groupId>
<artifactId>eforms-notice-viewer</artifactId>
<version>0.8.0</version>
<version>0.9.0-SNAPSHOT</version>

<name>eForms Notice Viewer</name>
<description>eForms Notice Viewer sample application.</description>
Expand Down Expand Up @@ -54,7 +54,7 @@
<maven.compiler.target>${java.version}</maven.compiler.target>

<!-- Versions - eForms -->
<version.efx-toolkit>1.3.0</version.efx-toolkit>
<version.efx-toolkit>2.0.0-SNAPSHOT</version.efx-toolkit>
<version.eforms-core>1.0.5</version.eforms-core>

<!-- Versions - Third-party libraries -->
Expand Down
10 changes: 0 additions & 10 deletions src/main/java/eu/europa/ted/eforms/viewer/DependencyFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ public SymbolResolver createSymbolResolver(String sdkVersion) {
}
}

@Override
public ScriptGenerator createScriptGenerator(String sdkVersion) {
throw new UnsupportedOperationException("Deprecated method, not used: 'createScriptGenerator(sdkVersion)'");
}

@Override
public ScriptGenerator createScriptGenerator(String sdkVersion, TranslatorOptions options) {
try {
Expand All @@ -47,11 +42,6 @@ public ScriptGenerator createScriptGenerator(String sdkVersion, TranslatorOption
}
}

@Override
public MarkupGenerator createMarkupGenerator(String sdkVersion) {
throw new UnsupportedOperationException("Deprecated method, not used: 'createMarkupGenerator(sdkVersion)'");
}

@Override
public MarkupGenerator createMarkupGenerator(String sdkVersion, TranslatorOptions options) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public String getPrimaryLanguage() {

/**
* @return A list of other languages
* @throws XPathExpressionException
*/
public List<String> getOtherLanguages() throws XPathExpressionException {
return Optional
Expand Down
56 changes: 32 additions & 24 deletions src/main/java/eu/europa/ted/eforms/viewer/NoticeViewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,33 +46,41 @@ private NoticeViewer(Builder builder) {
}

/**
* Creates a HTML file with the contents generated by applying XSL transformation on a notice's
* XML using a XSL template.
* <p>
* Creates a HTML file with the contents generated by applying XSL
* transformation on a notice's XML using a XSL template.
* This XSL template is generated internally, based on SDK files.
* <p>
* The SDK root directory is expected to contain a directory per minor SDK version.
* The SDK root directory is expected to contain a directory per minor SDK
* version.
*
* @param language The language as a two letter code. If set, it will be used as the primary
* language for the translation
* @param viewId An optional SDK view id to used for loading the corresponding EFX template.
* <p>
* This can be used to enforce a custom view like notice summary. It could fail if this
* custom view is not compatible with the notice sub type.
* <p>
* If not given, then the notice sub type ID from the notice XML will be used.
* @param notice A {@link NoticeDocument} object containing the notice's XML contents and metadata
* @return The path of the generated HTML file
* @param sdkRoot Path of the root SDK directory
* @param symbols The {@link DecimalFormat} to use for the translation
* @param forceBuild If true, forces the re-creation of XSL (re-creates cache entries)
* @param language The language as a two letter code. If set, it will be used
* as the primary
* language for the translation
* @param viewId An optional SDK view id to used for loading the
* corresponding EFX template.
* This can be used to enforce a custom view like notice
* summary. It could fail if this
* custom view is not compatible with the notice sub type.
* If not given, then the notice sub type ID from the notice
* XML will be used.
* @param notice A {@link NoticeDocument} object containing the notice's XML
* contents and metadata
* @param outputFile The path of the generated HTML file
* @param sdkRoot Path of the root SDK directory
* @param symbols The {@link DecimalFormat} to use for the translation
* @param forceBuild If true, forces the re-creation of XSL (re-creates cache
* entries)
*
* @return The path of the generated HTML file
* @throws IOException when the XML/XSL contents cannot be loaded
* @throws TransformerException when the XSL transformation fails
* @throws SAXException when the notice XML cannot be parsed
* @throws ParserConfigurationException when the XML parser is not configured properly
* @throws XPathExpressionException when an error occurs while extracting language information
* from the notice document
*
* @throws IOException when the XML/XSL contents cannot be
* loaded
* @throws TransformerException when the XSL transformation fails
* @throws SAXException when the notice XML cannot be parsed
* @throws ParserConfigurationException when the XML parser is not configured
* properly
* @throws XPathExpressionException when an error occurs while extracting
* language information
* from the notice document
*/
public Path generateHtmlFile(final String language, String viewId, final NoticeDocument notice,
final Path outputFile, final Path sdkRoot, final DecimalFormat symbols,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,33 @@
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.Pair;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.europa.ted.eforms.sdk.component.SdkComponent;
import eu.europa.ted.eforms.sdk.component.SdkComponentType;
import eu.europa.ted.eforms.viewer.enums.FreemarkerTemplate;
import eu.europa.ted.eforms.viewer.util.FreemarkerHelper;
import eu.europa.ted.eforms.viewer.util.xml.XmlHelper;
import eu.europa.ted.efx.interfaces.MarkupGenerator;
import eu.europa.ted.efx.interfaces.TranslatorOptions;
import eu.europa.ted.efx.model.Expression;
import eu.europa.ted.efx.model.Expression.PathExpression;
import eu.europa.ted.efx.model.Expression.StringExpression;
import eu.europa.ted.efx.model.Markup;
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;
import eu.europa.ted.efx.model.expressions.scalar.StringExpression;
import eu.europa.ted.efx.model.templates.Markup;

@SdkComponent(versions = {"1"}, componentType = SdkComponentType.MARKUP_GENERATOR)
@SdkComponent(versions = {"1", "2"}, componentType = SdkComponentType.MARKUP_GENERATOR)
public class XslMarkupGenerator implements MarkupGenerator {
private static final Logger logger = LoggerFactory.getLogger(XslMarkupGenerator.class);

Expand All @@ -42,9 +47,9 @@ protected String[] getAssetTypes() {
return new String[] {"business-term", "field", "code", "auxiliary"};
}

private final String translations = Arrays.stream(getAssetTypes())
private final String translations = "(" + Arrays.stream(getAssetTypes())
.map(assetType -> "fn:document(concat('" + assetType + "_' , $LANGUAGE, '.xml'))")
.collect(Collectors.joining(", "));
.collect(Collectors.joining(", ")) + ")";

private static List<String> markupsListToStringList(List<Markup> markupsList) {
return Optional.ofNullable(markupsList).orElse(Collections.emptyList()).stream()
Expand All @@ -62,6 +67,7 @@ private static final Markup generateMarkup(final FreemarkerTemplate template,
Arrays.asList(Optional.ofNullable(params)
.orElseGet(Pair::emptyArray))
.stream()
.filter(pair -> pair.getKey() != null && pair.getValue() != null)
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));

try (StringWriter writer = new StringWriter()) {
Expand Down Expand Up @@ -107,24 +113,37 @@ public Markup renderVariableExpression(final Expression valueReference) {

return generateMarkup(
FreemarkerTemplate.VARIABLE_EXPRESSION,
Pair.of("expression", valueReference.script));
Pair.of("expression", valueReference.getScript()));
}

@Override
public Markup renderLabelFromKey(final StringExpression key) {
return this.renderLabelFromKey(key, NumericExpression.empty());
}

@Override
public Markup renderLabelFromKey(final StringExpression key, NumericExpression quantity) {
logger.trace("Rendering label from key [{}]", key);

return generateMarkup(FreemarkerTemplate.LABEL_FROM_KEY, Pair.of("key", key.script));
return generateMarkup(FreemarkerTemplate.LABEL_FROM_KEY,
Pair.of("key", key.getScript()),
Pair.of("quantity", quantity.getScript()));
}

@Override
public Markup renderLabelFromExpression(final Expression expression) {
return this.renderLabelFromExpression(expression, NumericExpression.empty());
}

@Override
public Markup renderLabelFromExpression(final Expression expression, NumericExpression quantity) {
logger.trace("Rendering label from expression [{}]", expression);

return generateMarkup(
FreemarkerTemplate.LABEL_FROM_EXPRESSION,
Pair.of("expression", expression.script),
Pair.of("labelSuffix", String.valueOf(++variableCounter)));
Pair.of("expression", expression.getScript()),
Pair.of("variableSuffix", String.valueOf(++variableCounter)),
Pair.of("quantity", quantity.getScript()));
}

@Override
Expand All @@ -136,25 +155,41 @@ public Markup renderFreeText(final String freeText) {
}

@Override
public Markup composeFragmentDefinition(final String name, final String number,
final Markup content) {
logger.trace("Composing fragment definition with: name={}, number={}, content={}", name, number,
content);
public Markup renderLineBreak() {
return new Markup("<br/>");
}

@Override
public Markup composeFragmentDefinition(final String name, final String number, final Markup content) {
return this.composeFragmentDefinition(name, number, content, new LinkedHashSet<String>());
}

@Override
public Markup composeFragmentDefinition(String name, String number, Markup content, Set<String> parameters) {
logger.trace("Composing fragment definition with: name={}, number={}, content={}", name, number, content);

return generateMarkup(
FreemarkerTemplate.FRAGMENT_DEFINITION,
Pair.of("content", content.script),
Pair.of("name", name),
Pair.of("number", number));
Pair.of("number", number),
Pair.of("parameters", parameters));
}

@Override
public Markup renderFragmentInvocation(final String name, final PathExpression context) {
logger.trace("Rendering fragment invocation with: name={}, context={}", name, context.script);
return this.renderFragmentInvocation(name, context, new LinkedHashSet<Pair<String, String>>());
}

@Override
public Markup renderFragmentInvocation(final String name, final PathExpression context, final Set<Pair<String, String>> variables) {
logger.trace("Rendering fragment invocation with: name={}, context={}", name, context.getScript());

return generateMarkup(
FreemarkerTemplate.FRAGMENT_INVOCATION,
Pair.of("context", context.script),
Pair.of("name", name));
Pair.of("context", context.getScript()),
Pair.of("name", name),
Pair.of("variables", variables.stream().map(variable -> new String[] { variable.getKey(), variable.getValue() }).toArray())
);
}
}
10 changes: 5 additions & 5 deletions src/main/java/eu/europa/ted/eforms/viewer/util/CacheHelper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package eu.europa.ted.eforms.viewer.util;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.function.Supplier;
Expand All @@ -10,7 +11,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.ted.eforms.viewer.NoticeViewerConstants;
import eu.europa.ted.efx.model.Markup;
import eu.europa.ted.efx.model.templates.Markup;

/**
* Utility class with methods for managing the caching of objects.
Expand Down Expand Up @@ -122,10 +123,9 @@ public static String computeKey(String... strings) {
Validate.notEmpty(strings, "The array of strings cannot be empty");

try {
String key = new String(
MessageDigest.getInstance("SHA-512")
.digest((StringUtils.join(strings, "###").getBytes())));

byte[] bytes = MessageDigest.getInstance("SHA-512").digest((StringUtils.join(strings, "###").getBytes()));
BigInteger number = new BigInteger(1, bytes);
String key = StringUtils.leftPad(number.toString(16), 32, '0');
logger.trace("Computed key for [{}]: {}", strings, key);

return key;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
- number: Outline number
-->

<xsl:template name='${name}'>
<xsl:template name="${name}">
<#if parameters??>
<#list parameters as parameter>
<xsl:param name="${parameter}" />
</#list>
</#if>
<section title="${name}">
<#if number?has_content>
<xsl:text>${number}&#160;</xsl:text>
Expand Down
11 changes: 8 additions & 3 deletions src/main/resources/templates/xsl_markup/fragment_invocation.ftl
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
<#--
Available variables:
- context: Context path
- name: Content block ID
- name: Content block identifier
- context: Context XPath
- variables: Lits of additional variables (optional)
-->

<xsl:for-each select="${context}">
<xsl:call-template name="${name}"/>
<xsl:call-template name="${name}">
<#list variables as variable>
<xsl:with-param name="${variable[0]}" select = "${variable[1]}" />
</#list>
</xsl:call-template>
</xsl:for-each>
28 changes: 22 additions & 6 deletions src/main/resources/templates/xsl_markup/label_from_expression.ftl
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
<#--
Available variables:
- expression: The expression to use for rendering
- labelSuffix: Suffix for labels
- expression: Expression that generates the label key.
- variableSuffix: Suffix provided for uniqueness of variable names.
- quantity: A numeric quantity used to decide if the label needs to be pluralized.
-->
<span class="dynamic-label">
<xsl:variable name="labels${labelSuffix}" as="xs:string*">
<#--
The expression may return a sequence, so we will iterate over each label returned by the expression.
In the process we will collect the labels into a variable.
After we are done iterating, we will join the labels together in a comma separated list.
-->
<xsl:variable name="labels${variableSuffix}" as="xs:string*">
<xsl:for-each select="${expression}">
<xsl:variable name="label${labelSuffix}" select="."/>
<xsl:value-of select="($labels//entry[@key=$label${labelSuffix}]/text(), concat('{', $label${labelSuffix}, '}'))[1]"/>
<#if quantity?has_content>
<xsl:variable name="singular${variableSuffix}" select="."/>
<xsl:variable name="plural${variableSuffix}" select="concat(., ted:plural-label-suffix(${quantity}))"/>
<#-- This will fallback to the singual form if a pluralised label does not exist. -->
<#-- If the singular form label does not exist either, then the label key will be shown instead. -->
<xsl:value-of select="($labels//entry[@key=$plural${variableSuffix}]/text(), $labels//entry[@key=$singular${variableSuffix}]/text(), concat('{', $singular${variableSuffix}, '}'))[1]"/>
<#else>
<xsl:variable name="label${variableSuffix}" select="."/>
<#-- If the label does not exist, then the label key is displayed instead. -->
<xsl:value-of select="($labels//entry[@key=$label${variableSuffix}]/text(), concat('{', $label${variableSuffix}, '}'))[1]"/>
</#if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="string-join($labels${labelSuffix}, ', ')"/>

<xsl:value-of select="string-join($labels${variableSuffix}, ', ')"/>
</span>
8 changes: 7 additions & 1 deletion src/main/resources/templates/xsl_markup/label_from_key.ftl
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<#--
Available variables:
- key: The key to use for rendering
- quantity: If pluralisation is required, then this contains the suffix that identifies the plural form of the label.
-->
<span class="label"><xsl:value-of select="($labels//entry[@key=${key}]/text(), concat('{', ${key}, '}'))[1]"/></span>
<#if quantity?has_content>
<xsl:variable name="plural" select="concat(${key}, ted:plural-label-suffix(${quantity}))"/>
<span class="label"><xsl:value-of select="($labels//entry[@key=$plural]/text(), $labels//entry[@key=${key}}]/text(), concat('{', ${key}, '}'))[1]"/></span>
<#else>
<span class="label"><xsl:value-of select="($labels//entry[@key=${key}]/text(), concat('{', ${key}, '}'))[1]"/></span>
</#if>
1 change: 1 addition & 0 deletions src/main/resources/templates/xsl_markup/output_file.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
</html>
</xsl:template>

<#-- The templates are called by the markup inserted in the body above. -->
<#list templates as template>
${template}
</#list>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class NoticeViewerTest {
private static final String[] SOURCE_NOTICE_XML_FILENAMES2 =
new String[] {"16_cn_24_minimal-test"};

private static final String[] SOURCE_SDK_VERSIONS = new String[] {"1.0"};
private static final String[] SOURCE_SDK_VERSIONS = new String[] {"1.0", "2.0"};

private static final Path SDK_ROOT_DIR = NoticeViewerConstants.DEFAULT_SDK_ROOT_DIR;

Expand Down
Loading

0 comments on commit 757f2f0

Please sign in to comment.