You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2023/01/13 12:22:32 UTC
[myfaces-tobago] branch main updated: feat: message-format for tc:out
This is an automated email from the ASF dual-hosted git repository.
hnoeth pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
The following commit(s) were added to refs/heads/main by this push:
new 957ad7c84f feat: message-format for tc:out
957ad7c84f is described below
commit 957ad7c84f9df815a64a195ff04ccd39df18add0
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Fri Jan 13 13:15:10 2023 +0100
feat: message-format for tc:out
* support for f:param to tc:out
Issue: TOBAGO-2177
---
.../myfaces/tobago/component/Attributes.java | 1 +
.../tobago/internal/component/AbstractUIOut.java | 2 +
.../internal/renderkit/renderer/OutRenderer.java | 62 +++++++++++++++++++++-
.../taglib/component/OutTagDeclaration.java | 15 +++++-
.../internal/config/AbstractTobagoTestBase.java | 20 ++++---
.../renderkit/renderer/OutRendererUnitTest.java | 23 ++++++++
.../src/test/resources/renderer/out/outFormat.html | 18 +++++++
.../tobago/example/demo/DemoBundle.properties | 1 +
.../tobago/example/demo/DemoBundle_de.properties | 1 +
.../webapp/content/020-output/10-out/Out.xhtml | 14 ++++-
10 files changed, 141 insertions(+), 16 deletions(-)
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Attributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Attributes.java
index 0ad982eff0..5b29c108fa 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Attributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/component/Attributes.java
@@ -215,6 +215,7 @@ public enum Attributes {
maxWidth,
media,
method,
+ messageFormat,
min,
minHeight,
minWidth,
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIOut.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIOut.java
index 619c508b4b..ea0b1631e0 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIOut.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIOut.java
@@ -49,6 +49,8 @@ public abstract class AbstractUIOut extends UIOutput implements SupportsLabelLay
@Deprecated
public abstract boolean isCompact();
+ public abstract boolean isMessageFormat();
+
public abstract SanitizeMode getSanitize();
@Override
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRenderer.java
index 0ec33557d8..94a707d02d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRenderer.java
@@ -19,6 +19,9 @@
package org.apache.myfaces.tobago.internal.renderkit.renderer;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UIParameter;
+import jakarta.faces.context.FacesContext;
import org.apache.myfaces.tobago.config.TobagoConfig;
import org.apache.myfaces.tobago.context.Markup;
import org.apache.myfaces.tobago.internal.component.AbstractUIOut;
@@ -30,14 +33,22 @@ import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
import org.apache.myfaces.tobago.sanitizer.SanitizeMode;
import org.apache.myfaces.tobago.sanitizer.Sanitizer;
import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
-
-import jakarta.faces.context.FacesContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
import java.util.StringTokenizer;
public class OutRenderer<T extends AbstractUIOut> extends MessageLayoutRendererBase<T> {
+ private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
@Override
protected boolean isOutputOnly(T component) {
return true;
@@ -106,6 +117,10 @@ public class OutRenderer<T extends AbstractUIOut> extends MessageLayoutRendererB
text = "";
}
+ if (out.isMessageFormat()) {
+ text = getOutputFormatText(facesContext, text, out);
+ }
+
if (escape) {
if (keepLineBreaks) {
final StringTokenizer tokenizer = new StringTokenizer(text, "\r\n");
@@ -130,6 +145,49 @@ public class OutRenderer<T extends AbstractUIOut> extends MessageLayoutRendererB
}
}
+ private String getOutputFormatText(final FacesContext facesContext, final String pattern, final T out) {
+ final Object[] args;
+ if (out.getChildCount() > 0) {
+ final List<UIParameter> validParams = getValidUIParameterChildren(out);
+ if (!validParams.isEmpty()) {
+ List<Object> argsList = new ArrayList<>(validParams.size());
+ for (UIParameter param : validParams) {
+ argsList.add(param.getValue());
+ }
+ args = argsList.toArray(EMPTY_OBJECT_ARRAY);
+ } else {
+ args = EMPTY_OBJECT_ARRAY;
+ }
+ } else {
+ args = EMPTY_OBJECT_ARRAY;
+ }
+
+ final MessageFormat format = new MessageFormat(pattern, facesContext.getViewRoot().getLocale());
+ try {
+ return format.format(args);
+ } catch (Exception e) {
+ LOG.error("Error formatting message of component with clientId='{}'", out.getClientId(facesContext));
+ return pattern;
+ }
+ }
+
+ private List<UIParameter> getValidUIParameterChildren(T out) {
+
+ List<UIParameter> parameters = new ArrayList<>();
+ for (UIComponent child : out.getChildren()) {
+ if (child instanceof UIParameter) {
+ UIParameter param = (UIParameter) child;
+ if (param.isDisable() || !param.isRendered()) {
+ // ignore
+ continue;
+ }
+ parameters.add(param);
+ }
+ }
+
+ return parameters;
+ }
+
@Override
public void encodeEndField(final FacesContext facesContext, final T component) throws IOException {
final TobagoResponseWriter writer = getResponseWriter(facesContext);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/OutTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/OutTagDeclaration.java
index 86271ec4e0..8813a028b5 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/OutTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/OutTagDeclaration.java
@@ -19,12 +19,14 @@
package org.apache.myfaces.tobago.internal.taglib.component;
+import jakarta.faces.component.UIOutput;
import org.apache.myfaces.tobago.apt.annotation.BodyContentDescription;
import org.apache.myfaces.tobago.apt.annotation.Tag;
import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.internal.taglib.declaration.HasAutoSpacing;
import org.apache.myfaces.tobago.internal.taglib.declaration.HasConverter;
import org.apache.myfaces.tobago.internal.taglib.declaration.HasIdBindingAndRendered;
import org.apache.myfaces.tobago.internal.taglib.declaration.HasLabel;
@@ -32,11 +34,10 @@ import org.apache.myfaces.tobago.internal.taglib.declaration.HasLabelLayout;
import org.apache.myfaces.tobago.internal.taglib.declaration.HasSanitize;
import org.apache.myfaces.tobago.internal.taglib.declaration.HasTip;
import org.apache.myfaces.tobago.internal.taglib.declaration.HasValue;
-import org.apache.myfaces.tobago.internal.taglib.declaration.HasAutoSpacing;
import org.apache.myfaces.tobago.internal.taglib.declaration.IsPlain;
import org.apache.myfaces.tobago.internal.taglib.declaration.IsVisual;
-import jakarta.faces.component.UIOutput;
+import java.text.MessageFormat;
/**
* Renders a text
@@ -98,4 +99,14 @@ public interface OutTagDeclaration
@TagAttribute
@UIComponentTagAttribute(type = "boolean", defaultValue = "false")
void setCompact(String compact);
+
+ /**
+ * Activates formatting of the value with the method {@link MessageFormat#format(String, Object...)}
+ * A parameters the values of the children of type {@link jakarta.faces.component.UIParameter} are used.
+ *
+ * @since 5.5.0
+ */
+ @TagAttribute
+ @UIComponentTagAttribute(type = "boolean", defaultValue = "false")
+ void setMessageFormat(String messageFormat);
}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
index d53678b358..72f1010306 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
@@ -19,6 +19,11 @@
package org.apache.myfaces.tobago.internal.config;
+import jakarta.faces.component.UIParameter;
+import jakarta.faces.component.behavior.AjaxBehavior;
+import jakarta.faces.convert.DateTimeConverter;
+import jakarta.faces.render.RenderKit;
+import jakarta.servlet.ServletContext;
import org.apache.myfaces.test.base.junit4.AbstractJsfTestCase;
import org.apache.myfaces.test.config.ResourceBundleVarNames;
import org.apache.myfaces.test.mock.MockFacesContext;
@@ -64,9 +69,6 @@ import org.apache.myfaces.tobago.component.UITreeIndent;
import org.apache.myfaces.tobago.component.UITreeNode;
import org.apache.myfaces.tobago.component.UITreeSelect;
import org.apache.myfaces.tobago.config.TobagoConfig;
-
-import static org.apache.myfaces.tobago.config.TobagoConfig.TOBAGO_CONFIG;
-
import org.apache.myfaces.tobago.context.TobagoContext;
import org.apache.myfaces.tobago.internal.behavior.EventBehavior;
import org.apache.myfaces.tobago.internal.renderkit.renderer.BadgeRenderer;
@@ -108,23 +110,18 @@ import org.apache.myfaces.tobago.internal.renderkit.renderer.TreeNodeRenderer;
import org.apache.myfaces.tobago.internal.renderkit.renderer.TreeRenderer;
import org.apache.myfaces.tobago.internal.renderkit.renderer.TreeSelectRenderer;
import org.apache.myfaces.tobago.internal.webapp.HtmlResponseWriter;
-
-import static org.apache.myfaces.tobago.util.ResourceUtils.TOBAGO_RESOURCE_BUNDLE;
-
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
-import jakarta.faces.component.behavior.AjaxBehavior;
-import jakarta.faces.convert.DateTimeConverter;
-import jakarta.faces.render.RenderKit;
-import jakarta.servlet.ServletContext;
-
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Locale;
+import static org.apache.myfaces.tobago.config.TobagoConfig.TOBAGO_CONFIG;
+import static org.apache.myfaces.tobago.util.ResourceUtils.TOBAGO_RESOURCE_BUNDLE;
+
/**
* <p>Abstract JUnit test case base class, which sets up the JavaServer Faces
* mock object environment for a particular simulated request.
@@ -201,6 +198,7 @@ public abstract class AbstractTobagoTestBase extends AbstractJsfTestCase {
application.addComponent(Tags.treeNode.componentType(), UITreeNode.class.getName());
application.addComponent(Tags.treeIndent.componentType(), UITreeIndent.class.getName());
application.addComponent(Tags.treeSelect.componentType(), UITreeSelect.class.getName());
+ application.addComponent(UIParameter.COMPONENT_TYPE, UIParameter.class.getName());
application.addBehavior(AjaxBehavior.BEHAVIOR_ID, AjaxBehavior.class.getName());
application.addBehavior(EventBehavior.BEHAVIOR_ID, EventBehavior.class.getName());
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRendererUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRendererUnitTest.java
index c743086f20..8ef5017450 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRendererUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRendererUnitTest.java
@@ -19,6 +19,7 @@
package org.apache.myfaces.tobago.internal.renderkit.renderer;
+import jakarta.faces.component.UIParameter;
import org.apache.myfaces.tobago.component.RendererTypes;
import org.apache.myfaces.tobago.component.Tags;
import org.apache.myfaces.tobago.component.UIOut;
@@ -91,4 +92,26 @@ public class OutRendererUnitTest extends RendererTestBase {
Assertions.assertEquals(loadHtml("renderer/out/outSanitizeNever.html"), formattedResult());
}
+ @Test
+ public void outFormat() throws IOException {
+ final UIOut c = (UIOut) ComponentUtils.createComponent(
+ facesContext, Tags.out.componentType(), RendererTypes.Out, "id");
+ c.setValue("Hello {0} {1}!");
+ c.setMessageFormat(true);
+
+ final UIParameter p0 = (UIParameter) ComponentUtils.createComponent(
+ facesContext, UIParameter.COMPONENT_TYPE, null, "p0");
+ p0.setValue("Mrs");
+ c.getChildren().add(p0);
+
+ final UIParameter p1 = (UIParameter) ComponentUtils.createComponent(
+ facesContext, UIParameter.COMPONENT_TYPE, null, "p1");
+ p1.setValue("Smith");
+ c.getChildren().add(p1);
+
+ c.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/out/outFormat.html"), formattedResult());
+ }
+
}
diff --git a/tobago-core/src/test/resources/renderer/out/outFormat.html b/tobago-core/src/test/resources/renderer/out/outFormat.html
new file mode 100644
index 0000000000..ecf3ca6b2c
--- /dev/null
+++ b/tobago-core/src/test/resources/renderer/out/outFormat.html
@@ -0,0 +1,18 @@
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<tobago-out id='id' class='tobago-auto-spacing'><span class='form-control-plaintext'>Hello Mrs Smith!</span></tobago-out>
diff --git a/tobago-example/tobago-example-demo/src/main/resources/org/apache/myfaces/tobago/example/demo/DemoBundle.properties b/tobago-example/tobago-example-demo/src/main/resources/org/apache/myfaces/tobago/example/demo/DemoBundle.properties
index ae68c5e8ad..38f04e1fa1 100644
--- a/tobago-example/tobago-example-demo/src/main/resources/org/apache/myfaces/tobago/example/demo/DemoBundle.properties
+++ b/tobago-example/tobago-example-demo/src/main/resources/org/apache/myfaces/tobago/example/demo/DemoBundle.properties
@@ -15,3 +15,4 @@
locale_short=default
not_translated=Some content of the demo is not translated!
+message_format_example=Welcome {0} {1}!
diff --git a/tobago-example/tobago-example-demo/src/main/resources/org/apache/myfaces/tobago/example/demo/DemoBundle_de.properties b/tobago-example/tobago-example-demo/src/main/resources/org/apache/myfaces/tobago/example/demo/DemoBundle_de.properties
index 6b72675004..3b3b104445 100644
--- a/tobago-example/tobago-example-demo/src/main/resources/org/apache/myfaces/tobago/example/demo/DemoBundle_de.properties
+++ b/tobago-example/tobago-example-demo/src/main/resources/org/apache/myfaces/tobago/example/demo/DemoBundle_de.properties
@@ -15,3 +15,4 @@
locale_short=de
not_translated=Einige Teile dieser Seite sind nicht in allen Sprachen verf\u00FCgbar.
+message_format_example=Willkommen {0} {1}!
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/020-output/10-out/Out.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/020-output/10-out/Out.xhtml
index 4e5738f507..eef6d94df3 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/020-output/10-out/Out.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/020-output/10-out/Out.xhtml
@@ -20,7 +20,8 @@
<ui:composition template="/main.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:tc="http://myfaces.apache.org/tobago/component"
- xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
+ xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
+ xmlns:f="http://java.sun.com/jsf/core">
<p>The <code class="language-markup"><tc:out/></code> display an outputtext combined with a label.</p>
<tc:link label="Tag Library Documentation" image="#{request.contextPath}/image/feather-leaf.png"
@@ -66,6 +67,17 @@
<tc:out value="combination: deleted danger italic" markup="deleted danger italic"/>
</tc:segmentLayout>
</tc:section>
+ <tc:section label="Message Format">
+ Using message formatting for i18n strings.
+ <demo-highlight language="markup"><tc:out value="\#{demoBundle.message_format_example}" messageFormat="true">
+ <f:param value="Mrs"/>
+ <f:param value="Smith"/>
+</tc:out></demo-highlight>
+ <tc:out value="#{demoBundle.message_format_example}" messageFormat="true">
+ <f:param value="Mrs"/>
+ <f:param value="Smith"/>
+ </tc:out>
+ </tc:section>
<tc:section label="Escape">
<p>If the given string to the output field is HTML/XML code, it will be escaped by default.
But you can turn it off with the <code>escape</code> attribute.</p>