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 <lukaszlen...@apache.org>
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();
+    }
+}

Reply via email to