This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch WW-5016-uses-proper-format
in repository https://gitbox.apache.org/repos/asf/struts.git
The following commit(s) were added to refs/heads/WW-5016-uses-proper-format by
this push:
new e3dff76 WW-5016 Introduces different format adapters to allow use
different APIs
e3dff76 is described below
commit e3dff7691e72a30cf6ebcad1bbace48e56f56380
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sun Feb 20 13:29:46 2022 +0100
WW-5016 Introduces different format adapters to allow use different APIs
---
.../StrutsDefaultConfigurationProvider.java | 6 ++
.../java/org/apache/struts2/StrutsConstants.java | 3 +
.../java/org/apache/struts2/components/Date.java | 78 ++++++++++---------
.../struts2/components/date/DateFormatter.java | 33 ++++++++
.../components/date/DateTimeFormatterAdapter.java | 47 +++++++++++
.../components/date/SimpleDateFormatAdapter.java | 48 ++++++++++++
.../config/StrutsBeanSelectionProvider.java | 5 +-
.../org/apache/struts2/default.properties | 6 ++
core/src/main/resources/struts-default.xml | 5 +-
.../org/apache/struts2/components/DateTest.java | 90 ++++++++++++++++++++++
10 files changed, 280 insertions(+), 41 deletions(-)
diff --git
a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
index 400674d..a5dcf99 100644
---
a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
+++
b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
@@ -56,6 +56,9 @@ import com.opensymphony.xwork2.conversion.impl.DateConverter;
import
com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
+import org.apache.struts2.components.date.DateFormatter;
+import org.apache.struts2.components.date.DateTimeFormatterAdapter;
+import org.apache.struts2.components.date.SimpleDateFormatAdapter;
import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
import org.apache.struts2.conversion.StrutsTypeConverterCreator;
@@ -218,6 +221,9 @@ public class StrutsDefaultConfigurationProvider implements
ConfigurationProvider
, Scope.SINGLETON)
.factory(ValueSubstitutor.class, EnvsValueSubstitutor.class,
Scope.SINGLETON)
+
+ .factory(DateFormatter.class, "simpleDateFormat",
SimpleDateFormatAdapter.class, Scope.SINGLETON)
+ .factory(DateFormatter.class, "dateTimeFormatter",
DateTimeFormatterAdapter.class, Scope.SINGLETON)
;
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION,
Boolean.FALSE.toString());
diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java
b/core/src/main/java/org/apache/struts2/StrutsConstants.java
index 789b7c3..64ac93b 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;
/**
@@ -384,4 +385,6 @@ public final class StrutsConstants {
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/Date.java
b/core/src/main/java/org/apache/struts2/components/Date.java
index ec9bb19..0560057 100644
--- a/core/src/main/java/org/apache/struts2/components/Date.java
+++ b/core/src/main/java/org/apache/struts2/components/Date.java
@@ -18,11 +18,13 @@
*/
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.StrutsConstants;
+import org.apache.struts2.components.date.DateFormatter;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
@@ -33,8 +35,6 @@ 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;
@@ -157,6 +157,7 @@ import java.util.List;
public class Date extends ContextBean {
private static final Logger LOG = LogManager.getLogger(Date.class);
+
/**
* Property name to fall back when no format is specified
*/
@@ -208,17 +209,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;
}
/**
@@ -286,6 +288,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
@@ -304,10 +308,9 @@ public class Date extends ContextBean {
date = ((Instant) dateObject).atZone(tz);
} else {
if (devMode) {
- TextProvider tp = findProviderInStack();
String developerNotification = "";
- if (tp != null) {
- developerNotification = findProviderInStack().getText(
+ if (textProvider != null) {
+ developerNotification = textProvider.getText(
"devmode.notification",
"Developer Notification:\n{0}",
new String[]{
@@ -329,33 +332,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 {
@@ -373,6 +354,18 @@ public class Date extends ContextBean {
return super.end(writer, "");
}
+ private String formatDate(TextProvider textProvider, ZonedDateTime date) {
+ // if the format is not specified, fall back using the defined
property DATETAG_PROPERTY
+ String globalFormat = textProvider.getText(Date.DATETAG_PROPERTY);
+ if (DATETAG_PROPERTY.equals(globalFormat)) {
+ // if tp.getText can not find the property then the
+ // returned string is the same as input = DATETAG_PROPERTY
+ globalFormat = null;
+ }
+
+ return dateFormatter.format(date, format, globalFormat);
+ }
+
private ZoneId getTimeZone() {
ZoneId tz = ZoneId.systemDefault();
if (timezone != null) {
@@ -386,6 +379,15 @@ public class Date extends ContextBean {
return tz;
}
+ 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;
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 0000000..7ad276a
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/components/date/DateFormatter.java
@@ -0,0 +1,33 @@
+/*
+ * 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 {
+
+ String format(TemporalAccessor temporal, String format, String
defaultFormat);
+
+}
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 0000000..64bb4b1
--- /dev/null
+++
b/core/src/main/java/org/apache/struts2/components/date/DateTimeFormatterAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * 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, String
defaultFormat) {
+ DateTimeFormatter dtf;
+ Locale locale = ActionContext.getContext().getLocale();
+ if (format == null) {
+ if (defaultFormat != null) {
+ dtf = DateTimeFormatter.ofPattern(defaultFormat, locale);
+ } else {
+ 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 0000000..e9f29f0
--- /dev/null
+++
b/core/src/main/java/org/apache/struts2/components/date/SimpleDateFormatAdapter.java
@@ -0,0 +1,48 @@
+/*
+ * 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, String
defaultFormat) {
+ DateFormat df;
+ Locale locale = ActionContext.getContext().getLocale();
+ if (format == null) {
+ if (defaultFormat != null) {
+ df = new SimpleDateFormat(defaultFormat, locale);
+ } else {
+ 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/config/StrutsBeanSelectionProvider.java
b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
index ade6aa0..69aa925 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/resources/org/apache/struts2/default.properties
b/core/src/main/resources/org/apache/struts2/default.properties
index 571dcf5..57b692a 100644
--- a/core/src/main/resources/org/apache/struts2/default.properties
+++ b/core/src/main/resources/org/apache/struts2/default.properties
@@ -243,4 +243,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 for from details and 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 9dd8fbf..96b17b4 100644
--- a/core/src/main/resources/struts-default.xml
+++ b/core/src/main/resources/struts-default.xml
@@ -216,7 +216,7 @@
<bean type="com.opensymphony.xwork2.UnknownHandlerManager"
class="com.opensymphony.xwork2.DefaultUnknownHandlerManager" name="struts" />
<bean type="org.apache.struts2.dispatcher.DispatcherErrorHandler"
name="struts"
class="org.apache.struts2.dispatcher.DefaultDispatcherErrorHandler" />
-
+
<!-- Silly workarounds for OGNL since there is currently no way to flush
its internal caches -->
<bean type="ognl.PropertyAccessor" name="java.util.ArrayList"
class="com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor" />
<bean type="ognl.PropertyAccessor" name="java.util.HashSet"
class="com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor" />
@@ -228,6 +228,9 @@
<bean type="com.opensymphony.xwork2.config.providers.ValueSubstitutor"
class="com.opensymphony.xwork2.config.providers.EnvsValueSubstitutor"
scope="singleton"/>
+ <bean type="org.apache.struts2.components.date.DateFormatter"
name="simpleDateFormatter"
class="org.apache.struts2.components.date.SimpleDateFormatAdapter"
scope="singleton"/>
+ <bean type="org.apache.struts2.components.date.DateFormatter"
name="dateTimeFormatter"
class="org.apache.struts2.components.date.DateTimeFormatterAdapter"
scope="singleton"/>
+
<package name="struts-default" abstract="true">
<result-types>
<result-type name="chain"
class="com.opensymphony.xwork2.ActionChainResult"/>
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 0000000..2b9ca8b
--- /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<String, Object> 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();
+ }
+}