diff --git a/core/src/main/java/com/opensymphony/xwork2/ActionSupport.java b/core/src/main/java/com/opensymphony/xwork2/ActionSupport.java index 8c7e15e609..ab1a18099a 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ActionSupport.java +++ b/core/src/main/java/com/opensymphony/xwork2/ActionSupport.java @@ -90,6 +90,11 @@ public boolean isValidLocale(Locale locale) { return getLocaleProvider().isValidLocale(locale); } + @Override + public Locale toLocale(String localeStr) { + return getLocaleProvider().toLocale(localeStr); + } + @Override public boolean hasKey(String key) { return getTextProvider().hasKey(key); diff --git a/core/src/main/java/com/opensymphony/xwork2/DefaultLocaleProvider.java b/core/src/main/java/com/opensymphony/xwork2/DefaultLocaleProvider.java index da89306c0c..35f16191a8 100644 --- a/core/src/main/java/com/opensymphony/xwork2/DefaultLocaleProvider.java +++ b/core/src/main/java/com/opensymphony/xwork2/DefaultLocaleProvider.java @@ -46,17 +46,23 @@ public Locale getLocale() { @Override public boolean isValidLocaleString(String localeStr) { + Locale locale = this.toLocale(localeStr); + return isValidLocale(locale); + } + + @Override + public boolean isValidLocale(Locale locale) { + return locale != null && LocaleUtils.isAvailableLocale(locale); + } + + @Override + public Locale toLocale(String localeStr) { Locale locale = null; try { locale = LocaleUtils.toLocale(StringUtils.trimToNull(localeStr)); } catch (IllegalArgumentException e) { LOG.warn(new ParameterizedMessage("Cannot convert [{}] to proper locale", localeStr), e); } - return isValidLocale(locale); - } - - @Override - public boolean isValidLocale(Locale locale) { - return LocaleUtils.isAvailableLocale(locale); + return locale; } } diff --git a/core/src/main/java/com/opensymphony/xwork2/LocaleProvider.java b/core/src/main/java/com/opensymphony/xwork2/LocaleProvider.java index 67972af341..00a41a25b3 100644 --- a/core/src/main/java/com/opensymphony/xwork2/LocaleProvider.java +++ b/core/src/main/java/com/opensymphony/xwork2/LocaleProvider.java @@ -18,6 +18,9 @@ */ package com.opensymphony.xwork2; +import org.apache.commons.lang3.LocaleUtils; +import org.apache.commons.lang3.StringUtils; + import java.util.Locale; @@ -58,4 +61,17 @@ public interface LocaleProvider { */ boolean isValidLocale(Locale locale); + /** + * Tries to convert provided locale string into {@link Locale} or returns null + * @param localeStr a String representing locale, e.g.: en_EN + * @return instance of {@link Locale} or null + * @since Struts 6.5.0 + */ + default Locale toLocale(String localeStr) { + try { + return LocaleUtils.toLocale(StringUtils.trimToNull(localeStr)); + } catch (IllegalArgumentException e) { + return null; + } + } } diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java b/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java index 5c7f2c136a..bc8c88875c 100644 --- a/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java +++ b/core/src/main/java/com/opensymphony/xwork2/validator/DelegatingValidatorContext.java @@ -122,10 +122,15 @@ public boolean isValidLocale(Locale locale) { return localeProvider.isValidLocale(locale); } + @Override + public Locale toLocale(String localeStr) { + return localeProvider.toLocale(localeStr); + } + public boolean hasKey(String key) { return textProvider.hasKey(key); } - + public String getText(String aTextName) { return textProvider.getText(aTextName); } @@ -280,6 +285,11 @@ public boolean isValidLocaleString(String localeStr) { public boolean isValidLocale(Locale locale) { return getLocaleProvider().isValidLocale(locale); } + + @Override + public Locale toLocale(String localeStr) { + return getLocaleProvider().toLocale(localeStr); + } } /** diff --git a/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java index e0f978f6ea..6bce042446 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/I18nInterceptor.java @@ -24,7 +24,6 @@ import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.util.TextParseUtil; -import org.apache.commons.lang3.LocaleUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; @@ -85,7 +84,7 @@ public void setRequestCookieParameterName(String requestCookieParameterName) { } public void setLocaleStorage(String storageName) { - if (storageName == null || "".equals(storageName)) { + if (storageName == null || storageName.isEmpty()) { this.storage = Storage.ACCEPT_LANGUAGE; } else { try { @@ -169,27 +168,21 @@ protected LocaleHandler getLocaleHandler(ActionInvocation invocation) { } /** - * Creates a Locale object from the request param, which might - * be already a Local or a String + * Creates a Locale object from the request param * * @param requestedLocale the parameter from the request - * @return the Locale + * @return instance of {@link Locale} or null */ - protected Locale getLocaleFromParam(Object requestedLocale) { + protected Locale getLocaleFromParam(String requestedLocale) { LocaleProvider localeProvider = localeProviderFactory.createLocaleProvider(); Locale locale = null; if (requestedLocale != null) { - if (requestedLocale instanceof Locale) { - locale = (Locale) requestedLocale; - } else { - String localeStr = requestedLocale.toString(); - if (localeProvider.isValidLocaleString(localeStr)) { - locale = LocaleUtils.toLocale(localeStr); - } else { - locale = localeProvider.getLocale(); - } + locale = localeProvider.toLocale(requestedLocale); + if (locale == null) { + locale = localeProvider.getLocale(); } + if (locale != null) { LOG.debug("Found locale: {}", locale); } @@ -285,7 +278,7 @@ protected AcceptLanguageLocaleHandler(ActionInvocation invocation) { @Override @SuppressWarnings("rawtypes") public Locale find() { - if (supportedLocale.size() > 0) { + if (!supportedLocale.isEmpty()) { Enumeration locales = actionInvocation.getInvocationContext().getServletRequest().getLocales(); while (locales.hasMoreElements()) { Locale locale = (Locale) locales.nextElement(); diff --git a/core/src/test/java/com/opensymphony/xwork2/DefaultLocaleProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/DefaultLocaleProviderTest.java new file mode 100644 index 0000000000..bb5178abd9 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/DefaultLocaleProviderTest.java @@ -0,0 +1,174 @@ +/* + * 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; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Locale; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class DefaultLocaleProviderTest { + + private DefaultLocaleProvider provider; + + @Before + public void setUp() throws Exception { + provider = new DefaultLocaleProvider(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + ActionContext.of().bind(); + } + + @AfterClass + public static void afterClass() throws Exception { + ActionContext.clear(); + } + + @Test + public void getLocale() { + // given + ActionContext.getContext().withLocale(Locale.ITALY); + + // when + Locale actual = provider.getLocale(); + + // then + assertEquals(Locale.ITALY, actual); + } + + @Test + public void getLocaleNull() { + // given + ActionContext backup = ActionContext.getContext(); + ActionContext.clear(); + + // when + Locale actual = provider.getLocale(); + + // then + assertNull(actual); + ActionContext.bind(backup); + } + + @Test + public void toLocale() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + Locale actual = provider.toLocale("it"); + + // then + assertEquals(Locale.ITALIAN, actual); + } + + @Test + public void toLocaleFull() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + Locale actual = provider.toLocale("it_IT"); + + // then + assertEquals(Locale.ITALY, actual); + } + + @Test + public void toLocaleTrimEndOfLine() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + Locale actual = provider.toLocale("it_IT\n"); + + // then + assertEquals(Locale.ITALY, actual); + } + + @Test + public void toLocaleTrimEmptySpace() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + Locale actual = provider.toLocale(" it_IT "); + + // then + assertEquals(Locale.ITALY, actual); + } + + @Test + public void isValidLocaleNull() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + boolean actual = provider.isValidLocale(null); + + // then + assertFalse(actual); + } + + @Test + public void isValidLocale() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + boolean actual = provider.isValidLocale(Locale.ITALIAN); + + // then + assertTrue(actual); + } + + @Test + public void isValidLocaleString() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + boolean actual = provider.isValidLocaleString("it"); + + // then + assertTrue(actual); + } + + @Test + public void isValidLocaleStringNot() { + // given + ActionContext.getContext().withLocale(Locale.GERMAN); + + // when + boolean actual = provider.isValidLocaleString("italy"); + + // then + assertFalse(actual); + } + +} diff --git a/core/src/test/java/com/opensymphony/xwork2/LocaleProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/LocaleProviderTest.java new file mode 100644 index 0000000000..03e05f5c23 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/LocaleProviderTest.java @@ -0,0 +1,82 @@ +/* + * 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; + +import org.junit.Test; + +import java.util.Locale; + +import static org.junit.Assert.*; + +public class LocaleProviderTest { + + @Test + public void toLocale() { + // given + DummyLocale locale = new DummyLocale(); + + // when + Locale actual = locale.toLocale("de"); + + // then + assertEquals(Locale.GERMAN, actual); + } + + @Test + public void toLocaleTrim() { + // given + DummyLocale locale = new DummyLocale(); + + // when + Locale actual = locale.toLocale(" de_DE "); + + // then + assertEquals(Locale.GERMANY, actual); + } + + @Test + public void toLocaleNull() { + // given + DummyLocale locale = new DummyLocale(); + + // when + Locale actual = locale.toLocale("germany"); + + // then + assertNull(actual); + } + +} + +class DummyLocale implements LocaleProvider { + @Override + public Locale getLocale() { + return null; + } + + @Override + public boolean isValidLocaleString(String localeStr) { + return false; + } + + @Override + public boolean isValidLocale(Locale locale) { + return false; + } +} diff --git a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java index a8fd8420f7..604d61be69 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/I18nInterceptorTest.java @@ -147,6 +147,26 @@ public void testNotExistingLocale() throws Exception { assertEquals(Locale.getDefault(), session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should create a locale object } + public void testTrimableLocaleString1() throws Exception { + prepare(I18nInterceptor.DEFAULT_PARAMETER, "de\n"); + + interceptor.intercept(mai); + + assertFalse(mai.getInvocationContext().getParameters().get(I18nInterceptor.DEFAULT_PARAMETER).isDefined()); // should have been removed + assertNotNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should be stored here + assertEquals(Locale.GERMAN, session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should create a locale object + } + + public void testTrimableLocaleString2() throws Exception { + prepare(I18nInterceptor.DEFAULT_PARAMETER, "de "); + + interceptor.intercept(mai); + + assertFalse(mai.getInvocationContext().getParameters().get(I18nInterceptor.DEFAULT_PARAMETER).isDefined()); // should have been removed + assertNotNull(session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should be stored here + assertEquals(Locale.GERMAN, session.get(I18nInterceptor.DEFAULT_SESSION_ATTRIBUTE)); // should create a locale object + } + public void testWithVariant() throws Exception { prepare(I18nInterceptor.DEFAULT_PARAMETER, "ja_JP_JP"); interceptor.intercept(mai);