You have come to this page because you used an 'redirect-action' prefix.
-
+
Because this is a redirect-action, the text will be lost, due to a redirection
implies a new request being issued from the client.
The text you've enter is ${text!''}
- <@s.a href="javascript:history.back();" cssClass="btn btn-info" escapeHtmlBody="false"> Back@s.a>
+ <@s.a href="javascript:history.back();" cssClass="btn btn-info"> Back@s.a>
diff --git a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/appendIteratorTagDemoResult.jsp b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/appendIteratorTagDemoResult.jsp
index c95de872d..c2c7ff3dd 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/appendIteratorTagDemoResult.jsp
+++ b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/appendIteratorTagDemoResult.jsp
@@ -46,7 +46,7 @@
- Back To Input
+ Back To Input
diff --git a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/iteratorGeneratorTagDemoResult.jsp b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/iteratorGeneratorTagDemoResult.jsp
index fefb0933e..f4a31d897 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/iteratorGeneratorTagDemoResult.jsp
+++ b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/iteratorGeneratorTagDemoResult.jsp
@@ -41,7 +41,7 @@
- Back To Input
+ Back To Input
diff --git a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/mergeIteratorTagDemoResult.jsp b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/mergeIteratorTagDemoResult.jsp
index 3ae67a590..1798da445 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/mergeIteratorTagDemoResult.jsp
+++ b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/mergeIteratorTagDemoResult.jsp
@@ -45,7 +45,7 @@
- Back To Input
+ Back To Input
diff --git a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/subsetIteratorTagDemoResult.jsp b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/subsetIteratorTagDemoResult.jsp
index fc89d5e28..e704e577d 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/subsetIteratorTagDemoResult.jsp
+++ b/apps/showcase/src/main/webapp/WEB-INF/tags/non-ui/iteratorTag/subsetIteratorTagDemoResult.jsp
@@ -42,7 +42,7 @@
- Back To Input
+ Back To Input
diff --git a/apps/showcase/src/main/webapp/WEB-INF/token/example4.ftl b/apps/showcase/src/main/webapp/WEB-INF/token/example4.ftl
index e97fa0820..19dac0114 100644
--- a/apps/showcase/src/main/webapp/WEB-INF/token/example4.ftl
+++ b/apps/showcase/src/main/webapp/WEB-INF/token/example4.ftl
@@ -51,7 +51,7 @@
<@s.form action="transfer4">
<@s.token/>
<@s.textfield label="Amount" name="amount" required=true value="400"/>
- <@s.submit value="Transfer money" cssClass="btn btn-primary" escapeHtmlBody="false"/>
+ <@s.submit value="Transfer money" cssClass="btn btn-primary"/>
@s.form>
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 4ad736216..5abd8547c 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -24,7 +24,7 @@
org.apache.strutsstruts2-parent
- 2.6-SNAPSHOT
+ 6.0.0-SNAPSHOTstruts2-assembly
diff --git a/bom/pom.xml b/bom/pom.xml
index 9d5be00f8..021a767b2 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -29,7 +29,7 @@
struts2-bom
- 2.6-SNAPSHOT
+ 6.0.0-SNAPSHOTpomStruts 2 Bill of Materials
@@ -44,7 +44,7 @@
- 2.6-SNAPSHOT
+ 6.0.0-SNAPSHOTtruetrue
diff --git a/bundles/admin/pom.xml b/bundles/admin/pom.xml
index c6cd0e652..4f5ce6cc2 100644
--- a/bundles/admin/pom.xml
+++ b/bundles/admin/pom.xml
@@ -24,7 +24,7 @@
org.apache.strutsstruts2-osgi-bundles
- 2.6-SNAPSHOT
+ 6.0.0-SNAPSHOTstruts2-osgi-admin-bundle
diff --git a/bundles/demo/pom.xml b/bundles/demo/pom.xml
index a2f639bd7..df1cd3231 100644
--- a/bundles/demo/pom.xml
+++ b/bundles/demo/pom.xml
@@ -24,7 +24,7 @@
org.apache.strutsstruts2-osgi-bundles
- 2.6-SNAPSHOT
+ 6.0.0-SNAPSHOTstruts2-osgi-demo-bundle
diff --git a/bundles/pom.xml b/bundles/pom.xml
index c6d4a9de8..606f2358f 100755
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -24,7 +24,7 @@
org.apache.strutsstruts2-parent
- 2.6-SNAPSHOT
+ 6.0.0-SNAPSHOTstruts2-osgi-bundles
diff --git a/core/pom.xml b/core/pom.xml
index 980714243..4ae6fa027 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -24,7 +24,7 @@
org.apache.strutsstruts2-parent
- 2.6-SNAPSHOT
+ 6.0.0-SNAPSHOTstruts2-corejar
@@ -342,7 +342,7 @@
org.slf4jslf4j-simple
- true
+ test
diff --git a/core/src/main/java/com/opensymphony/xwork2/conversion/metadata/ConversionDescription.java b/core/src/main/java/com/opensymphony/xwork2/conversion/metadata/ConversionDescription.java
deleted file mode 100644
index 74c6d6e6d..000000000
--- a/core/src/main/java/com/opensymphony/xwork2/conversion/metadata/ConversionDescription.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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 com.opensymphony.xwork2.conversion.metadata;
-
-import com.opensymphony.xwork2.conversion.annotations.ConversionRule;
-import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.LogManager;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-/**
- * ConversionDescription
- *
- * @author Rainer Hermanns
- * @version $Id$
- *
- * @deprecated class will be removed
- */
-@Deprecated
-public class ConversionDescription {
-
- /**
- * Jakarta commons-logging reference.
- */
- protected static Logger log = null;
-
-
- public static final String KEY_PREFIX = "Key_";
- public static final String ELEMENT_PREFIX = "Element_";
- public static final String KEY_PROPERTY_PREFIX = "KeyProperty_";
- public static final String DEPRECATED_ELEMENT_PREFIX = "Collection_";
-
- /**
- * Key used for type conversion of maps.
- */
- String MAP_PREFIX = "Map_";
-
- public String property;
- public String typeConverter = "";
- public String rule = "";
- public String value = "";
- public String fullQualifiedClassName;
- public String type = null;
-
- public ConversionDescription() {
- log = LogManager.getLogger(this.getClass());
- }
-
- /**
- * Creates an ConversionDescription with the specified property name.
- *
- * @param property property
- */
- public ConversionDescription(String property) {
- this.property = property;
- log = LogManager.getLogger(this.getClass());
- }
-
- /**
- *
- * Sets the property name to be inserted into the related conversion.properties file.
- * Note: Do not add COLLECTION_PREFIX or MAP_PREFIX keys to property names.
- *
- *
- * @param property The property to be converted.
- */
- public void setProperty(String property) {
- this.property = property;
- }
-
- /**
- * Sets the class name of the type converter to be used.
- *
- * @param typeConverter The class name of the type converter.
- */
- public void setTypeConverter(String typeConverter) {
- this.typeConverter = typeConverter;
- }
-
- /**
- * @param rule the rule prefix for COLLECTION_PREFIX or MAP_PREFIX key. Defaults to en empty String.
- */
- public void setRule(String rule) {
- if (rule != null && rule.length() > 0) {
- if (rule.equals(ConversionRule.COLLECTION.toString())) {
- this.rule = DefaultObjectTypeDeterminer.DEPRECATED_ELEMENT_PREFIX;
- } else if (rule.equals(ConversionRule.ELEMENT.toString())) {
- this.rule = DefaultObjectTypeDeterminer.ELEMENT_PREFIX;
- } else if (rule.equals(ConversionRule.KEY.toString())) {
- this.rule = DefaultObjectTypeDeterminer.KEY_PREFIX;
- } else if (rule.equals(ConversionRule.KEY_PROPERTY.toString())) {
- this.rule = DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX;
- } else if (rule.equals(ConversionRule.MAP.toString())) {
- this.rule = MAP_PREFIX;
- }
- }
- }
-
-
- public void setType(String type) {
- this.type = type;
- }
-
- public String getType() {
- return type;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(String value) {
- this.value = value;
- }
-
- /**
- * Returns the conversion description as property entry.
- *
- *
- * @return the conversion description as property entry.
- */
- public String asProperty() {
- StringWriter sw = new StringWriter();
- PrintWriter writer = null;
- try {
- writer = new PrintWriter(sw);
- writer.print(rule);
- writer.print(property);
- writer.print("=");
- if ( rule.startsWith(DefaultObjectTypeDeterminer.KEY_PROPERTY_PREFIX) && value != null && value.length() > 0 ) {
- writer.print(value);
- } else {
- writer.print(typeConverter);
- }
- } finally {
- if (writer != null) {
- writer.flush();
- writer.close();
- }
- }
-
- return sw.toString();
-
- }
-
- /**
- * Returns the fullQualifiedClassName attribute is used to create the special conversion.properties file name.
- *
- * @return full qualified class name
- */
- public String getFullQualifiedClassName() {
- return fullQualifiedClassName;
- }
-
- /**
- * The fullQualifiedClassName attribute is used to create the special conversion.properties file name.
- *
- * @param fullQualifiedClassName a full qualified class name
- */
- public void setFullQualifiedClassName(String fullQualifiedClassName) {
- this.fullQualifiedClassName = fullQualifiedClassName;
- }
-}
diff --git a/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
index a8f717636..69b717c6c 100644
--- a/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
+++ b/core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java
@@ -40,6 +40,7 @@ import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
+import java.util.regex.Pattern;
/**
* This interceptor sets all parameters on the value stack.
@@ -50,8 +51,11 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
protected static final int PARAM_NAME_MAX_LENGTH = 100;
+ private static final Pattern DMI_IGNORED_PATTERN = Pattern.compile("^(action|method):.*", Pattern.CASE_INSENSITIVE);
+
private int paramNameMaxLength = PARAM_NAME_MAX_LENGTH;
private boolean devMode = false;
+ private boolean dmiEnabled = false;
protected boolean ordered = false;
@@ -79,6 +83,11 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
this.acceptedPatterns = acceptedPatterns;
}
+ @Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
+ protected void setDynamicMethodInvocation(String dmiEnabled) {
+ this.dmiEnabled = Boolean.parseBoolean(dmiEnabled);
+ }
+
/**
* If the param name exceeds the configured maximum length it will not be
* accepted.
@@ -101,13 +110,10 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
/**
* Compares based on number of '.' and '[' characters (fewer is higher)
*/
- static final Comparator rbCollator = new Comparator() {
- public int compare(String s1, String s2) {
- int l1 = countOGNLCharacters(s1);
- int l2 = countOGNLCharacters(s2);
- return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
- }
-
+ static final Comparator rbCollator = (s1, s2) -> {
+ int l1 = countOGNLCharacters(s1);
+ int l2 = countOGNLCharacters(s2);
+ return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
};
@Override
@@ -286,6 +292,10 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
}
protected boolean acceptableName(String name) {
+ if (isIgnoredDMI(name)) {
+ LOG.trace("DMI is enabled, ignoring DMI method: {}", name);
+ return false;
+ }
boolean accepted = isWithinLengthLimit(name) && !isExcluded(name) && isAccepted(name);
if (devMode && accepted) { // notify only when in devMode
LOG.debug("Parameter [{}] was accepted and will be appended to action!", name);
@@ -293,6 +303,14 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
return accepted;
}
+ private boolean isIgnoredDMI(String name) {
+ if (dmiEnabled) {
+ return DMI_IGNORED_PATTERN.matcher(name).matches();
+ } else {
+ return false;
+ }
+ }
+
protected boolean isWithinLengthLimit(String name) {
boolean matchLength = name.length() <= paramNameMaxLength;
if (!matchLength) {
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/AbstractLocalizedTextProvider.java b/core/src/main/java/com/opensymphony/xwork2/util/AbstractLocalizedTextProvider.java
index fabb89c16..5685d5e30 100644
--- a/core/src/main/java/com/opensymphony/xwork2/util/AbstractLocalizedTextProvider.java
+++ b/core/src/main/java/com/opensymphony/xwork2/util/AbstractLocalizedTextProvider.java
@@ -306,13 +306,13 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
/**
* A helper method for {@link ResourceBundle} bundle reload logic.
- *
- * Uses standard {@link ResourceBundle} methods to clear the bundle caches for the
+ *
+ * Uses standard {@link ResourceBundle} methods to clear the bundle caches for the
* {@link ClassLoader} instances that this class is aware of at the time of the call.
- *
- * The clearCache() methods have been available since Java 1.6, so
+ *
+ * The clearCache() methods have been available since Java 1.6, so
* it is anticipated the logic will work on any subsequent JVM versions.
- *
+ *
* @since 2.6
*/
private void clearResourceBundleClassloaderCaches() {
@@ -326,14 +326,14 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
/**
* "Hacky" helper method that attempts to clear the Tomcat ResourceEntry
* {@link Map} using knowledge of the Tomcat source code.
- *
- * It relies on the {@link #TOMCAT_RESOURCE_ENTRIES_FIELD} field name, base class name
+ *
+ * It relies on the {@link #TOMCAT_RESOURCE_ENTRIES_FIELD} field name, base class name
* {@link #TOMCAT_WEBAPP_CLASSLOADER_BASE}. and descendant class names {@link #TOMCAT_WEBAPP_CLASSLOADER},
* {@link #TOMCAT_PARALLEL_WEBAPP_CLASSLOADER}, to keep the values identified in the constants.
* It appears to be valid for Tomcat versions 7-10 so far, but could become invalid at any time in the future
* when the resource handling logic in Tomcat changes.
- *
- * Note: With Java 9+, calling this method may result in "Illegal reflective access" warnings. Be aware
+ *
+ * Note: With Java 9+, calling this method may result in "Illegal reflective access" warnings. Be aware
* its logic may fail in a future version of Java that blocks the reflection calls needed for this method.
*/
private void clearTomcatCache() {
@@ -367,10 +367,10 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
/**
* Helper method that is intended to clear a {@link Map} instance by name.
- *
+ *
* This method relies on reflection to perform its operations, and may be blocked in Java 9 and later,
* depending on the accessibility of the field.
- *
+ *
* @param cl The {@link Class} of the obj parameter.
* @param obj The {@link Object} from which the named field is to be extracted (may be null for a static field).
* @param name The name of the field containing a {@link Map} reference.
@@ -433,9 +433,9 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
* Set the {@link #searchDefaultBundlesFirst} flag state. This flag may be used by descendant TextProvider
* implementations to determine if default bundles should be searched for messages first (before the standard
* flow of the {@link LocalizedTextProvider} implementation the descendant provides).
- *
+ *
* @param searchDefaultBundlesFirst provide {@link String} "true" or "false" to set the flag state accordingly.
- *
+ *
* @since 2.6
*/
@Inject(value = StrutsConstants.STRUTS_I18N_SEARCH_DEFAULTBUNDLES_FIRST, required = false)
@@ -490,7 +490,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
}
return bundle;
}
-
+
/**
* Clears all the internal lists.
*
@@ -566,17 +566,17 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
* against the default resource bundles. The default resource bundles are searched for a value using key first, then
* alternateKey when the first search fails, then utilizing defaultMessage (which may be null) if both
* key lookup operations fail.
- *
+ *
*
* A known use case is when a key indexes a collection (e.g. user.phone[0]) for which some specific keys may exist, but not all,
* along with a general key (e.g. user.phone[*]). In such cases the specific key would be passed in the key parameter and the
* general key would be passed in the alternateKey parameter.
*
- *
+ *
* @param key the initial key to search for a value within the default resource bundles.
* @param alternateKey the alternate (fall-back) key to search for a value within the default resource bundles, if the initial key lookup fails.
* @param locale the {@link Locale} to be used for the default resource bundle lookup.
- * @param valueStack the {@link ValueStack} associated with the operation.
+ * @param valueStack the {@link ValueStack} associated with the operation.
* @param args the argument array for parameterized messages (may be null).
* @param defaultMessage the default message {@link String} to use if both key lookup operations fail.
* @return the {@link GetDefaultMessageReturnArg} result containing the processed message lookup (by key first, then alternateKey if key's lookup fails).
@@ -616,11 +616,7 @@ abstract class AbstractLocalizedTextProvider implements LocalizedTextProvider {
MessageFormat mf = buildMessageFormat(message, locale);
return formatWithNullDetection(mf, args);
} catch (MissingResourceException e) {
- if (devMode) {
- LOG.warn("Missing key [{}] in bundle [{}]!", key, bundleName);
- } else {
- LOG.debug("Missing key [{}] in bundle [{}]!", key, bundleName);
- }
+ LOG.debug("Missing key [{}] in bundle [{}]!", key, bundleName);
return null;
}
}
diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java
index 84b608077..a5cf7e0f6 100644
--- a/core/src/main/java/org/apache/struts2/StrutsConstants.java
+++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java
@@ -18,6 +18,7 @@
*/
package org.apache.struts2;
+import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.dispatcher.mapper.CompositeActionMapper;
/**
@@ -132,6 +133,9 @@ public final class StrutsConstants {
/** A path to static content, by default and from historical point of view it's /static. */
public static final String STRUTS_UI_STATIC_CONTENT_PATH = "struts.ui.staticContentPath";
+ /** A global flag to enable/disable html body escaping in tags, can be overwritten per tag */
+ public static final String STRUTS_UI_ESCAPE_HTML_BODY = "struts.ui.escapeHtmlBody";
+
/** The maximize size of a multipart request (file upload) */
public static final String STRUTS_MULTIPART_MAXSIZE = "struts.multipart.maxSize";
@@ -442,4 +446,7 @@ public final class StrutsConstants {
public static final String STRUTS_CHAINING_COPY_FIELD_ERRORS = "struts.chaining.copyFieldErrors";
public static final String STRUTS_CHAINING_COPY_MESSAGES = "struts.chaining.copyMessages";
public static final String STRUTS_OBJECT_FACTORY_CLASSLOADER = "struts.objectFactory.classloader";
+
+ /** See {@link org.apache.struts2.components.Date#setDateFormatter(DateFormatter)} */
+ public static final String STRUTS_DATE_FORMATTER = "struts.date.formatter";
}
diff --git a/core/src/main/java/org/apache/struts2/components/Anchor.java b/core/src/main/java/org/apache/struts2/components/Anchor.java
index 153fdf8e8..ba9d84ac9 100644
--- a/core/src/main/java/org/apache/struts2/components/Anchor.java
+++ b/core/src/main/java/org/apache/struts2/components/Anchor.java
@@ -20,6 +20,7 @@ package org.apache.struts2.components;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -69,8 +70,7 @@ public class Anchor extends ClosingUIBean {
protected UrlProvider urlProvider;
protected UrlRenderer urlRenderer;
protected boolean processingTagBody = false;
- protected boolean escapeHtmlBody = true;
-
+
//these params are passed by the Param tag
protected Map urlParameters = new LinkedHashMap();
@@ -96,18 +96,6 @@ public class Anchor extends ClosingUIBean {
return true;
}
- /**
- * Override to set if body content should be HTML-escaped.
- *
- * @return true if body should be HTML-escaped, false otherwise.
- *
- * @since 2.6
- */
- @Override
- public boolean escapeHtmlBody() {
- return escapeHtmlBody;
- }
-
@Override
protected void evaluateExtraParams() {
super.evaluateExtraParams();
@@ -124,6 +112,8 @@ public class Anchor extends ClosingUIBean {
addParameter("href", ensureAttributeSafelyNotEscaped(builtHref));
}
}
+
+ addParameter("escapeHtmlBody", escapeHtmlBody);
}
@Inject(StrutsConstants.STRUTS_URL_INCLUDEPARAMS)
@@ -276,7 +266,7 @@ public class Anchor extends ClosingUIBean {
urlProvider.setForceAddSchemeHostAndPort(forceAddSchemeHostAndPort);
}
- @StrutsTagAttribute(required = false, description = "Specifies whether to HTML-escape the tag body or not", type = "Boolean", defaultValue = "true")
+ @StrutsTagAttribute(description = "Specifies whether to HTML-escape the tag body or not", type = "Boolean", defaultValue = "false")
public void setEscapeHtmlBody(boolean escapeHtmlBody) {
this.escapeHtmlBody = escapeHtmlBody;
}
diff --git a/core/src/main/java/org/apache/struts2/components/Checkbox.java b/core/src/main/java/org/apache/struts2/components/Checkbox.java
index 591f425ac..dd853caba 100644
--- a/core/src/main/java/org/apache/struts2/components/Checkbox.java
+++ b/core/src/main/java/org/apache/struts2/components/Checkbox.java
@@ -75,7 +75,8 @@ public class Checkbox extends UIBean {
}
if (submitUnchecked != null) {
- addParameter("submitUnchecked", findValue(submitUnchecked, Boolean.class));
+ Object parsedValue = findValue(submitUnchecked, Boolean.class);
+ addParameter("submitUnchecked", parsedValue == null ? Boolean.valueOf(submitUnchecked) : parsedValue);
} else {
addParameter("submitUnchecked", false);
}
diff --git a/core/src/main/java/org/apache/struts2/components/CheckboxList.java b/core/src/main/java/org/apache/struts2/components/CheckboxList.java
index e83091888..601ce5bdf 100644
--- a/core/src/main/java/org/apache/struts2/components/CheckboxList.java
+++ b/core/src/main/java/org/apache/struts2/components/CheckboxList.java
@@ -48,7 +48,7 @@ import com.opensymphony.xwork2.util.ValueStack;
allowDynamicAttributes = true)
public class CheckboxList extends ListUIBean {
final public static String TEMPLATE = "checkboxlist";
-
+
public CheckboxList(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@@ -56,9 +56,19 @@ public class CheckboxList extends ListUIBean {
protected String getDefaultTemplate() {
return TEMPLATE;
}
-
+
public void evaluateExtraParams() {
super.evaluateExtraParams();
}
-}
\ No newline at end of file
+ /**
+ * Checkboxlist tag requires lazy evaluation as list of tags is dynamically generated using
+ *
+ * @return boolean true by default
+ */
+ @Override
+ protected boolean lazyEvaluation() {
+ return true;
+ }
+
+}
diff --git a/core/src/main/java/org/apache/struts2/components/Component.java b/core/src/main/java/org/apache/struts2/components/Component.java
index 4d5c84560..f45dac07d 100644
--- a/core/src/main/java/org/apache/struts2/components/Component.java
+++ b/core/src/main/java/org/apache/struts2/components/Component.java
@@ -68,6 +68,7 @@ public class Component {
protected static ConcurrentMap, Collection> standardAttributesMap = new ConcurrentHashMap<>();
protected boolean devMode = false;
+ protected boolean escapeHtmlBody = false;
protected ValueStack stack;
protected Map parameters;
protected ActionMapper actionMapper;
@@ -116,6 +117,11 @@ public class Component {
this.throwExceptionOnELFailure = BooleanUtils.toBoolean(throwException);
}
+ @Inject(value = StrutsConstants.STRUTS_UI_ESCAPE_HTML_BODY, required = false)
+ public void setEscapeHtmlBody(String escapeHtmlBody) {
+ this.escapeHtmlBody = BooleanUtils.toBoolean(escapeHtmlBody);
+ }
+
@Inject
public void setUrlHelper(UrlHelper urlHelper) {
this.urlHelper = urlHelper;
@@ -213,13 +219,13 @@ public class Component {
* @return the component if found, null if not.
*/
protected Component findAncestor(Class> clazz) {
- Stack componentStack = getComponentStack();
+ Stack componentStack = getComponentStack();
int currPosition = componentStack.search(this);
if (currPosition >= 0) {
int start = componentStack.size() - currPosition - 1;
for (int i = start; i >= 0; i--) {
- Component component = (Component) componentStack.get(i);
+ Component component = componentStack.get(i);
if (clazz.isAssignableFrom(component.getClass()) && component != this) {
return component;
}
@@ -378,9 +384,8 @@ public class Component {
return expression;
}
} else {
- expression = stripExpression(expression);
-
- return getStack().findValue(expression, toType, throwExceptionOnELFailure);
+ String strippedExpression = stripExpression(expression);
+ return getStack().findValue(strippedExpression, toType, throwExceptionOnELFailure);
}
}
@@ -536,7 +541,7 @@ public class Component {
* @since 2.6
*/
public boolean escapeHtmlBody() {
- return true;
+ return escapeHtmlBody;
}
/**
@@ -572,16 +577,16 @@ public class Component {
/**
* Request that the tag state be cleared during {@link org.apache.struts2.views.jsp.StrutsBodyTagSupport#doEndTag()} processing,
* which may help with certain edge cases with tag logic running on servers that implement JSP Tag Pooling.
- *
- * Note: All Tag classes that extend {@link org.apache.struts2.views.jsp.StrutsBodyTagSupport} must implement a setter for
+ *
+ * Note: All Tag classes that extend {@link org.apache.struts2.views.jsp.StrutsBodyTagSupport} must implement a setter for
* this attribute (same name), and it must be defined at the Tag class level.
* Defining a setter in the superclass alone is insufficient (results in "Cannot find a setter method for the attribute").
- *
+ *
* See {@link org.apache.struts2.views.jsp.StrutsBodyTagSupport#clearTagStateForTagPoolingServers() for additional details.
- *
+ *
* @param performClearTagStateForTagPoolingServers true if tag state should be cleared, false otherwise.
*/
- @StrutsTagAttribute(description="Whether to clear all tag state during doEndTag() processing (if applicable)", type="Boolean", defaultValue="false", required = false)
+ @StrutsTagAttribute(description="Whether to clear all tag state during doEndTag() processing (if applicable)", type="Boolean", defaultValue="false")
public void setPerformClearTagStateForTagPoolingServers(boolean performClearTagStateForTagPoolingServers) {
this.performClearTagStateForTagPoolingServers = performClearTagStateForTagPoolingServers;
}
diff --git a/core/src/main/java/org/apache/struts2/components/Date.java b/core/src/main/java/org/apache/struts2/components/Date.java
index 5f65d9c7b..ad174fd11 100644
--- a/core/src/main/java/org/apache/struts2/components/Date.java
+++ b/core/src/main/java/org/apache/struts2/components/Date.java
@@ -18,29 +18,29 @@
*/
package org.apache.struts2.components;
-import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.TextProvider;
+import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import java.io.IOException;
import java.io.Writer;
import java.time.Instant;
+import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
*
- *
+ *
* Format Date object in different ways.
*
* The date tag will allow you to format a Date in a quick and easy way.
@@ -59,6 +59,12 @@ import java.util.List;
*
*
*
+ * Note: Since Struts 2.6 a new Java 8 API has been used to format the Date, it's based on
+ * DateTimeFormatter
+ * which uses a bit different patterns.
+ *
+ *
+ *
* Configurable attributes are:
*
*
@@ -130,8 +136,8 @@ import java.util.List;
*
if one is not found DateFormat.MEDIUM format will be used
*
*
- *
- *
+ *
+ *
*
*
*
Examples
@@ -145,12 +151,12 @@ import java.util.List;
*
*
* Date
- *
*/
-@StrutsTag(name="date", tldBodyContent="empty", tldTagClass="org.apache.struts2.views.jsp.DateTag", description="Render a formatted date.")
+@StrutsTag(name = "date", tldBodyContent = "empty", tldTagClass = "org.apache.struts2.views.jsp.DateTag", description = "Render a formatted date.")
public class Date extends ContextBean {
private static final Logger LOG = LogManager.getLogger(Date.class);
+
/**
* Property name to fall back when no format is specified
*/
@@ -202,17 +208,18 @@ public class Date extends ContextBean {
private String timezone;
+ private DateFormatter dateFormatter;
+
public Date(ValueStack stack) {
super(stack);
}
- private TextProvider findProviderInStack() {
- for (Object o : getStack().getRoot()) {
- if (o instanceof TextProvider) {
- return (TextProvider) o;
- }
- }
- return null;
+ /**
+ * An instance of {@link DateFormatter}
+ */
+ @Inject
+ public void setDateFormatter(DateFormatter dateFormatter) {
+ this.dateFormatter = dateFormatter;
}
/**
@@ -280,6 +287,8 @@ public class Date extends ContextBean {
@Override
public boolean end(Writer writer, String body) {
+ TextProvider textProvider = findProviderInStack();
+
ZonedDateTime date = null;
final ZoneId tz = getTimeZone();
// find the name on the valueStack
@@ -292,26 +301,27 @@ public class Date extends ContextBean {
date = Instant.ofEpochMilli((long) dateObject).atZone(tz);
} else if (dateObject instanceof LocalDateTime) {
date = ((LocalDateTime) dateObject).atZone(tz);
+ } else if (dateObject instanceof LocalDate) {
+ date = ((LocalDate) dateObject).atStartOfDay(tz);
} else if (dateObject instanceof Instant) {
date = ((Instant) dateObject).atZone(tz);
} else {
if (devMode) {
- TextProvider tp = findProviderInStack();
String developerNotification = "";
- if (tp != null) {
- developerNotification = findProviderInStack().getText(
- "devmode.notification",
- "Developer Notification:\n{0}",
- new String[]{
- "Expression [" + name + "] passed to tag which was evaluated to [" + dateObject + "]("
- + (dateObject != null ? dateObject.getClass() : "null") + ") isn't supported!"
- }
+ if (textProvider != null) {
+ developerNotification = textProvider.getText(
+ "devmode.notification",
+ "Developer Notification:\n{0}",
+ new String[]{
+ "Expression [" + name + "] passed to tag which was evaluated to [" + dateObject + "]("
+ + (dateObject != null ? dateObject.getClass() : "null") + ") isn't supported!"
+ }
);
}
LOG.warn(developerNotification);
} else {
LOG.debug("Expression [{}] passed to tag which was evaluated to [{}]({}) isn't supported!",
- name, dateObject, (dateObject != null ? dateObject.getClass() : "null"));
+ name, dateObject, (dateObject != null ? dateObject.getClass() : "null"));
}
}
@@ -321,33 +331,11 @@ public class Date extends ContextBean {
}
String msg;
if (date != null) {
- TextProvider tp = findProviderInStack();
- if (tp != null) {
+ if (textProvider != null) {
if (nice) {
- msg = formatTime(tp, date);
+ msg = formatTime(textProvider, date);
} else {
- DateTimeFormatter dtf;
- if (format == null) {
- String globalFormat = null;
-
- // if the format is not specified, fall back using the
- // defined property DATETAG_PROPERTY
- globalFormat = tp.getText(DATETAG_PROPERTY);
-
- // if tp.getText can not find the property then the
- // returned string is the same as input =
- // DATETAG_PROPERTY
- if (globalFormat != null
- && !DATETAG_PROPERTY.equals(globalFormat)) {
- dtf = DateTimeFormatter.ofPattern(globalFormat, ActionContext.getContext().getLocale());
- } else {
- dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
- .withLocale(ActionContext.getContext().getLocale());
- }
- } else {
- dtf = DateTimeFormatter.ofPattern(format, ActionContext.getContext().getLocale());
- }
- msg = dtf.format(date);
+ msg = formatDate(textProvider, date);
}
if (msg != null) {
try {
@@ -365,6 +353,20 @@ public class Date extends ContextBean {
return super.end(writer, "");
}
+ private String formatDate(TextProvider textProvider, ZonedDateTime date) {
+ String useFormat = format;
+ if (useFormat == null) {
+ // if the format is not specified, fall back using the defined property DATETAG_PROPERTY
+ useFormat = textProvider.getText(DATETAG_PROPERTY);
+ if (DATETAG_PROPERTY.equals(useFormat)) {
+ // if tp.getText can not find the property then the
+ // returned string is the same as input = DATETAG_PROPERTY
+ useFormat = null;
+ }
+ }
+ return dateFormatter.format(date, useFormat);
+ }
+
private ZoneId getTimeZone() {
ZoneId tz = ZoneId.systemDefault();
if (timezone != null) {
@@ -378,17 +380,26 @@ public class Date extends ContextBean {
return tz;
}
- @StrutsTagAttribute(description="Date or DateTime format pattern", rtexprvalue=false)
+ private TextProvider findProviderInStack() {
+ for (Object o : getStack().getRoot()) {
+ if (o instanceof TextProvider) {
+ return (TextProvider) o;
+ }
+ }
+ return null;
+ }
+
+ @StrutsTagAttribute(description = "Date or DateTime format pattern")
public void setFormat(String format) {
this.format = format;
}
- @StrutsTagAttribute(description="Whether to print out the date nicely", type="Boolean", defaultValue="false")
+ @StrutsTagAttribute(description = "Whether to print out the date nicely", type = "Boolean", defaultValue = "false")
public void setNice(boolean nice) {
this.nice = nice;
}
- @StrutsTagAttribute(description = "The specific timezone in which to format the date", required = false)
+ @StrutsTagAttribute(description = "The specific timezone in which to format the date")
public void setTimezone(String timezone) {
this.timezone = timezone;
}
@@ -400,7 +411,7 @@ public class Date extends ContextBean {
return name;
}
- @StrutsTagAttribute(description="The date value to format", required=true)
+ @StrutsTagAttribute(description = "The date value to format", required = true)
public void setName(String name) {
this.name = name;
}
diff --git a/core/src/main/java/org/apache/struts2/components/ListUIBean.java b/core/src/main/java/org/apache/struts2/components/ListUIBean.java
index 26484f250..bfaffe6f1 100644
--- a/core/src/main/java/org/apache/struts2/components/ListUIBean.java
+++ b/core/src/main/java/org/apache/struts2/components/ListUIBean.java
@@ -195,7 +195,6 @@ public abstract class ListUIBean extends UIBean {
this.listTitle = listTitle;
}
-
public void setThrowExceptionOnNullValueAttribute(boolean throwExceptionOnNullValueAttribute) {
this.throwExceptionOnNullValueAttribute = throwExceptionOnNullValueAttribute;
}
diff --git a/core/src/main/java/org/apache/struts2/components/Radio.java b/core/src/main/java/org/apache/struts2/components/Radio.java
index 0315cb65b..ba5eb471f 100644
--- a/core/src/main/java/org/apache/struts2/components/Radio.java
+++ b/core/src/main/java/org/apache/struts2/components/Radio.java
@@ -57,7 +57,7 @@ import javax.servlet.http.HttpServletResponse;
allowDynamicAttributes = true)
public class Radio extends ListUIBean {
final public static String TEMPLATE = "radiomap";
-
+
public Radio(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@@ -65,8 +65,19 @@ public class Radio extends ListUIBean {
protected String getDefaultTemplate() {
return TEMPLATE;
}
-
+
public void evaluateExtraParams() {
super.evaluateExtraParams();
}
-}
\ No newline at end of file
+
+ /**
+ * Radio tag requires lazy evaluation as list of tags is dynamically generated using
+ *
+ * @return boolean true by default
+ */
+ @Override
+ protected boolean lazyEvaluation() {
+ return true;
+ }
+
+}
diff --git a/core/src/main/java/org/apache/struts2/components/Submit.java b/core/src/main/java/org/apache/struts2/components/Submit.java
index 124526a8e..7d13d3b85 100644
--- a/core/src/main/java/org/apache/struts2/components/Submit.java
+++ b/core/src/main/java/org/apache/struts2/components/Submit.java
@@ -23,6 +23,9 @@ import java.io.Writer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import com.opensymphony.xwork2.inject.Inject;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.struts2.StrutsConstants;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
@@ -54,7 +57,6 @@ public class Submit extends FormButton {
final public static String OPEN_TEMPLATE = "submit";
final public static String TEMPLATE = "submit-close";
protected String src;
- protected boolean escapeHtmlBody = true;
public Submit(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
@@ -73,7 +75,7 @@ public class Submit extends FormButton {
value = "Submit";
}
- if (((key != null)) && (value == null)) {
+ if ((key != null) && (value == null)) {
this.value = "%{getText('"+key +"')}";
}
@@ -83,8 +85,11 @@ public class Submit extends FormButton {
public void evaluateExtraParams() {
super.evaluateExtraParams();
- if (src != null)
+ if (src != null) {
addParameter("src", findString(src));
+ }
+
+ addParameter("escapeHtmlBody", escapeHtmlBody);
}
/**
@@ -101,7 +106,7 @@ public class Submit extends FormButton {
this.src = src;
}
- @StrutsTagAttribute(required = false, description = "Specifies whether to HTML-escape the tag body or not", type = "Boolean", defaultValue = "true")
+ @StrutsTagAttribute(description = "Specifies whether to HTML-escape the tag body or not", type = "Boolean", defaultValue = "false")
public void setEscapeHtmlBody(boolean escapeHtmlBody) {
this.escapeHtmlBody = escapeHtmlBody;
}
@@ -111,18 +116,6 @@ public class Submit extends FormButton {
return true;
}
- /**
- * Override to set if body content should be HTML-escaped.
- *
- * @return true if body should be HTML-escaped, false otherwise.
- *
- * @since 2.6
- */
- @Override
- public boolean escapeHtmlBody() {
- return escapeHtmlBody;
- }
-
/**
* Overrides to be able to render body in a template rather than always before the template
*/
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 24b47fa14..4a4b5eb2b 100644
--- a/core/src/main/java/org/apache/struts2/components/UIBean.java
+++ b/core/src/main/java/org/apache/struts2/components/UIBean.java
@@ -20,7 +20,9 @@ package org.apache.struts2.components;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -31,6 +33,7 @@ import org.apache.struts2.components.template.TemplateEngine;
import org.apache.struts2.components.template.TemplateEngineManager;
import org.apache.struts2.components.template.TemplateRenderingContext;
import org.apache.struts2.dispatcher.StaticContentLoader;
+import org.apache.struts2.util.ComponentUtils;
import org.apache.struts2.util.TextProviderHelper;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import org.apache.struts2.views.util.ContextUtil;
@@ -699,11 +702,13 @@ public abstract class UIBean extends Component {
}
if (requiredLabel != null) {
- addParameter("required", findValue(requiredLabel, Boolean.class));
+ Object parsedValue = findValue(requiredLabel, Boolean.class);
+ addParameter("required", parsedValue == null ? Boolean.valueOf(requiredLabel) : parsedValue);
}
if (disabled != null) {
- addParameter("disabled", findValue(disabled, Boolean.class));
+ Object parsedValue = findValue(disabled, Boolean.class);
+ addParameter("disabled", parsedValue == null ? Boolean.valueOf(disabled) : parsedValue);
}
if (tabindex != null) {
@@ -883,9 +888,9 @@ public abstract class UIBean extends Component {
this.addParameter("tooltipDelay", findString(this.tooltipDelay));
if (this.javascriptTooltip != null) {
- Boolean jsTooltips = (Boolean) findValue(this.javascriptTooltip, Boolean.class);
+ Object jsTooltips = findValue(this.javascriptTooltip, Boolean.class);
//TODO use a Boolean model when tooltipConfig is dropped
- this.addParameter("jsTooltipEnabled", jsTooltips.toString());
+ this.addParameter("jsTooltipEnabled", jsTooltips == null ? this.javascriptTooltip : jsTooltips.toString());
if (form != null)
form.addParameter("hasTooltip", jsTooltips);
@@ -965,7 +970,7 @@ public abstract class UIBean extends Component {
// 1] UI component's tooltipConfig attribute OR
// 2] param tag value attribute
- result = new LinkedHashMap<>((Map) tooltipConfigObj);
+ result = new LinkedHashMap((Map) tooltipConfigObj);
} else if (tooltipConfigObj instanceof String) {
// we get this if its configured using
@@ -1272,10 +1277,16 @@ public abstract class UIBean extends Component {
public void setDynamicAttributes(Map tagDynamicAttributes) {
for (Map.Entry entry : tagDynamicAttributes.entrySet()) {
- String entryKey = entry.getKey();
+ String attrName = entry.getKey();
+ String attrValue = entry.getValue();
- if (!isValidTagAttribute(entryKey)) {
- dynamicAttributes.put(entryKey, entry.getValue());
+ if (!isValidTagAttribute(attrName)) {
+ if (ComponentUtils.containsExpression(attrValue) && !lazyEvaluation()) {
+ String translated = TextParseUtil.translateVariables('%', attrValue, stack);
+ dynamicAttributes.put(attrName, ObjectUtils.defaultIfNull(translated, attrValue));
+ } else {
+ dynamicAttributes.put(attrName, attrValue);
+ }
}
}
}
@@ -1296,4 +1307,14 @@ public abstract class UIBean extends Component {
}
}
+ /**
+ * Used to avoid evaluating attributes in {@link #evaluateParams()} or {@link #evaluateExtraParams()}
+ * as evaluation will happen in tag's template
+ *
+ * @return boolean false if evaluation should be performed in ftl
+ */
+ protected boolean lazyEvaluation() {
+ return false;
+ }
+
}
diff --git a/core/src/main/java/org/apache/struts2/components/date/DateFormatter.java b/core/src/main/java/org/apache/struts2/components/date/DateFormatter.java
new file mode 100644
index 000000000..282daaa06
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/components/date/DateFormatter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.date;
+
+import java.time.temporal.TemporalAccessor;
+
+/**
+ * Allows defines a wrapper around different formatting APIs, like old SimpleDateFormat
+ * and new DateTimeFormatter introduced in Java 8 Date/Time API
+ *
+ * New instance will be injected using {@link org.apache.struts2.StrutsConstants#STRUTS_DATE_FORMATTER}
+ */
+public interface DateFormatter {
+
+ /**
+ * Formats provided temporal with the given format
+ *
+ * @param temporal Java 8 {@link TemporalAccessor}
+ * @param format implementation specific format
+ * @return a string representation of the formatted `temporal`
+ */
+ String format(TemporalAccessor temporal, String format);
+
+}
diff --git a/core/src/main/java/org/apache/struts2/components/date/DateTimeFormatterAdapter.java b/core/src/main/java/org/apache/struts2/components/date/DateTimeFormatterAdapter.java
new file mode 100644
index 000000000..05767ab1b
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/components/date/DateTimeFormatterAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.date;
+
+import com.opensymphony.xwork2.ActionContext;
+
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.time.temporal.TemporalAccessor;
+import java.util.Locale;
+
+public class DateTimeFormatterAdapter implements DateFormatter {
+
+ @Override
+ public String format(TemporalAccessor temporal, String format) {
+ DateTimeFormatter dtf;
+ Locale locale = ActionContext.getContext().getLocale();
+ if (format == null) {
+ dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
+ .withLocale(locale);
+ } else {
+ dtf = DateTimeFormatter.ofPattern(format, locale);
+ }
+ return dtf.format(temporal);
+ }
+
+}
diff --git a/core/src/main/java/org/apache/struts2/components/date/SimpleDateFormatAdapter.java b/core/src/main/java/org/apache/struts2/components/date/SimpleDateFormatAdapter.java
new file mode 100644
index 000000000..38f3c5296
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/components/date/SimpleDateFormatAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.date;
+
+import com.opensymphony.xwork2.ActionContext;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.temporal.TemporalAccessor;
+import java.util.Date;
+import java.util.Locale;
+
+public class SimpleDateFormatAdapter implements DateFormatter {
+
+ @Override
+ public String format(TemporalAccessor temporal, String format) {
+ DateFormat df;
+ Locale locale = ActionContext.getContext().getLocale();
+ if (format == null) {
+ df = SimpleDateFormat.getDateInstance(DateFormat.MEDIUM, locale);
+ } else {
+ df = new SimpleDateFormat(format, locale);
+ }
+ return df.format(new Date(Instant.from(temporal).toEpochMilli()));
+ }
+
+}
diff --git a/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java b/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
index 3bbd2196b..91e155867 100644
--- a/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
+++ b/core/src/main/java/org/apache/struts2/components/template/FreemarkerTemplateEngine.java
@@ -148,16 +148,13 @@ public class FreemarkerTemplateEngine extends BaseTemplateEngine {
}
};
- LOG.debug("Puts action on the top of ValueStack, just before the tag");
- action = stack.pop();
+ LOG.debug("Push tag on top of the stack");
stack.push(templateContext.getTag());
- stack.push(action);
try {
template.process(model, writer);
} finally {
- stack.pop(); // removes action
- stack.pop(); // removes tag
- stack.push(action); // puts back action
+ LOG.debug("Removes tag from top of the stack");
+ stack.pop();
}
}
diff --git a/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
index ade6aa0b0..69aa9258e 100644
--- a/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
+++ b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
@@ -54,13 +54,12 @@ import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.location.LocatableProperties;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.LogManager;
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.components.UrlRenderer;
+import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.dispatcher.DispatcherErrorHandler;
import org.apache.struts2.dispatcher.StaticContentLoader;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
@@ -422,6 +421,8 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
alias(NotExcludedAcceptedPatternsChecker.class, StrutsConstants.STRUTS_NOT_EXCLUDED_ACCEPTED_PATTERNS_CHECKER
, builder, props, Scope.SINGLETON);
+ alias(DateFormatter.class, StrutsConstants.STRUTS_DATE_FORMATTER, builder, props, Scope.SINGLETON);
+
switchDevMode(props);
}
diff --git a/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java
index 0ef0f3786..634240085 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/ServletConfigInterceptor.java
@@ -45,25 +45,23 @@ import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
*
*
*
@@ -101,13 +99,19 @@ import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
*
*
* @see ServletContextAware
+ * @see org.apache.struts2.action.ServletContextAware
* @see ServletRequestAware
+ * @see org.apache.struts2.action.ServletRequestAware
* @see ServletResponseAware
+ * @see org.apache.struts2.action.ServletResponseAware
* @see ParameterAware
- * @see ParametersAware
+ * @see org.apache.struts2.action.ParametersAware
* @see SessionAware
+ * @see org.apache.struts2.action.SessionAware
* @see ApplicationAware
+ * @see org.apache.struts2.action.ApplicationAware
* @see PrincipalAware
+ * @see org.apache.struts2.action.PrincipalAware
*/
public class ServletConfigInterceptor extends AbstractInterceptor implements StrutsStatics {
@@ -159,7 +163,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
if (action instanceof ApplicationAware) {
((ApplicationAware) action).setApplication(context.getApplication());
}
-
+
if (action instanceof org.apache.struts2.action.ApplicationAware) {
((org.apache.struts2.action.ApplicationAware) action).withApplication(context.getApplication());
}
@@ -167,7 +171,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
if (action instanceof SessionAware) {
((SessionAware) action).setSession(context.getSession());
}
-
+
if (action instanceof org.apache.struts2.action.SessionAware) {
((org.apache.struts2.action.SessionAware) action).withSession(context.getSession());
}
@@ -179,7 +183,7 @@ public class ServletConfigInterceptor extends AbstractInterceptor implements Str
if (action instanceof PrincipalAware) {
HttpServletRequest request = context.getServletRequest();
if(request != null) {
- // We are in servtlet environment, so principal information resides in HttpServletRequest
+ // We are in servlet environment, so principal information resides in HttpServletRequest
((PrincipalAware) action).setPrincipalProxy(new ServletPrincipalProxy(request));
}
}
diff --git a/core/src/main/java/org/apache/struts2/util/TextProviderHelper.java b/core/src/main/java/org/apache/struts2/util/TextProviderHelper.java
index 1fa411080..d7ad70681 100644
--- a/core/src/main/java/org/apache/struts2/util/TextProviderHelper.java
+++ b/core/src/main/java/org/apache/struts2/util/TextProviderHelper.java
@@ -71,11 +71,6 @@ public class TextProviderHelper {
} else {
LOG.warn("Could not locate the message resource '{}' as there is no TextProvider in the ValueStack.", key);
}
- if (defaultMessage.equals(msg)) {
- LOG.warn("The default value expression '{}' was evaluated and did not match a property. The literal value '{}' will be used.", defaultMessage, defaultMessage);
- } else {
- LOG.warn("The default value expression '{}' evaluated to '{}'", defaultMessage, msg);
- }
}
}
return msg;
diff --git a/core/src/main/java/org/apache/struts2/views/jsp/ui/AnchorTag.java b/core/src/main/java/org/apache/struts2/views/jsp/ui/AnchorTag.java
index 4f334ada0..0e181d5c6 100644
--- a/core/src/main/java/org/apache/struts2/views/jsp/ui/AnchorTag.java
+++ b/core/src/main/java/org/apache/struts2/views/jsp/ui/AnchorTag.java
@@ -47,13 +47,13 @@ public class AnchorTag extends AbstractClosingTag {
protected String portletUrlType;
protected String anchor;
protected String forceAddSchemeHostAndPort;
- protected boolean escapeHtmlBody = true; // Default - escape HTML body
+ protected String escapeHtmlBody;
@Override
public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
return new Anchor(stack, req, res);
}
-
+
@Override
protected void populateParams() {
super.populateParams();
@@ -80,10 +80,12 @@ public class AnchorTag extends AbstractClosingTag {
if (escapeAmp != null) {
tag.setEscapeAmp(BooleanUtils.toBoolean(escapeAmp));
}
- if (forceAddSchemeHostAndPort != null) {
+ if (forceAddSchemeHostAndPort != null) {
tag.setForceAddSchemeHostAndPort(BooleanUtils.toBoolean(forceAddSchemeHostAndPort));
}
- tag.setEscapeHtmlBody(escapeHtmlBody);
+ if (escapeHtmlBody != null) {
+ tag.setEscapeHtmlBody(escapeHtmlBody);
+ }
}
public void setHref(String href) {
@@ -149,12 +151,12 @@ public class AnchorTag extends AbstractClosingTag {
/**
* Set via parameter to control if body content should be HTML-escaped.
- *
- * @param escapeHtmlBody
- *
+ *
+ * @param escapeHtmlBody
+ *
* @since 2.6
*/
- public void setEscapeHtmlBody(boolean escapeHtmlBody) {
+ public void setEscapeHtmlBody(String escapeHtmlBody) {
this.escapeHtmlBody = escapeHtmlBody;
}
diff --git a/core/src/main/java/org/apache/struts2/views/jsp/ui/SubmitTag.java b/core/src/main/java/org/apache/struts2/views/jsp/ui/SubmitTag.java
index 5250cbe62..2bfd6a91e 100644
--- a/core/src/main/java/org/apache/struts2/views/jsp/ui/SubmitTag.java
+++ b/core/src/main/java/org/apache/struts2/views/jsp/ui/SubmitTag.java
@@ -37,7 +37,7 @@ public class SubmitTag extends AbstractClosingTag {
protected String method;
protected String type;
protected String src;
- protected boolean escapeHtmlBody = true; // Default - escape HTML body
+ protected boolean escapeHtmlBody = false;
@Override
public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
@@ -78,9 +78,9 @@ public class SubmitTag extends AbstractClosingTag {
/**
* Set via parameter to control if body content should be HTML-escaped.
- *
- * @param escapeHtmlBody
- *
+ *
+ * @param escapeHtmlBody
+ *
* @since 2.6
*/
public void setEscapeHtmlBody(boolean escapeHtmlBody) {
diff --git a/core/src/main/resources/org/apache/struts2/default.properties b/core/src/main/resources/org/apache/struts2/default.properties
index eef441ebc..74a85f137 100644
--- a/core/src/main/resources/org/apache/struts2/default.properties
+++ b/core/src/main/resources/org/apache/struts2/default.properties
@@ -151,6 +151,11 @@ struts.ui.theme.expansion.token=~~~
### Sets the default template type. Either ftl, vm, or jsp
struts.ui.templateSuffix=ftl
+### Sets a global flag which will escape html body of Anchor, Submit and Component tag
+### You can control this flag per tag, e.g.: ...
+### and this take precedence over the global flag
+# struts.ui.escapeHtmlBody=true
+
### Configuration reloading
### This will cause the configuration to reload struts.xml when it is changed
# struts.configuration.xml.reload=false
@@ -256,7 +261,7 @@ struts.handle.exception=true
### Applies maximum length allowed on OGNL expressions for security enhancement (optional)
###
-### **WARNING**: If developers enable this option (by configuration) they should make sure that they understand the implications of setting
+### **WARNING**: If developers enable this option (by configuration) they should make sure that they understand the implications of setting
### struts.ognl.expressionMaxLength. They must choose a value large enough to permit ALL valid OGNL expressions used within the application.
### Values larger than the 200-400 range have diminishing security value (at which point it is really only a "style guard" for long OGNL
### expressions in an application. Setting a value of null or "" will also disable the feature.
@@ -264,4 +269,10 @@ struts.handle.exception=true
### NOTE: The sample line below is *INTENTIONALLY* commented out, as this feature is disabled by default.
# struts.ognl.expressionMaxLength=256
+### Defines which named instance of DateFormatter to use, there are two instances:
+### - simpleDateFormatter (based on SimpleDateFormat)
+### - dateTimeFormatter (based on Java 8 Date/Time API)
+### These formatters are using a slightly different patterns, please check JavaDocs of both and more details is in WW-5016
+struts.date.formatter=dateTimeFormatter
+
### END SNIPPET: complete_file
diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml
index 65b89f169..775b3fbbf 100644
--- a/core/src/main/resources/struts-default.xml
+++ b/core/src/main/resources/struts-default.xml
@@ -216,7 +216,7 @@
-
+
@@ -228,6 +228,9 @@
+
+
+
diff --git a/core/src/main/resources/template/simple/checkboxlist.ftl b/core/src/main/resources/template/simple/checkboxlist.ftl
index 3fa27f189..87bef6651 100644
--- a/core/src/main/resources/template/simple/checkboxlist.ftl
+++ b/core/src/main/resources/template/simple/checkboxlist.ftl
@@ -30,7 +30,7 @@
<#assign itemKeyStr = stack.findString('top')>
#if>
<#if parameters.listLabelKey??>
- <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
+ <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
file for it's localized value. This is then used as a label -->
<#assign itemValue = struts.getText(stack.findString(parameters.listLabelKey))/>
<#elseif parameters.listValue??>
@@ -95,9 +95,10 @@
<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
+ <#global evaluate_dynamic_attributes = true/>
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
/>
-
+
<#if parameters.id?has_content>
for="${parameters.id}-${itemCount}"<#rt/>
<#else>
@@ -106,11 +107,10 @@
class="checkboxLabel">${itemValue}
@s.iterator>
<#else>
-
#if>
<#if parameters.disabled!false>
disabled="disabled"<#rt/>
#if>
- />
\ No newline at end of file
+ />
diff --git a/core/src/main/resources/template/simple/dynamic-attributes.ftl b/core/src/main/resources/template/simple/dynamic-attributes.ftl
index 47a91139f..7f15aa48f 100644
--- a/core/src/main/resources/template/simple/dynamic-attributes.ftl
+++ b/core/src/main/resources/template/simple/dynamic-attributes.ftl
@@ -30,7 +30,11 @@
<#list aKeys?filter(acceptKey) as aKey><#rt/>
<#assign keyValue = parameters.dynamicAttributes.get(aKey)/>
<#if keyValue?is_string>
- <#assign value = struts.translateVariables(keyValue)!keyValue/>
+ <#if evaluate_dynamic_attributes!false == true>
+ <#assign value = struts.translateVariables(keyValue)!keyValue/><#rt/>
+ <#else>
+ <#assign value = keyValue/><#rt/>
+ #if>
<#else>
<#assign value = keyValue?string/>
#if>
diff --git a/core/src/main/resources/template/simple/radiomap.ftl b/core/src/main/resources/template/simple/radiomap.ftl
index 5c37a4b4b..597300e95 100644
--- a/core/src/main/resources/template/simple/radiomap.ftl
+++ b/core/src/main/resources/template/simple/radiomap.ftl
@@ -27,7 +27,7 @@
<#assign itemKeyStr = stack.findString('top')>
#if>
<#if parameters.listValueKey??>
- <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
+ <#-- checks the valueStack for the 'valueKey.' The valueKey is then looked-up in the locale
file for it's localized value. This is then used as a label -->
<#assign valueKey = stack.findString(parameters.listValueKey)!''/>
<#if valueKey?has_content>
@@ -94,9 +94,10 @@
<#include "/${parameters.templateDir}/${parameters.expandTheme}/css.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/scripting-events.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
+<#global evaluate_dynamic_attributes = true/>
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
/><#rt/>
><#rt/>
${itemValue}<#t/>
-@s.iterator>
\ No newline at end of file
+@s.iterator>
diff --git a/core/src/site/resources/tags/a-attributes.html b/core/src/site/resources/tags/a-attributes.html
index e9cbb587f..e9fd9089c 100644
--- a/core/src/site/resources/tags/a-attributes.html
+++ b/core/src/site/resources/tags/a-attributes.html
@@ -112,7 +112,7 @@
escapeHtmlBody
false
-
true
+
false
false
Boolean
Specifies whether to HTML-escape the tag body or not
Specifies whether to HTML-escape the tag body or not
diff --git a/core/src/test/java/com/opensymphony/xwork2/interceptor/ParametersInterceptorTest.java b/core/src/test/java/com/opensymphony/xwork2/interceptor/ParametersInterceptorTest.java
index 381f909d3..515b7cffe 100644
--- a/core/src/test/java/com/opensymphony/xwork2/interceptor/ParametersInterceptorTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/interceptor/ParametersInterceptorTest.java
@@ -730,6 +730,33 @@ public class ParametersInterceptorTest extends XWorkTestCase {
assertEquals(expected, actual);
}
+ public void testDMIMethodsAreIgnored() throws Exception {
+ // given
+ ParametersInterceptor interceptor = createParametersInterceptor();
+ final Map actual = injectValueStackFactory(interceptor);
+ ValueStack stack = injectValueStack(actual);
+
+ final Map expected = new HashMap() {
+ {
+ put("ordinary.bean", "value");
+ }
+ };
+
+ Map parameters = new HashMap() {
+ {
+ put("ordinary.bean", "value");
+ put("action:", "myAction");
+ put("method:", "doExecute");
+ }
+ };
+
+ // when
+ interceptor.setParameters(new NoParametersAction(), stack, HttpParameters.create(parameters).build());
+
+ // then
+ assertEquals(expected, actual);
+ }
+
public void testBeanListSingleValue() throws Exception {
Map params = new HashMap<>();
params.put("beanList.name", new String[]{"Superman"});
diff --git a/core/src/test/java/org/apache/struts2/components/ComponentTest.java b/core/src/test/java/org/apache/struts2/components/ComponentTest.java
index 0bc098265..762e37ae3 100644
--- a/core/src/test/java/org/apache/struts2/components/ComponentTest.java
+++ b/core/src/test/java/org/apache/struts2/components/ComponentTest.java
@@ -18,14 +18,11 @@
*/
package org.apache.struts2.components;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Stack;
-
-import javax.servlet.jsp.tagext.TagSupport;
-
import com.opensymphony.xwork2.LocalizedTextProvider;
+import org.apache.struts2.StrutsException;
+import org.apache.struts2.TestConfigurationProvider;
import org.apache.struts2.views.jsp.AbstractTagTest;
+import org.apache.struts2.views.jsp.ActionTag;
import org.apache.struts2.views.jsp.BeanTag;
import org.apache.struts2.views.jsp.ElseIfTag;
import org.apache.struts2.views.jsp.ElseTag;
@@ -42,17 +39,19 @@ import org.apache.struts2.views.jsp.iterator.MergeIteratorTag;
import org.apache.struts2.views.jsp.ui.TextFieldTag;
import org.apache.struts2.views.jsp.ui.UpDownSelectTag;
+import javax.servlet.jsp.tagext.TagSupport;
import java.util.HashMap;
-import org.apache.struts2.StrutsException;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Stack;
/**
* Test case for method findAncestor(Class) in Component and some commons
* test cases for Component in general.
- *
*/
public class ComponentTest extends AbstractTagTest {
- public void testFindAncestorTest() throws Exception {
+ public void testFindAncestorTest() {
Property property = new Property(stack);
Form form = new Form(stack, request, response);
ActionComponent actionComponent = new ActionComponent(stack, request, response);
@@ -61,8 +60,8 @@ public class ComponentTest extends AbstractTagTest {
TextField textField = new TextField(stack, request, response);
- Stack stack = property.getComponentStack();
- Iterator i = stack.iterator();
+ Stack stack = property.getComponentStack();
+ Iterator i = stack.iterator();
try {
@@ -107,8 +106,7 @@ public class ComponentTest extends AbstractTagTest {
assertEquals(textField.findAncestor(Anchor.class), anchor);
assertEquals(textField.findAncestor(ActionComponent.class), actionComponent);
assertEquals(textField.findAncestor(Property.class), property);
- }
- finally {
+ } finally {
property.getComponentStack().pop();
property.getComponentStack().pop();
property.getComponentStack().pop();
@@ -118,16 +116,7 @@ public class ComponentTest extends AbstractTagTest {
}
// Action Component
- /*
- public void testActionComponentDisposeItselfFromComponentStack() throws Exception {
- ConfigurationManager.clearConfigurationProviders();
- ConfigurationManager.addConfigurationProvider(new TestConfigurationProvider());
- ConfigurationManager.getConfiguration().reload();
-
- ActionContext actionContext = new ActionContext(context);
- actionContext.setValueStack(stack);
- ActionContext.setContext(actionContext);
-
+ public void testActionComponentDisposeItselfFromComponentStack() {
request.setupGetServletPath(TestConfigurationProvider.TEST_NAMESPACE + "/" + "foo.action");
try {
TextFieldTag t = new TextFieldTag();
@@ -145,17 +134,14 @@ public class ComponentTest extends AbstractTagTest {
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
}
- */
-
// AppendInterator
- public void testAppendIteratorDisposeItselfFromComponentStack() throws Exception {
+ public void testAppendIteratorDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -170,8 +156,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -179,7 +164,7 @@ public class ComponentTest extends AbstractTagTest {
// Bean
- public void testBeanComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testBeanComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -195,8 +180,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail();
}
@@ -204,7 +188,7 @@ public class ComponentTest extends AbstractTagTest {
// ElseIf
- public void testElseIfComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testElseIfComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -219,8 +203,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -228,7 +211,7 @@ public class ComponentTest extends AbstractTagTest {
// Else
- public void testElseComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testElseComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -243,8 +226,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -252,7 +234,7 @@ public class ComponentTest extends AbstractTagTest {
// If
- public void testIfComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testIfComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -268,8 +250,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -277,7 +258,7 @@ public class ComponentTest extends AbstractTagTest {
// Iterator
- public void testIteratorComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testIteratorComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -291,15 +272,14 @@ public class ComponentTest extends AbstractTagTest {
tag.doStartTag();
assertEquals(tag.getComponent().getComponentStack().peek(), tag.getComponent());
int endIt = tag.doAfterBody();
- while(TagSupport.EVAL_BODY_AGAIN == endIt) {
+ while (TagSupport.EVAL_BODY_AGAIN == endIt) {
assertEquals(tag.getComponent().getComponentStack().peek(), tag.getComponent());
endIt = tag.doAfterBody();
}
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -307,7 +287,7 @@ public class ComponentTest extends AbstractTagTest {
// MergeIterator
- public void testMergeIteratorComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testMergeIteratorComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -322,8 +302,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -331,7 +310,7 @@ public class ComponentTest extends AbstractTagTest {
// Property
- public void testPropertyComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testPropertyComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -346,8 +325,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -355,7 +333,7 @@ public class ComponentTest extends AbstractTagTest {
// Push
- public void testPushComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testPushComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -371,8 +349,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -380,7 +357,7 @@ public class ComponentTest extends AbstractTagTest {
// Set
- public void testSetComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testSetComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -397,8 +374,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -406,7 +382,7 @@ public class ComponentTest extends AbstractTagTest {
// Text
- public void testTextComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testTextComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -422,15 +398,14 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
}
- public void testI18nComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testI18nComponentDisposeItselfFromComponentStack() {
stack.getActionContext().withLocale(Locale.getDefault());
TextFieldTag t = new TextFieldTag();
@@ -450,15 +425,14 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
}
// URL
- public void testURLComponentDisposeItselfFromComponentStack() throws Exception {
+ public void testURLComponentDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -473,15 +447,14 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
}
// updownselect
- public void testUpDownSelectDisposeItselfFromComponentStack() throws Exception {
+ public void testUpDownSelectDisposeItselfFromComponentStack() {
TextFieldTag t = new TextFieldTag();
t.setPageContext(pageContext);
t.setName("textFieldName");
@@ -499,8 +472,7 @@ public class ComponentTest extends AbstractTagTest {
tag.doEndTag();
assertEquals(t.getComponent().getComponentStack().peek(), t.getComponent());
t.doEndTag();
- }
- catch(Exception e) {
+ } catch (Exception e) {
e.printStackTrace();
fail(e.toString());
}
@@ -508,22 +480,22 @@ public class ComponentTest extends AbstractTagTest {
/**
* Test the expected default behaviour for component body state methods.
- */
+ */
public void testComponentDefaultBodyStates() {
Component component = new Component(stack);
// Test expected default results for: usesBody(), escapeHtmlBody().
assertFalse("Component default usesBody not false ?", component.usesBody());
- assertTrue("Component default htmlEscapeBody not true ?", component.escapeHtmlBody());
+ assertFalse("Component default htmlEscapeBody not false ?", component.escapeHtmlBody());
}
/**
* Test the behaviour for Anchor body state methods.
- */
+ */
public void testAnchorBodyStates() {
Anchor anchor = new Anchor(stack, request, response);
// Test expected default results for: usesBody(), escapeHtmlBody().
assertTrue("Anchor default usesBody not true ?", anchor.usesBody());
- assertTrue("Anchor default htmlEscapeBody not true ?", anchor.escapeHtmlBody());
+ assertFalse("Anchor default htmlEscapeBody not false ?", anchor.escapeHtmlBody());
anchor.setEscapeHtmlBody(false);
assertFalse("Anchor htmlEscapeBody not false after set false ?", anchor.escapeHtmlBody());
anchor.setEscapeHtmlBody(true);
@@ -532,12 +504,12 @@ public class ComponentTest extends AbstractTagTest {
/**
* Test the behaviour for Submit body state methods.
- */
- public void testAnchortBodyStates() {
+ */
+ public void testSubmitBodyStates() {
Submit submit = new Submit(stack, request, response);
// Test expected default results for: usesBody(), escapeHtmlBody().
assertTrue("Submit default usesBody not true ?", submit.usesBody());
- assertTrue("Submit default htmlEscapeBody not true ?", submit.escapeHtmlBody());
+ assertFalse("Submit default htmlEscapeBody not false ?", submit.escapeHtmlBody());
submit.setEscapeHtmlBody(false);
assertFalse("Submit htmlEscapeBody not false after set false ?", submit.escapeHtmlBody());
submit.setEscapeHtmlBody(true);
@@ -547,10 +519,8 @@ public class ComponentTest extends AbstractTagTest {
/**
* Attempt some code coverage tests for {@link Component} that can be achieved without
* too much difficulty.
- *
- * @throws Exception
*/
- public void testComponent_coverageTest() throws Exception {
+ public void testComponent_coverageTest() {
HashMap propertyMap = new HashMap<>();
Exception exception = new Exception("Generic exception");
Property property = new Property(stack);
@@ -586,8 +556,7 @@ public class ComponentTest extends AbstractTagTest {
assertFalse("Initial performClearTagStateForTagPoolingServers not false ?", actionComponent.getPerformClearTagStateForTagPoolingServers());
actionComponent.setPerformClearTagStateForTagPoolingServers(true);
assertTrue("performClearTagStateForTagPoolingServers false after setting to true ?", actionComponent.getPerformClearTagStateForTagPoolingServers());
- }
- finally {
+ } finally {
property.getComponentStack().pop();
}
}
diff --git a/core/src/test/java/org/apache/struts2/components/DateTest.java b/core/src/test/java/org/apache/struts2/components/DateTest.java
new file mode 100644
index 000000000..2b9ca8bd7
--- /dev/null
+++ b/core/src/test/java/org/apache/struts2/components/DateTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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 com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
+import org.apache.struts2.StrutsInternalTestCase;
+import org.apache.struts2.components.date.SimpleDateFormatAdapter;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+
+public class DateTest extends StrutsInternalTestCase {
+
+ private Map context;
+ private ValueStack stack;
+
+ public void testSupportSimpleDateTimeFormat() {
+ // given
+ Date date = new Date(stack);
+ date.setDateFormatter(new SimpleDateFormatAdapter());
+
+ String format = "EEEE MMMM dd, hh:mm aa";
+ java.util.Date now = new java.util.Date();
+
+ String expected = new SimpleDateFormat(format, ActionContext.getContext().getLocale()).format(now);
+ context.put("myDate", now);
+
+ Writer writer = new StringWriter();
+
+ // when
+ date.setFormat(format);
+ date.setName("myDate");
+ date.setNice(false);
+ date.start(writer);
+ date.end(writer, "");
+
+ // then
+ assertEquals(expected, writer.toString());
+ }
+
+ public void testDefaultFormat() {
+ // given
+ Date date = new Date(stack);
+ date.setDateFormatter(new SimpleDateFormatAdapter());
+
+ java.util.Date now = new java.util.Date();
+
+ String expected = SimpleDateFormat.getDateInstance(DateFormat.MEDIUM, ActionContext.getContext().getLocale()).format(now);
+ context.put("myDate", now);
+
+ Writer writer = new StringWriter();
+
+ // when
+ date.setName("myDate");
+ date.setNice(false);
+ date.start(writer);
+ date.end(writer, "");
+
+ // then
+ assertEquals(expected, writer.toString());
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ stack = container.getInstance(ValueStackFactory.class).createValueStack();
+ context = stack.getContext();
+ }
+}
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/AnchorTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/AnchorTest.java
index 8101cc8a1..7c3f80d71 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/AnchorTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/AnchorTest.java
@@ -18,17 +18,15 @@
*/
package org.apache.struts2.views.jsp.ui;
+import org.apache.struts2.StrutsConstants;
import org.apache.struts2.TestAction;
+import org.apache.struts2.components.Anchor;
import org.apache.struts2.views.jsp.AbstractUITagTest;
-import javax.servlet.jsp.JspException;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
-
-/**
- */
public class AnchorTest extends AbstractUITagTest {
public void testBeanInfo() throws Exception {
@@ -72,7 +70,7 @@ public class AnchorTest extends AbstractUITagTest {
AnchorTag freshTag = new AnchorTag();
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
- assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
+ assertTrue("Tag state after doEndTag() and explicit tag state clearing is unequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -111,7 +109,7 @@ public class AnchorTest extends AbstractUITagTest {
AnchorTag freshTag = new AnchorTag();
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
- assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
+ assertTrue("Tag state after doEndTag() and explicit tag state clearing is unequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -156,7 +154,7 @@ public class AnchorTest extends AbstractUITagTest {
AnchorTag freshTag = new AnchorTag();
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
- assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
+ assertTrue("Tag state after doEndTag() and explicit tag state clearing is unequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -201,7 +199,7 @@ public class AnchorTest extends AbstractUITagTest {
AnchorTag freshTag = new AnchorTag();
freshTag.setPerformClearTagStateForTagPoolingServers(true);
freshTag.setPageContext(pageContext);
- assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
+ assertTrue("Tag state after doEndTag() and explicit tag state clearing is unequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
@@ -211,7 +209,7 @@ public class AnchorTest extends AbstractUITagTest {
testAction.setFoo("bar");
}
- private AnchorTag createTag() throws JspException {
+ private AnchorTag createTag() {
AnchorTag tag = new AnchorTag();
tag.setPageContext(pageContext);
@@ -221,8 +219,6 @@ public class AnchorTest extends AbstractUITagTest {
/**
* Test anchor tag body supported
- *
- * @throws Exception
*/
public void testSimpleWithBody() throws Exception {
createAction();
@@ -242,8 +238,6 @@ public class AnchorTest extends AbstractUITagTest {
/**
* Test that by default anchor tag body is HTML-escaped.
- *
- * @throws Exception
*/
public void testSimpleWithBodyHTMLEscaped() throws Exception {
createAction();
@@ -254,6 +248,7 @@ public class AnchorTest extends AbstractUITagTest {
StrutsBodyContent body = new StrutsBodyContent(null);
body.print("should HTML escape: < & >");
tag.setBodyContent(body);
+ tag.setEscapeHtmlBody("true");
tag.doStartTag();
tag.doEndTag();
@@ -263,15 +258,13 @@ public class AnchorTest extends AbstractUITagTest {
/**
* Test that with htmlEscapeBody false anchor tag body is not HTML-escaped.
- *
- * @throws Exception
*/
public void testSimpleWithBodyNotHTMLEscaped() throws Exception {
createAction();
AnchorTag tag = createTag();
tag.setHref("a");
- tag.setEscapeHtmlBody(false);
+ tag.setEscapeHtmlBody("false");
StrutsBodyContent body = new StrutsBodyContent(null);
body.print("should not HTML escape: < & >");
@@ -282,4 +275,50 @@ public class AnchorTest extends AbstractUITagTest {
verifyResource("href-5.txt");
}
+
+ public void testInjectEscapeHtmlBodyFlag() throws Exception {
+ // given
+ initDispatcherWithConfigs("struts-default.xml, struts-escape-body.xml");
+ String escapeHtmlBody = container.getInstance(String.class, StrutsConstants.STRUTS_UI_ESCAPE_HTML_BODY);
+ assertEquals("true", escapeHtmlBody);
+
+ createMocks();
+
+ createAction();
+
+ AnchorTag tag = createTag();
+
+ // when
+ tag.doStartTag();
+
+ // then
+ Anchor component = (Anchor) tag.getComponent();
+ assertTrue(component.escapeHtmlBody());
+
+ tag.doEndTag();
+ }
+
+ public void testTagAttributeTakesPrecedenceOverInjectEscapeHtmlBodyFlag() throws Exception {
+ // given
+ initDispatcherWithConfigs("struts-default.xml, struts-escape-body.xml");
+ String escapeHtmlBody = container.getInstance(String.class, StrutsConstants.STRUTS_UI_ESCAPE_HTML_BODY);
+ assertEquals("true", escapeHtmlBody);
+
+ createMocks();
+
+ createAction();
+
+ AnchorTag tag = createTag();
+ tag.setEscapeHtmlBody("false");
+
+ // when
+ tag.doStartTag();
+
+ // then
+ Anchor component = (Anchor) tag.getComponent();
+ assertFalse(component.escapeHtmlBody());
+
+ tag.doEndTag();
+ }
+
}
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java
index 778fb19f4..783d5aefd 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/CheckboxTest.java
@@ -40,8 +40,8 @@ public class CheckboxTest extends AbstractUITagTest {
* as key.
*/
@Override
- protected Map initializedGenericTagTestProperties() {
- Map result = super.initializedGenericTagTestProperties();
+ protected Map initializedGenericTagTestProperties() {
+ Map result = super.initializedGenericTagTestProperties();
new PropertyHolder("value", "true").addToMap(result);
return result;
}
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/DateTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/DateTagTest.java
index 6f6e31ecc..05e267e05 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/DateTagTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/DateTagTest.java
@@ -19,35 +19,33 @@
package org.apache.struts2.views.jsp.ui;
import com.opensymphony.xwork2.ActionContext;
-
import org.apache.struts2.TestAction;
+import org.apache.struts2.components.Component;
+import org.apache.struts2.components.DateTextField;
import org.apache.struts2.views.jsp.AbstractTagTest;
import org.apache.struts2.views.jsp.DateTag;
+import javax.servlet.jsp.JspException;
import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.time.Instant;
+import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
-import java.util.TimeZone;
-import org.apache.struts2.components.Component;
-import org.apache.struts2.components.DateTextField;
/**
* Unit test for {@link org.apache.struts2.components.Date}.
- *
*/
public class DateTagTest extends AbstractTagTest {
private DateTag tag;
- public void testCustomFormat() throws Exception {
+ public void testCustomFormatForDateTime() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = new Date();
- String formatted = new SimpleDateFormat(format).format(now);
+ LocalDateTime now = LocalDateTime.now();
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
tag.setName("myDate");
@@ -62,13 +60,55 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ }
+
+ public void testCustomFormatForLong() throws Exception {
+ String format = "yyyy/MM/dd";
+ long now = new Date().getTime();
+ String formatted = DateTimeFormatter.ofPattern(format).format(Instant.ofEpochMilli(now).atZone(ZoneId.systemDefault()));
+ context.put("myDate", now);
+
+ tag.setName("myDate");
+ tag.setNice(false);
+ tag.setFormat(format);
+ tag.doStartTag();
+ tag.doEndTag();
+ assertEquals(formatted, writer.toString());
+
+ // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+ DateTag freshTag = new DateTag();
+ freshTag.setPageContext(pageContext);
+ assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ }
+
+ public void testCustomFormatForDate() throws Exception {
+ String format = "yyyy/MM/dd";
+ LocalDate now = LocalDate.now();
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
+ context.put("myDate", now);
+
+ tag.setName("myDate");
+ tag.setNice(false);
+ tag.setFormat(format);
+ tag.doStartTag();
+ tag.doEndTag();
+ assertEquals(formatted, writer.toString());
+
+ // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+ DateTag freshTag = new DateTag();
+ freshTag.setPageContext(pageContext);
+ assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormat_clearTagStateSet() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = new Date();
- String formatted = new SimpleDateFormat(format).format(now);
+ LocalDateTime now = LocalDateTime.now();
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
@@ -86,13 +126,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomGlobalFormatFormat() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = new Date();
- String formatted = new SimpleDateFormat(format).format(now);
+ LocalDateTime now = LocalDateTime.now();
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
((TestAction) action).setText(org.apache.struts2.components.Date.DATETAG_PROPERTY, format);
@@ -106,10 +146,8 @@ public class DateTagTest extends AbstractTagTest {
public void testCustomFormatWithTimezone() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+1")).getTime();
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- sdf.setTimeZone(TimeZone.getTimeZone("GMT+1"));
- String formatted = sdf.format(now);
+ LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+1"));
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
tag.setName("myDate");
@@ -126,15 +164,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatWithTimezone_clearTagStateSet() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+1")).getTime();
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- sdf.setTimeZone(TimeZone.getTimeZone("GMT+1"));
- String formatted = sdf.format(now);
+ LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+1"));
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
@@ -153,15 +189,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatWithTimezoneAsExpression() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+2")).getTime();
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- sdf.setTimeZone(TimeZone.getTimeZone("GMT+2"));
- String formatted = sdf.format(now);
+ LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+2"));
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
context.put("myTimezone", "GMT+2");
@@ -178,15 +212,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatWithTimezoneAsExpression_clearTagStateSet() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = Calendar.getInstance(TimeZone.getTimeZone("GMT+2")).getTime();
- SimpleDateFormat sdf = new SimpleDateFormat(format);
- sdf.setTimeZone(TimeZone.getTimeZone("GMT+2"));
- String formatted = sdf.format(now);
+ LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+2"));
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
context.put("myTimezone", "GMT+2");
@@ -206,13 +238,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatCalendar() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
Calendar calendar = Calendar.getInstance();
- String formatted = new SimpleDateFormat(format).format(calendar.getTime());
+ String formatted = DateTimeFormatter.ofPattern(format).format(calendar.toInstant().atZone(ZoneId.systemDefault()));
context.put("myDate", calendar);
tag.setName("myDate");
@@ -227,13 +259,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatCalendar_clearTagStateSet() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
Calendar calendar = Calendar.getInstance();
- String formatted = new SimpleDateFormat(format).format(calendar.getTime());
+ String formatted = DateTimeFormatter.ofPattern(format).format(calendar.toInstant().atZone(ZoneId.systemDefault()));
context.put("myDate", calendar);
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
@@ -251,13 +283,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatLong() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
Date date = new Date();
- String formatted = new SimpleDateFormat(format).format(date);
+ String formatted = DateTimeFormatter.ofPattern(format).format(date.toInstant().atZone(ZoneId.systemDefault()));
// long
context.put("myDate", date.getTime());
@@ -273,13 +305,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatLong_clearTagStateSet() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
Date date = new Date();
- String formatted = new SimpleDateFormat(format).format(date);
+ String formatted = DateTimeFormatter.ofPattern(format).format(date.toInstant().atZone(ZoneId.systemDefault()));
// long
context.put("myDate", date.getTime());
@@ -298,7 +330,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatLocalDateTime() throws Exception {
@@ -332,7 +364,7 @@ public class DateTagTest extends AbstractTagTest {
public void testDefaultFormat() throws Exception {
Date now = new Date();
String formatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
- ActionContext.getContext().getLocale()).format(now);
+ ActionContext.getContext().getLocale()).format(now);
context.put("myDate", now);
tag.setName("myDate");
@@ -346,13 +378,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testDefaultFormat_clearTagStateSet() throws Exception {
Date now = new Date();
String formatted = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
- ActionContext.getContext().getLocale()).format(now);
+ ActionContext.getContext().getLocale()).format(now);
context.put("myDate", now);
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
@@ -369,13 +401,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatAndComponent() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = new Date();
- String formatted = new SimpleDateFormat(format).format(now);
+ LocalDateTime now = LocalDateTime.now();
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
tag.setName("myDate");
@@ -388,7 +420,7 @@ public class DateTagTest extends AbstractTagTest {
org.apache.struts2.components.Date component = (org.apache.struts2.components.Date) tag.getComponent();
assertEquals("myDate", component.getName());
assertEquals(format, component.getFormat());
- assertEquals(false, component.isNice());
+ assertFalse(component.isNice());
tag.doEndTag();
@@ -399,13 +431,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testCustomFormatAndComponent_clearTagStateSet() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = new Date();
- String formatted = new SimpleDateFormat(format).format(now);
+ LocalDateTime now = LocalDateTime.now();
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
@@ -420,7 +452,7 @@ public class DateTagTest extends AbstractTagTest {
org.apache.struts2.components.Date component = (org.apache.struts2.components.Date) tag.getComponent();
assertEquals("myDate", component.getName());
assertEquals(format, component.getFormat());
- assertEquals(false, component.isNice());
+ assertFalse(component.isNice());
tag.doEndTag();
@@ -432,13 +464,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testSetId() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = new Date();
- String formatted = new SimpleDateFormat(format).format(now);
+ LocalDateTime now = LocalDateTime.now();
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
tag.setName("myDate");
@@ -454,13 +486,13 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testSetId_clearTagStateSet() throws Exception {
String format = "yyyy/MM/dd hh:mm:ss";
- Date now = new Date();
- String formatted = new SimpleDateFormat(format).format(now);
+ LocalDateTime now = LocalDateTime.now();
+ String formatted = DateTimeFormatter.ofPattern(format).format(now);
context.put("myDate", now);
tag.setPerformClearTagStateForTagPoolingServers(true); // Explicitly request tag state clearing.
@@ -479,7 +511,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureNiceHour() throws Exception {
@@ -501,7 +533,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureNiceHour_clearTagStateSet() throws Exception {
@@ -526,7 +558,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testPastNiceHour() throws Exception {
@@ -548,7 +580,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testPastNiceHour_clearTagStateSet() throws Exception {
@@ -573,7 +605,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureNiceHourMinSec() throws Exception {
@@ -596,7 +628,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureNiceHourMinSec_clearTagStateSet() throws Exception {
@@ -622,7 +654,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testPastNiceHourMin() throws Exception {
@@ -645,7 +677,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testPastNiceHourMin_clearTagStateSet() throws Exception {
@@ -671,7 +703,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureLessOneMin() throws Exception {
@@ -693,7 +725,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureLessOneMin_clearTagStateSet() throws Exception {
@@ -718,7 +750,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureLessOneHour() throws Exception {
@@ -740,7 +772,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureLessOneHour_clearTagStateSet() throws Exception {
@@ -765,7 +797,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureLessOneYear() throws Exception {
@@ -787,7 +819,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureLessOneYear_clearTagStateSet() throws Exception {
@@ -812,7 +844,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureTwoYears() throws Exception {
@@ -838,7 +870,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testFutureTwoYears_clearTagStateSet() throws Exception {
@@ -867,7 +899,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testNoDateObjectInContext() throws Exception {
@@ -884,7 +916,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
public void testNoDateObjectInContext_clearTagStateSet() throws Exception {
@@ -904,7 +936,7 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(tag, freshTag));
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
/**
@@ -912,10 +944,8 @@ public class DateTagTest extends AbstractTagTest {
* since that tag does not have its own unit tests, and it also appears to be
* a broken tag. The code coverage tests can be moved if the tag is fixed, or
* removed if the tag is dropped.
- *
- * @throws Exception
*/
- public void testDateTextFieldTag_artificialCoverageTest() throws Exception {
+ public void testDateTextFieldTag_artificialCoverageTest() throws JspException {
final String format = "yyyy/MM/dd hh:mm:ss";
DateTextFieldTag dateTextFieldTag = createDateTextFieldTag();
dateTextFieldTag.setFormat(format);
@@ -939,20 +969,40 @@ public class DateTagTest extends AbstractTagTest {
freshTag.setPageContext(pageContext);
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
- strutsBodyTagsAreReflectionEqual(dateTextFieldTag, freshTag));
+ strutsBodyTagsAreReflectionEqual(dateTextFieldTag, freshTag));
+ }
+
+ public void testNewJava8Format() throws Exception {
+ String format = "EEEE MMMM dd, hh:mm a";
+ LocalDateTime now = LocalDateTime.now();
+ String formatted = DateTimeFormatter.ofPattern(format, ActionContext.getContext().getLocale()).format(now);
+ context.put("myDate", now);
+
+ tag.setName("myDate");
+ tag.setNice(false);
+ tag.setFormat(format);
+ tag.doStartTag();
+ tag.doEndTag();
+ assertEquals(formatted, writer.toString());
+
+ // Basic sanity check of clearTagStateForTagPoolingServers() behaviour for Struts Tags after doEndTag().
+ DateTag freshTag = new DateTag();
+ freshTag.setPageContext(pageContext);
+ assertFalse("Tag state after doEndTag() under default tag clear state is equal to new Tag with pageContext/parent set. " +
+ "May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
+ strutsBodyTagsAreReflectionEqual(tag, freshTag));
}
/**
* Utility method to create a new {@link DateTextFieldTag} instance for code coverage tests.
- *
+ *
* Note: There is no datetextfield.ftl template for the tag, so it does not appear that it can
- * actually be used in practice. We can perform basic coverage tests from within this
- * unit test class until the {@link DateTextFieldTag} is fixed or removed.
- *
+ * actually be used in practice. We can perform basic coverage tests from within this
+ * unit test class until the {@link DateTextFieldTag} is fixed or removed.
+ *
* @return a basic {@link DateTextFieldTag} instance
- * @throws Exception
*/
- private DateTextFieldTag createDateTextFieldTag() throws Exception {
+ private DateTextFieldTag createDateTextFieldTag() {
DateTextFieldTag tag = new DateTextFieldTag();
tag.setPageContext(pageContext);
tag.setName("myDate");
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java
index 32b932229..d5a296c7f 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/FileTest.java
@@ -96,8 +96,8 @@ public class FileTest extends AbstractUITagTest {
* as key.
*/
@Override
- protected Map initializedGenericTagTestProperties() {
- Map result = super.initializedGenericTagTestProperties();
+ protected Map initializedGenericTagTestProperties() {
+ Map result = super.initializedGenericTagTestProperties();
new PropertyHolder("accept", "someAccepted").addToMap(result);
new PropertyHolder("size", "101").addToMap(result);
new PropertyHolder("value", "", "").addToMap(result);
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java
index 6f573ae07..99eff80d5 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/SubmitTest.java
@@ -630,8 +630,8 @@ public class SubmitTest extends AbstractUITagTest {
* as key.
*/
@Override
- protected Map initializedGenericTagTestProperties() {
- Map result = new HashMap();
+ protected Map initializedGenericTagTestProperties() {
+ Map result = new HashMap<>();
new PropertyHolder("title", "someTitle").addToMap(result);
new PropertyHolder("cssClass", "cssClass1", "class=\"cssClass1\"").addToMap(result);
new PropertyHolder("cssStyle", "cssStyle1", "style=\"cssStyle1\"").addToMap(result);
@@ -652,8 +652,6 @@ public class SubmitTest extends AbstractUITagTest {
/**
* Test that by default submit tag body is HTML-escaped.
- *
- * @throws Exception
*/
public void testSubmitWithBodyHTMLEscaped() throws Exception {
TestAction testAction = (TestAction) action;
@@ -670,6 +668,7 @@ public class SubmitTest extends AbstractUITagTest {
StrutsBodyContent body = new StrutsBodyContent(null);
body.print("should HTML escape: < & >");
tag.setBodyContent(body);
+ tag.setEscapeHtmlBody(true);
tag.doStartTag();
tag.doEndTag();
@@ -678,8 +677,6 @@ public class SubmitTest extends AbstractUITagTest {
/**
* Test that with htmlEscapeBody false submit tag body is not HTML-escaped.
- *
- * @throws Exception
*/
public void testSubmitWithBodyNotHTMLEscaped() throws Exception {
TestAction testAction = (TestAction) action;
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java
index 54fa4787a..d94bbd2c5 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextareaTest.java
@@ -130,8 +130,8 @@ public class TextareaTest extends AbstractUITagTest {
* as key.
*/
@Override
- protected Map initializedGenericTagTestProperties() {
- Map result = super.initializedGenericTagTestProperties();
+ protected Map initializedGenericTagTestProperties() {
+ Map result = super.initializedGenericTagTestProperties();
new PropertyHolder("cols", "10").addToMap(result);
new PropertyHolder("rows", "11").addToMap(result);
new PropertyHolder("readonly", "true", "readonly=\"readonly\"").addToMap(result);
diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
index cf300e806..f42fdbccf 100644
--- a/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
+++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/TextfieldTest.java
@@ -347,6 +347,7 @@ public class TextfieldTest extends AbstractUITagTest {
tag.setValue("%{foo}");
tag.setSize("10");
tag.setDynamicAttribute(null, "anotherAttr", "%{foo}");
+ tag.setDynamicAttribute(null, "secondAttr", "second_%{foo}");
tag.doStartTag();
tag.doEndTag();
@@ -373,6 +374,7 @@ public class TextfieldTest extends AbstractUITagTest {
tag.setValue("%{foo}");
tag.setSize("10");
tag.setDynamicAttribute(null, "anotherAttr", "%{foo}");
+ tag.setDynamicAttribute(null, "secondAttr", "second_%{foo}");
tag.doStartTag();
setComponentTagClearTagState(tag, true); // Ensure component tag state clearing is set true (to match tag).
diff --git a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
index 281847627..5dc125c18 100644
--- a/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
+++ b/core/src/test/resources/org/apache/struts2/views/jsp/ui/Textfield-5.txt
@@ -1,4 +1,4 @@