This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch feature/WW-5695-html5-constraint-validation in repository https://gitbox.apache.org/repos/asf/struts.git
commit 2292e922fb1cfbd89df7de4f38b53f63e0199b35 Author: Lukasz Lenart <[email protected]> AuthorDate: Mon Aug 24 23:13:15 2026 +0200 WW-5695 feat(components): resolve the HTML control type per component attributes.type is set by TextField and nothing else on the input path, so the control type cannot come from the attribute map alone. Adds getControlType() with four overrides; Checkbox, Radio, File and Hidden fall through to OTHER, which emits nothing and is correct for all four. Co-Authored-By: Claude Opus 5 <[email protected]> --- .../org/apache/struts2/components/Password.java | 5 ++ .../java/org/apache/struts2/components/Select.java | 5 ++ .../org/apache/struts2/components/TextArea.java | 5 ++ .../org/apache/struts2/components/TextField.java | 6 +++ .../java/org/apache/struts2/components/UIBean.java | 11 ++++ .../apache/struts2/components/ControlTypeTest.java | 62 ++++++++++++++++++++++ 6 files changed, 94 insertions(+) diff --git a/core/src/main/java/org/apache/struts2/components/Password.java b/core/src/main/java/org/apache/struts2/components/Password.java index 471f7dbd2..4bd75c1a1 100644 --- a/core/src/main/java/org/apache/struts2/components/Password.java +++ b/core/src/main/java/org/apache/struts2/components/Password.java @@ -64,6 +64,11 @@ public class Password extends TextField { return TEMPLATE; } + @Override + protected HtmlControlType getControlType() { + return HtmlControlType.PASSWORD; + } + public void evaluateExtraParams() { super.evaluateExtraParams(); diff --git a/core/src/main/java/org/apache/struts2/components/Select.java b/core/src/main/java/org/apache/struts2/components/Select.java index f1a8e5b6d..237775fce 100644 --- a/core/src/main/java/org/apache/struts2/components/Select.java +++ b/core/src/main/java/org/apache/struts2/components/Select.java @@ -97,6 +97,11 @@ public class Select extends ListUIBean { return TEMPLATE; } + @Override + protected HtmlControlType getControlType() { + return HtmlControlType.SELECT; + } + public void evaluateExtraParams() { super.evaluateExtraParams(); diff --git a/core/src/main/java/org/apache/struts2/components/TextArea.java b/core/src/main/java/org/apache/struts2/components/TextArea.java index 7f3babf56..41856b270 100644 --- a/core/src/main/java/org/apache/struts2/components/TextArea.java +++ b/core/src/main/java/org/apache/struts2/components/TextArea.java @@ -62,6 +62,11 @@ public class TextArea extends UIBean { return TEMPLATE; } + @Override + protected HtmlControlType getControlType() { + return HtmlControlType.TEXTAREA; + } + public void evaluateExtraParams() { super.evaluateExtraParams(); diff --git a/core/src/main/java/org/apache/struts2/components/TextField.java b/core/src/main/java/org/apache/struts2/components/TextField.java index 726c4fc5b..019c371ca 100644 --- a/core/src/main/java/org/apache/struts2/components/TextField.java +++ b/core/src/main/java/org/apache/struts2/components/TextField.java @@ -72,6 +72,12 @@ public class TextField extends UIBean { return TEMPLATE; } + @Override + protected HtmlControlType getControlType() { + Object type = getAttributes().get("type"); + return type == null ? HtmlControlType.TEXT : HtmlControlType.from(String.valueOf(type)); + } + protected void evaluateExtraParams() { super.evaluateExtraParams(); diff --git a/core/src/main/java/org/apache/struts2/components/UIBean.java b/core/src/main/java/org/apache/struts2/components/UIBean.java index adac94dba..9d095095f 100644 --- a/core/src/main/java/org/apache/struts2/components/UIBean.java +++ b/core/src/main/java/org/apache/struts2/components/UIBean.java @@ -968,6 +968,17 @@ public abstract class UIBean extends Component { } } + /** + * The kind of HTML control this component renders, used to decide which HTML5 constraint + * attributes are legal on it. Defaults to {@link HtmlControlType#OTHER}, which supports no + * constraints — so a component that does not override this emits none. + * + * @since 7.4.0 + */ + protected HtmlControlType getControlType() { + return HtmlControlType.OTHER; + } + protected void evaluateExtraParams() { } diff --git a/core/src/test/java/org/apache/struts2/components/ControlTypeTest.java b/core/src/test/java/org/apache/struts2/components/ControlTypeTest.java new file mode 100644 index 000000000..d0d0d82ab --- /dev/null +++ b/core/src/test/java/org/apache/struts2/components/ControlTypeTest.java @@ -0,0 +1,62 @@ +/* + * 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. + */ +package org.apache.struts2.components; + +import org.apache.struts2.views.jsp.AbstractUITagTest; + +public class ControlTypeTest extends AbstractUITagTest { + + public void testTextFieldDefaultsToText() { + TextField textField = new TextField(stack, request, response); + assertEquals(HtmlControlType.TEXT, textField.getControlType()); + } + + public void testTextFieldHonoursAnExplicitType() { + TextField textField = new TextField(stack, request, response); + textField.addParameter("type", "number"); + assertEquals(HtmlControlType.NUMBER, textField.getControlType()); + } + + public void testTextFieldFallsBackForAnUnknownType() { + TextField textField = new TextField(stack, request, response); + textField.addParameter("type", "supercolor"); + assertEquals(HtmlControlType.OTHER, textField.getControlType()); + } + + public void testPasswordIsAlwaysPassword() { + Password password = new Password(stack, request, response); + assertEquals(HtmlControlType.PASSWORD, password.getControlType()); + } + + public void testTextAreaIsTextarea() { + TextArea textArea = new TextArea(stack, request, response); + assertEquals(HtmlControlType.TEXTAREA, textArea.getControlType()); + } + + public void testSelectIsSelect() { + Select select = new Select(stack, request, response); + assertEquals(HtmlControlType.SELECT, select.getControlType()); + } + + public void testControlsWithoutAnOverrideAreUnknown() { + assertEquals(HtmlControlType.OTHER, new Checkbox(stack, request, response).getControlType()); + assertEquals(HtmlControlType.OTHER, new Hidden(stack, request, response).getControlType()); + assertEquals(HtmlControlType.OTHER, new File(stack, request, response).getControlType()); + } +}
