- Revision
- 838
- Author
- mauro
- Date
- 2008-09-15 11:04:36 -0500 (Mon, 15 Sep 2008)
Log Message
WAFFLE-97: Refactored bundle-based message resources to support multiple bundles encoded as CSV in the URI.
Modified Paths
- trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/DefaultMessageResources.java
- trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/DefaultMessageResourcesConfiguration.java
- trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/MessageResourcesConfiguration.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/AbstractValueConverterTest.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/i18n/DefaultMessageResourcesTest.java
Added Paths
- trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/ResourceBundleMerger.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/i18n/ResourceBundleMergerTest.java
- trunk/waffle-core/src/test/resources/AnotherBundle.properties
- trunk/waffle-core/src/test/resources/Bundle.properties
- trunk/waffle-core/src/test/resources/Bundle_en_GB.properties
- trunk/waffle-core/src/test/resources/Bundle_it_IT.properties
Removed Paths
Diff
Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/DefaultMessageResources.java (837 => 838)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/DefaultMessageResources.java 2008-09-15 15:24:15 UTC (rev 837) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/DefaultMessageResources.java 2008-09-15 16:04:36 UTC (rev 838) @@ -4,20 +4,26 @@ package org.codehaus.waffle.i18n; import static java.text.MessageFormat.format; +import static java.util.Arrays.asList; import static java.util.ResourceBundle.getBundle; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; /** - * Default [EMAIL PROTECTED] ResourceBundle}-based implementation of MessageResorces. + * Default [EMAIL PROTECTED] ResourceBundle}-based implementation of MessageResorces. Supports a URI defining multiple resource + * bundle names as CSV-list. The decoding of the bundle names from the URI (including the separator string) is + * overrideable by subclassing the method [EMAIL PROTECTED] DefaultMessageResources#bundleNames(String)}. * * @author Michael Ward * @author Mauro Talevi */ public class DefaultMessageResources implements MessageResources { private static final ResourceBundle EMPTY_BUNDLE = new EmptyResourceBundle(); + private final ResourceBundleMerger merger = new ResourceBundleMerger(); private String uri; private Locale locale; private ResourceBundle bundle; @@ -27,13 +33,18 @@ } public DefaultMessageResources(MessageResourcesConfiguration configuration) { - this.uri = configuration.getDefaultURI(); - this.locale = configuration.getDefaultLocale(); + this.uri = configuration.getURI(); + this.locale = configuration.getLocale(); lookupBundle(); } - private void lookupBundle() { - bundle = lookupBunde(uri); + private void lookupBundle() { + List<String> bundleNames = bundleNames(uri); + List<ResourceBundle> bundles = new ArrayList<ResourceBundle>(); + for (String bundleName : bundleNames) { + bundles.add(lookupBunde(bundleName)); + } + bundle = merger.merge(bundles); } private ResourceBundle lookupBunde(String bundleName) { @@ -43,6 +54,11 @@ return EMPTY_BUNDLE; } } + + protected List<String> bundleNames(String uri) { + return asList(uri.split(",")); + } + public String getURI() { return uri; } @@ -61,7 +77,7 @@ lookupBundle(); } - public String getMessage(String key, Object... arguments) { + public String getMessage(String key, Object... arguments) { return format(bundle.getString(key), arguments); }
Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/DefaultMessageResourcesConfiguration.java (837 => 838)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/DefaultMessageResourcesConfiguration.java 2008-09-15 15:24:15 UTC (rev 837) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/DefaultMessageResourcesConfiguration.java 2008-09-15 16:04:36 UTC (rev 838) @@ -6,37 +6,43 @@ import java.util.Locale; /** - * Default MessageResourcesConfiguration which simply allows the injection of default locale and resource. + * Default MessageResourcesConfiguration which allows the injection of locale and resource. * * @author Mauro Talevi */ public class DefaultMessageResourcesConfiguration implements MessageResourcesConfiguration { - private final String defaultURI; - private final Locale defaultLocale; + private static final String DEFAULT_URI = "ApplicationResources"; + private static final Locale DEFAULT_LOCALE = Locale.getDefault(); + private final String uri; + private final Locale locale; public DefaultMessageResourcesConfiguration() { - this("ApplicationResources", Locale.getDefault()); + this(DEFAULT_URI, DEFAULT_LOCALE); } + + public DefaultMessageResourcesConfiguration(String defaultURI) { + this(defaultURI, DEFAULT_LOCALE); + } - public DefaultMessageResourcesConfiguration(String defaultURI, Locale defaultLocale) { - this.defaultLocale = defaultLocale; - this.defaultURI = defaultURI; + public DefaultMessageResourcesConfiguration(String uri, Locale locale) { + this.uri = uri; + this.locale = locale; } /** - * @deprecated Use DefaultMessageResourcesConfiguration(String defaultURI, Locale defaultLocale) + * @deprecated Use DefaultMessageResourcesConfiguration(String uri, Locale locale) */ - public DefaultMessageResourcesConfiguration(Locale defaultLocale, String defaultURI) { - this(defaultURI, defaultLocale); + public DefaultMessageResourcesConfiguration(Locale locale, String uri) { + this(uri, locale); } - public Locale getDefaultLocale() { - return defaultLocale; + public Locale getLocale() { + return locale; } - public String getDefaultURI() { - return defaultURI; + public String getURI() { + return uri; } }
Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/MessageResourcesConfiguration.java (837 => 838)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/MessageResourcesConfiguration.java 2008-09-15 15:24:15 UTC (rev 837) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/MessageResourcesConfiguration.java 2008-09-15 16:04:36 UTC (rev 838) @@ -13,7 +13,7 @@ */ public interface MessageResourcesConfiguration { - String getDefaultURI(); + String getURI(); - Locale getDefaultLocale(); + Locale getLocale(); }
Added: trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/ResourceBundleMerger.java (0 => 838)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/ResourceBundleMerger.java (rev 0) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/i18n/ResourceBundleMerger.java 2008-09-15 16:04:36 UTC (rev 838) @@ -0,0 +1,52 @@ +/* + * Copyright (c) terms as published in http://waffle.codehaus.org/license.html + */ +package org.codehaus.waffle.i18n; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + +/** + * Merges resource bundles into a [EMAIL PROTECTED] PropertyResourceBundle}. + * The merged resource bundle will contain all distinct keys from all bundles, + * and any overlapping keys from a given bundle will the overwritten by the next. + * It thus employs a last-key-wins strategy. + * + * @author Mauro Talevi + */ +public class ResourceBundleMerger { + + private static final ResourceBundle EMPTY_BUNDLE = new EmptyResourceBundle(); + + private final ByteArrayOutputStream stream; + + public ResourceBundleMerger() { + this(new ByteArrayOutputStream()); + } + + public ResourceBundleMerger(ByteArrayOutputStream stream) { + this.stream = stream; + } + + public ResourceBundle merge(List<ResourceBundle> bundles) { + Properties merged = new Properties(); + for (ResourceBundle bundle : bundles) { + for (Enumeration<String> e = bundle.getKeys(); e.hasMoreElements();) { + String key = e.nextElement(); + merged.setProperty(key, bundle.getString(key)); + } + } + try { + merged.store(stream, "Merged bundles"); + return new PropertyResourceBundle(new ByteArrayInputStream(stream.toByteArray())); + } catch (Exception e) { + return EMPTY_BUNDLE; + } + } + +}
Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/AbstractValueConverterTest.java (837 => 838)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/AbstractValueConverterTest.java 2008-09-15 15:24:15 UTC (rev 837) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/AbstractValueConverterTest.java 2008-09-15 16:04:36 UTC (rev 838) @@ -25,12 +25,12 @@ protected MessageResourcesConfiguration configuration = new MessageResourcesConfiguration() { - public Locale getDefaultLocale() { + public Locale getLocale() { return Locale.UK; } - public String getDefaultURI() { - return "FakeResourceBundle"; + public String getURI() { + return "Bundle"; } };
Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/i18n/DefaultMessageResourcesTest.java (837 => 838)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/i18n/DefaultMessageResourcesTest.java 2008-09-15 15:24:15 UTC (rev 837) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/i18n/DefaultMessageResourcesTest.java 2008-09-15 16:04:36 UTC (rev 838) @@ -1,28 +1,19 @@ package org.codehaus.waffle.i18n; +import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; +import java.util.List; import java.util.Locale; import org.junit.Test; /** - * * @author Michael Ward * @author Mauro Talevi */ -public class DefaultMessageResourcesTest { +public class DefaultMessageResourcesTest { - private MessageResourcesConfiguration configuration = new MessageResourcesConfiguration() { - public String getDefaultURI() { - return "FakeResourceBundle"; - } - - public Locale getDefaultLocale() { - return Locale.US; - } - }; - @Test public void canGetDefaultLocaleIfNoLocaleSpecified() { MessageResources messageResources = new DefaultMessageResources(); @@ -30,21 +21,48 @@ } @Test - public void canGetMessage() { - MessageResources messageResources = new DefaultMessageResources(configuration); + public void canGetMessageForDifferentURIsAndLocales() { + MessageResources messageResources = new DefaultMessageResources(new DefaultMessageResourcesConfiguration("Bundle")); assertEquals("thoughtworks", messageResources.getMessage("company")); assertEquals("hello mars", messageResources.getMessage("foo.bar", "mars")); - messageResources.useLocale(Locale.UK); assertEquals("thoughtworks", messageResources.getMessage("company")); assertEquals("cheerio mars", messageResources.getMessage("foo.bar", "mars")); + messageResources.useLocale(Locale.ITALY); + assertEquals("thoughtworks", messageResources.getMessage("company")); + assertEquals("ciao mars", messageResources.getMessage("foo.bar", "mars")); + messageResources = new DefaultMessageResources(new DefaultMessageResourcesConfiguration("AnotherBundle")); + assertEquals("howdy mars", messageResources.getMessage("foo.bar", "mars")); + } @Test + public void canGetMessageUsingMultipleBundles() { + assertMessagesUsingMultipleBundles(new DefaultMessageResources(new DefaultMessageResourcesConfiguration( + "Bundle,AnotherBundle"))); + // can ignore trailing spaces in bundle names + assertMessagesUsingMultipleBundles(new DefaultMessageResources(new DefaultMessageResourcesConfiguration( + " Bundle, AnotherBundle "))); + // can configure other separators for bundle names + assertMessagesUsingMultipleBundles(new DefaultMessageResources(new DefaultMessageResourcesConfiguration( + "Bundle;AnotherBundle")){ + @Override + protected List<String> bundleNames(String uri) { + return asList(uri.split(";")); + } + }); + } + + private void assertMessagesUsingMultipleBundles(MessageResources messageResources) { + assertEquals("thoughtworks", messageResources.getMessage("company")); + assertEquals("howdy mars", messageResources.getMessage("foo.bar", "mars")); + assertEquals("waffle", messageResources.getMessage("project.name", "mars")); + } + + @Test public void canGetMessageWithDefault() { - MessageResources messageResources = new DefaultMessageResources(configuration); - - assertEquals("thoughtworks", messageResources.getMessageWithDefault("company", "BEARS")); + MessageResources messageResources = new DefaultMessageResources(new DefaultMessageResourcesConfiguration("Bundle")); + assertEquals("thoughtworks", messageResources.getMessageWithDefault("company", "should-be-ignored")); assertEquals("hello mars", messageResources.getMessageWithDefault("foo.bar", "should-be-ignored", "mars")); assertEquals("defaultValue", messageResources.getMessageWithDefault("should.not.exist", "defaultValue")); }
Added: trunk/waffle-core/src/test/java/org/codehaus/waffle/i18n/ResourceBundleMergerTest.java (0 => 838)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/i18n/ResourceBundleMergerTest.java (rev 0) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/i18n/ResourceBundleMergerTest.java 2008-09-15 16:04:36 UTC (rev 838) @@ -0,0 +1,59 @@ +package org.codehaus.waffle.i18n; + +import static java.util.Arrays.asList; +import static java.util.ResourceBundle.getBundle; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; + +import org.junit.Test; + +/** + * @author Mauro Talevi + */ +public class ResourceBundleMergerTest { + + private static final Locale LOCALE = Locale.getDefault(); + + @Test + public void canMergeMultipleBundles() { + ResourceBundleMerger merger = new ResourceBundleMerger(); + ResourceBundle bundle = getBundle("Bundle", LOCALE); + ResourceBundle anotherBundle = getBundle("AnotherBundle", LOCALE); + List<ResourceBundle> bundles = asList(bundle, anotherBundle); + ResourceBundle merged = merger.merge(bundles); + assertEquals(bundle.getString("company"), merged.getString("company")); + assertEquals(anotherBundle.getString("foo.bar"), merged.getString("foo.bar")); + } + + @Test + public void canAllowBundleToOverridePreviousBundles() { + ResourceBundleMerger merger = new ResourceBundleMerger(); + ResourceBundle bundle = getBundle("Bundle", LOCALE); + ResourceBundle anotherBundle = getBundle("AnotherBundle", LOCALE); + List<ResourceBundle> bundles = asList(bundle, anotherBundle); + ResourceBundle merged = merger.merge(bundles); + assertEquals(anotherBundle.getString("project.name"), merged.getString("project.name")); + } + + @Test + public void canMergeMultipleBundlesToEmptyBundleIfOutputStreamInvalid() { + ResourceBundleMerger merger = new ResourceBundleMerger(new ByteArrayOutputStream() { + @Override + public synchronized byte[] toByteArray() { + throw new RuntimeException("mock"); + } + + }); + ResourceBundle bundle = getBundle("Bundle", LOCALE); + ResourceBundle anotherBundle = getBundle("AnotherBundle", LOCALE); + List<ResourceBundle> bundles = asList(bundle, anotherBundle); + ResourceBundle merged = merger.merge(bundles); + assertTrue(merged instanceof EmptyResourceBundle); + } + +}
Added: trunk/waffle-core/src/test/resources/AnotherBundle.properties (0 => 838)
--- trunk/waffle-core/src/test/resources/AnotherBundle.properties (rev 0) +++ trunk/waffle-core/src/test/resources/AnotherBundle.properties 2008-09-15 16:04:36 UTC (rev 838) @@ -0,0 +1,4 @@ +foo.bar=howdy {0} +project.name=waffle + +
Copied: trunk/waffle-core/src/test/resources/Bundle.properties (from rev 834, trunk/waffle-core/src/test/resources/FakeResourceBundle.properties) (0 => 838)
--- trunk/waffle-core/src/test/resources/Bundle.properties (rev 0) +++ trunk/waffle-core/src/test/resources/Bundle.properties 2008-09-15 16:04:36 UTC (rev 838) @@ -0,0 +1,13 @@ +company=thoughtworks +foo.bar=hello {0} +project.name=waffle +date.format=dd-MM-yyyy +date.format.day=dd +date.format.day.name=day.* +date.format.time=HH:mm:ss +date.format.time.name=time.* +bind.error.date=Date {1} has invalid format {2} for field {0} +bind.error.date.missing=Date field {0} is missing +bind.error.list=No list values for field {0} +bind.error.map=No map values for field {0} +
Property changes: trunk/waffle-core/src/test/resources/Bundle.properties
Name: svn:mergeinfo
+
Copied: trunk/waffle-core/src/test/resources/Bundle_en_GB.properties (from rev 834, trunk/waffle-core/src/test/resources/FakeResourceBundle_en_GB.properties) (0 => 838)
--- trunk/waffle-core/src/test/resources/Bundle_en_GB.properties (rev 0) +++ trunk/waffle-core/src/test/resources/Bundle_en_GB.properties 2008-09-15 16:04:36 UTC (rev 838) @@ -0,0 +1 @@ +foo.bar=cheerio {0} \ No newline at end of file
Property changes: trunk/waffle-core/src/test/resources/Bundle_en_GB.properties
Name: svn:mergeinfo
+
Added: trunk/waffle-core/src/test/resources/Bundle_it_IT.properties (0 => 838)
--- trunk/waffle-core/src/test/resources/Bundle_it_IT.properties (rev 0) +++ trunk/waffle-core/src/test/resources/Bundle_it_IT.properties 2008-09-15 16:04:36 UTC (rev 838) @@ -0,0 +1 @@ +foo.bar=ciao {0} \ No newline at end of file Property changes on: trunk/waffle-core/src/test/resources/Bundle_it_IT.properties ___________________________________________________________________ Name: svn:mergeinfo +
Deleted: trunk/waffle-core/src/test/resources/FakeResourceBundle.properties (837 => 838)
--- trunk/waffle-core/src/test/resources/FakeResourceBundle.properties 2008-09-15 15:24:15 UTC (rev 837) +++ trunk/waffle-core/src/test/resources/FakeResourceBundle.properties 2008-09-15 16:04:36 UTC (rev 838) @@ -1,12 +0,0 @@ -company=thoughtworks -foo.bar=hello {0} -date.format=dd-MM-yyyy -date.format.day=dd -date.format.day.name=day.* -date.format.time=HH:mm:ss -date.format.time.name=time.* -bind.error.date=Date {1} has invalid format {2} for field {0} -bind.error.date.missing=Date field {0} is missing -bind.error.list=No list values for field {0} -bind.error.map=No map values for field {0} -
Deleted: trunk/waffle-core/src/test/resources/FakeResourceBundle_en_GB.properties (837 => 838)
--- trunk/waffle-core/src/test/resources/FakeResourceBundle_en_GB.properties 2008-09-15 15:24:15 UTC (rev 837) +++ trunk/waffle-core/src/test/resources/FakeResourceBundle_en_GB.properties 2008-09-15 16:04:36 UTC (rev 838) @@ -1 +0,0 @@ -foo.bar=cheerio {0} \ No newline at end of file
To unsubscribe from this list please visit:
