Author: mck Date: Sun Mar 30 13:57:47 2014 New Revision: 1583141 URL: http://svn.apache.org/r1583141 Log: TILES-571 – Please make the significance of underscore (_) in tiles definition filename more obvious SPR-11491 – Doc: Tiles 3 and underscores in definition names
<quote> … it looks like there is an issue with PostfixedApplicationResource. This issue comes from SpringWildcardServletTilesApplicationContext#getResources(String) -> URLApplicationResource(String, URL) constructor -> super PostfixedApplicationResource(String localePath) constructor. When there is an underscore, the string after the last underscore is identified as the locale. But clearly it's not a locale and one should be able to load /WEB-INF/base_tiles.xml. </quote> Along with improving the documentation the following code changes have been made • if the resource name contains "_" and after it there is no supported locale language code then the whole resource name is treated as a non-localized path. This situation prints a warning like > No supported matching language for locale "zz". Using /my/path_zz.html as a non-localized resource path. • if the resource name contains "_" and after it there is a supported locale language code but the locale constructed isn't valid/supported then the closest supported locale is used. This situation prints a warning like > For resource /my/path_en_ZZ.html the closest supported matching locale to "en_ZZ" is "en". Using /my/path.html as resource path. Modified: tiles/framework/branches/TILES_3_0_X/src/site/apt/config-reference.apt tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/main/java/org/apache/tiles/request/locale/PostfixedApplicationResource.java tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/PostfixedApplicationResourceTest.java tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/URLApplicationResourceTest.java Modified: tiles/framework/branches/TILES_3_0_X/src/site/apt/config-reference.apt URL: http://svn.apache.org/viewvc/tiles/framework/branches/TILES_3_0_X/src/site/apt/config-reference.apt?rev=1583141&r1=1583140&r2=1583141&view=diff ============================================================================== --- tiles/framework/branches/TILES_3_0_X/src/site/apt/config-reference.apt (original) +++ tiles/framework/branches/TILES_3_0_X/src/site/apt/config-reference.apt Sun Mar 30 13:57:47 2014 @@ -143,9 +143,14 @@ Configuring Tiles internals The reason to use a custom Tiles application context could be: - * supporting a platform not supported yet; + * supporting a platform not supported yet; - * providing custom behaviour, such as loading resources in a different manner. + * providing custom behaviour, such as loading resources in a different manner. + + + When loading resources (by {{{/tiles-request/apidocs/org/apache/tiles/request/locale/PostfixedApplicationResource.html}default}}) + an underscore in the name of a file is used to indicate locale information. + See the documentation for {{{/framework/tutorial/advanced/l10n.html}localization}}. * Custom {TilesContainerFactory} @@ -160,41 +165,41 @@ Configuring Tiles internals {{{./apidocs/org/apache/tiles/factory/BasicTilesContainerFactory.html}Javadoc documentation of BasicTilesContainerFactory}} documents all the methods that can be overridden to use your own configuration. - + ** Changing the path for the Tiles Definitions file The <<<BasicTilesContainerFactory>>> loads the "/WEB-INF/tiles.xml" file; the <<<CompleteAutoloadTilesContainerFactory>>> loads all the files named "tiles*.xml" under /WEB-INF and under every META-INF in any part of the classpath. - + If this behaviour doesn't suits you, you can override the method <<<getSources>>> and retrieve whatever resource you prefer. Just specify the path for the default locale; Tiles will extrapolate and load the localized files as needed. * Custom components These components can be used by overriding the appropriate <<<create>>> method in a custom TilesContainerFactory. - + ** Custom {LocaleResolver} - + The default implementation is {{{./apidocs/org/apache/tiles/locale/impl/DefaultLocaleResolver.html}DefaultLocaleResolver}}. ** Custom {DefinitionDAO} The default implementation is {{{./apidocs/org/apache/tiles/definition/dao/ResolvingLocaleUrlDefinitionDAO.html}ResolvingLocaleUrlDefinitionDAO}}. - + ** Custom {AttributeEvaluatorFactory} The default implementation is {{{./apidocs/org/apache/tiles/evaluator/BasicAttributeEvaluatorFactory.html}BasicAttributeEvaluatorFactory}}. - + It can be used with a number of AttributeEvaluators like: - - * the {{{./apidocs/org/apache/tiles/evaluator/impl/DirectAttributeEvaluator.html}DirectAttributeEvaluator}}, - * the {{{./apidocs/org/apache/tiles/el/ELAttributeEvaluator.html}ELAttributeEvaluator}}, + * the {{{./apidocs/org/apache/tiles/evaluator/impl/DirectAttributeEvaluator.html}DirectAttributeEvaluator}}, + + * the {{{./apidocs/org/apache/tiles/el/ELAttributeEvaluator.html}ELAttributeEvaluator}}, + + * the {{{./apidocs/org/apache/tiles/mvel/MVELAttributeEvaluator.html}MVELAttributeEvaluator}}, - * the {{{./apidocs/org/apache/tiles/mvel/MVELAttributeEvaluator.html}MVELAttributeEvaluator}}, + * the {{{./apidocs/org/apache/tiles/ognl/OGNLAttributeEvaluator.html}OGNLAttributeEvaluator}}. - * the {{{./apidocs/org/apache/tiles/ognl/OGNLAttributeEvaluator.html}OGNLAttributeEvaluator}}. - Please see {{{./xref/org/apache/tiles/extras/complete/CompleteAutoloadTilesContainerFactory.html}CompleteAutoloadTilesContainerFactory}} for an example of how to configure those. @@ -214,20 +219,20 @@ Configuring Tiles internals The default implementation is {{{./apidocs/org/apache/tiles/definition/pattern/BasicPatternDefinitionResolver.html}BasicPatternDefinitionResolver}}, that implements the <wildcard> syntax. - - <<<CompleteAutoloadTilesContainerFactory>>> defines a <<<PrefixedPatternDefinitionResolver>>> to enable the use of + + <<<CompleteAutoloadTilesContainerFactory>>> defines a <<<PrefixedPatternDefinitionResolver>>> to enable the use of both the <wildcard> syntax and the <regexp> syntax, with appropriate prefixes. * Registering {Renderers} Custom {{{/tiles-request/apidocs/org/apache/tiles/request/render/Renderer.html}Renderers}} can be registered by overriding the methods <<<registerAttributeRenderers>>> and <<<createDefaultAttributeRenderer>>>. - + <<<BasicTilesContainerFactory>>> registers 3 renderers: <<<string>>>, <<<template>>>, and <<<definition>>>, in order to render plain strings, JSPs and tiles definitions. - - <<<CompleteAutoloadTilesContainerFactory>>> registers 5 renderers: <<<string>>>, <<<template>>>, <<<freemarker>>>, - <<<velocity>>> and <<<definition>>>, in order to render plain strings, JSPs, freemarker and velocity templates + + <<<CompleteAutoloadTilesContainerFactory>>> registers 5 renderers: <<<string>>>, <<<template>>>, <<<freemarker>>>, + <<<velocity>>> and <<<definition>>>, in order to render plain strings, JSPs, freemarker and velocity templates and tiles definitions. * Changing the {definition files} @@ -245,6 +250,6 @@ protected List<ApplicationResource> getS ----------- Please note that when using <<<CompleteAutoloadTilesContainerFactory>>>, - the <<<ApplicationContext>>> loads the resources via spring, and supports + the <<<ApplicationContext>>> loads the resources via spring, and supports {{{http://static.springsource.org/spring/docs/2.5.x/reference/resources.html#resources-resource-strings}the spring syntax}} for locating resources. Modified: tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/main/java/org/apache/tiles/request/locale/PostfixedApplicationResource.java URL: http://svn.apache.org/viewvc/tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/main/java/org/apache/tiles/request/locale/PostfixedApplicationResource.java?rev=1583141&r1=1583140&r2=1583141&view=diff ============================================================================== --- tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/main/java/org/apache/tiles/request/locale/PostfixedApplicationResource.java (original) +++ tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/main/java/org/apache/tiles/request/locale/PostfixedApplicationResource.java Sun Mar 30 13:57:47 2014 @@ -21,14 +21,18 @@ package org.apache.tiles.request.locale; +import java.util.Arrays; +import java.util.IllformedLocaleException; +import java.util.List; import java.util.Locale; - import org.apache.tiles.request.ApplicationResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An ApplicationResource whose localization is managed by postfixing the file name. * The various localizations are file sitting next to each other, with the locale identified in the postfix. - * + * * For instance: * <pre> * /WEB-INF/tiles.xml @@ -36,13 +40,15 @@ import org.apache.tiles.request.Applicat * /WEB-INF/tiles_it.xml * /WEB-INF/tiles_it_IT.xml * </pre> - * + * * Two PostfixedApplicationResources are equals if they share the same localized path and the same class. - * + * * @version $Rev$ $Date$ */ public abstract class PostfixedApplicationResource implements ApplicationResource { + private static final Logger LOG = LoggerFactory.getLogger(PostfixedApplicationResource.class); + /** The path without its suffix and its locale postfix. */ private String pathPrefix; /** The suffix. */ @@ -69,18 +75,18 @@ public abstract class PostfixedApplicati } else { pathPrefix = localePath.substring(0, prefixIndex); String localeString = localePath.substring(prefixIndex + 1, suffixIndex); - int countryIndex = localeString.indexOf('_'); - if (countryIndex < 0) { - locale = new Locale(localeString); - } else { - int variantIndex = localeString.indexOf('_', countryIndex + 1); - if (variantIndex < 0) { - locale = new Locale(localeString.substring(0, countryIndex), - localeString.substring(countryIndex + 1)); - } else { - locale = new Locale(localeString.substring(0, countryIndex), localeString.substring( - countryIndex + 1, variantIndex), localeString.substring(variantIndex + 1)); - } + Locale found = localeFrom(localeString); + locale = validateLocale(found); + if (Locale.ROOT.equals(locale)) { + pathPrefix = suffixIndex < 0 ? localePath : localePath.substring(0, suffixIndex); + + LOG.warn("No supported matching language for locale \"" + localeString + "\". Using " + + getPath() + " as a non-localized resource path. see TILES-571"); + + } else if (!localeString.equalsIgnoreCase(getPostfix(locale).substring(1))) { + LOG.warn("For resource " + localePath + + " the closest supported matching locale to \"" + localeString + "\" is \"" + locale + + "\". Using " + getPath() + " as resource path. see TILES-571"); } } } @@ -193,4 +199,45 @@ public abstract class PostfixedApplicati return false; return true; } + + private static Locale localeFrom(String localeString) { + Locale.Builder builder = new Locale.Builder(); + try { + int countryIndex = localeString.indexOf('_'); + if (countryIndex < 0) { + builder.setLanguage(localeString); + } else { + int variantIndex = localeString.indexOf('_', countryIndex + 1); + builder.setLanguage(localeString.substring(0, countryIndex)); + if (variantIndex < 0) { + builder.setRegion(localeString.substring(countryIndex + 1)); + } else { + builder.setRegion(localeString.substring(countryIndex + 1, variantIndex)); + builder.setVariant(localeString.substring(variantIndex + 1)); + } + } + } catch (IllformedLocaleException ex) { + LOG.debug(localeString + " is an ill-formed locale", ex); + } + return builder.build(); + } + + private static Locale validateLocale(Locale locale) { + List<Locale> availableLocales = Arrays.asList(Locale.getAvailableLocales()); + + Locale withoutVariant = locale.getVariant().isEmpty() + ? locale + : new Locale(locale.getLanguage(), locale.getCountry()); + + Locale result = locale; + if (!availableLocales.contains(withoutVariant)) { + if (!result.getCountry().isEmpty()) { + result = new Locale(result.getLanguage()); + } + if (!availableLocales.contains(result)) { + result = Locale.ROOT; + } + } + return result; + } } Modified: tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/PostfixedApplicationResourceTest.java URL: http://svn.apache.org/viewvc/tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/PostfixedApplicationResourceTest.java?rev=1583141&r1=1583140&r2=1583141&view=diff ============================================================================== --- tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/PostfixedApplicationResourceTest.java (original) +++ tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/PostfixedApplicationResourceTest.java Sun Mar 30 13:57:47 2014 @@ -53,9 +53,9 @@ public class PostfixedApplicationResourc public long getLastModified() throws IOException { return 0; } - + }; - + /** * Test getLocalePath(String path, Locale locale). */ @@ -68,7 +68,7 @@ public class PostfixedApplicationResourc assertEquals("/my/path_it_IT.html", resource.getLocalePath(Locale.ITALY)); assertEquals("/my/path_en_GB_scotland.html", resource.getLocalePath(new Locale("en", "GB", "scotland"))); } - + @Test public void testBuildFromString() { TestApplicationResource resource = new TestApplicationResource("/my/path_en_GB_scotland.html"); @@ -87,8 +87,32 @@ public class PostfixedApplicationResourc assertEquals("/my/path.html", resource.getLocalePath()); assertEquals("/my/path.html", resource.getPath()); assertEquals(Locale.ROOT, resource.getLocale()); + resource = new TestApplicationResource("/my/path_zz.html"); + assertEquals("/my/path_zz.html", resource.getLocalePath()); + assertEquals("/my/path_zz.html", resource.getPath()); + assertEquals(Locale.ROOT, resource.getLocale()); + resource = new TestApplicationResource("/my/path_en_ZZ.html"); + assertEquals("/my/path_en.html", resource.getLocalePath()); + assertEquals("/my/path.html", resource.getPath()); + assertEquals(new Locale("en"), resource.getLocale()); + resource = new TestApplicationResource("/my/path_tiles.html"); + assertEquals("/my/path_tiles.html", resource.getLocalePath()); + assertEquals("/my/path_tiles.html", resource.getPath()); + assertEquals(Locale.ROOT, resource.getLocale()); + resource = new TestApplicationResource("/my/path_longwordthatbreaksISO639.html"); + assertEquals("/my/path_longwordthatbreaksISO639.html", resource.getLocalePath()); + assertEquals("/my/path_longwordthatbreaksISO639.html", resource.getPath()); + assertEquals(Locale.ROOT, resource.getLocale()); + resource = new TestApplicationResource("/my/path_en_tiles.html"); + assertEquals("/my/path_en.html", resource.getLocalePath()); + assertEquals("/my/path.html", resource.getPath()); + assertEquals(new Locale("en"), resource.getLocale()); + resource = new TestApplicationResource("/my/path_en_longwordthatbreaksISO3166.html"); + assertEquals("/my/path_en.html", resource.getLocalePath()); + assertEquals("/my/path.html", resource.getPath()); + assertEquals(new Locale("en"), resource.getLocale()); } - + @Test public void testBuildFromStringAndLocale() { TestApplicationResource resource = new TestApplicationResource("/my/path.html", new Locale("en", "GB", "scotland")); Modified: tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/URLApplicationResourceTest.java URL: http://svn.apache.org/viewvc/tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/URLApplicationResourceTest.java?rev=1583141&r1=1583140&r2=1583141&view=diff ============================================================================== --- tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/URLApplicationResourceTest.java (original) +++ tiles/request/branches/TREQ_1_0_X/tiles-request-api/src/test/java/org/apache/tiles/request/locale/URLApplicationResourceTest.java Sun Mar 30 13:57:47 2014 @@ -97,6 +97,42 @@ public class URLApplicationResourceTest assertEquals("file:/", resource.getURL().toString()); assertEquals("/", resource.getFile().toString()); assertEquals(Locale.ROOT, resource.getLocale()); + resource = new TestApplicationResource("/my/path_zz.html", new URL("file:///")); + assertEquals("/my/path_zz.html", resource.getLocalePath()); + assertEquals("/my/path_zz.html", resource.getPath()); + assertEquals("file:/", resource.getURL().toString()); + assertEquals("/", resource.getFile().toString()); + assertEquals(Locale.ROOT, resource.getLocale()); + resource = new TestApplicationResource("/my/path_en_ZZ.html", new URL("file:///")); + assertEquals("/my/path_en.html", resource.getLocalePath()); + assertEquals("/my/path.html", resource.getPath()); + assertEquals("file:/", resource.getURL().toString()); + assertEquals("/", resource.getFile().toString()); + assertEquals(new Locale("en"), resource.getLocale()); + resource = new TestApplicationResource("/my/path_tiles.html", new URL("file:///")); + assertEquals("/my/path_tiles.html", resource.getLocalePath()); + assertEquals("/my/path_tiles.html", resource.getPath()); + assertEquals("file:/", resource.getURL().toString()); + assertEquals("/", resource.getFile().toString()); + assertEquals(Locale.ROOT, resource.getLocale()); + resource = new TestApplicationResource("/my/path_longwordthatbreaksISO639.html", new URL("file:///")); + assertEquals("/my/path_longwordthatbreaksISO639.html", resource.getLocalePath()); + assertEquals("/my/path_longwordthatbreaksISO639.html", resource.getPath()); + assertEquals("file:/", resource.getURL().toString()); + assertEquals("/", resource.getFile().toString()); + assertEquals(Locale.ROOT, resource.getLocale()); + resource = new TestApplicationResource("/my/path_en_tiles.html", new URL("file:///")); + assertEquals("/my/path_en.html", resource.getLocalePath()); + assertEquals("/my/path.html", resource.getPath()); + assertEquals("file:/", resource.getURL().toString()); + assertEquals("/", resource.getFile().toString()); + assertEquals(new Locale("en"), resource.getLocale()); + resource = new TestApplicationResource("/my/path_en_longwordthatbreaksISO3166.html", new URL("file:///")); + assertEquals("/my/path_en.html", resource.getLocalePath()); + assertEquals("/my/path.html", resource.getPath()); + assertEquals("file:/", resource.getURL().toString()); + assertEquals("/", resource.getFile().toString()); + assertEquals(new Locale("en"), resource.getLocale()); } @Test