Changes to make GWT i18n support "real" default locales. Introduces a .gwt.xml tag <set-property-fallback name="prop" value="chosenvalue"/>, which can be used in property providers as a template substitution for /*-FALLBACK-*/. In i18n, this is used to select a "real" locale to be returned in situations when "default" would otherwise have been used. Also modified AbstractLocalizableImplCreator so that generators running in GwtLocale.DEFAULT will run on this "real" locale, even if <set-property> has been used so that the literal "default" is not being generated. Review by: jat git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5568 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/SelectionProperty.java b/dev/core/src/com/google/gwt/core/ext/SelectionProperty.java index fda8cfc..ba1fca6 100644 --- a/dev/core/src/com/google/gwt/core/ext/SelectionProperty.java +++ b/dev/core/src/com/google/gwt/core/ext/SelectionProperty.java
@@ -19,7 +19,10 @@ import java.util.SortedSet; /** - * A named deferred binding (property, value) pair. + * A named deferred binding (property, value) pair for use in generators. + * + * @see com.google.gwt.core.ext.linker.SelectionProperty A similarly-named + * analog for linkers. */ public interface SelectionProperty { @@ -38,9 +41,15 @@ String getCurrentValue(); /** + * Gets the fallback value for the property + * @return the fallback, or "" + */ + String getFallbackValue(); + + /** * Returns the possible values for the property in sorted order. * * @return a SortedSet of Strings containing the possible property values. */ - SortedSet<String> getPossibleValues(); + SortedSet<String> getPossibleValues(); }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/SelectionProperty.java b/dev/core/src/com/google/gwt/core/ext/linker/SelectionProperty.java index 4c6aaac..e0f17bf 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/SelectionProperty.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/SelectionProperty.java
@@ -22,6 +22,9 @@ * may not have a single value applied across all permutations. * * SelectionProperty implementations must support object identity comparisons. + * + * @see com.google.gwt.core.ext.SelectionProperty A similarly-named interface used + * in generators. */ public interface SelectionProperty { /**
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java index 5f71786..b3ed31f 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java
@@ -28,6 +28,8 @@ * {@link BindingProperty}. */ public class StandardSelectionProperty implements SelectionProperty { + private static final String FALLBACK_TOKEN = "/*-FALLBACK-*/"; + private final String activeValue; private final String name; private final String provider; @@ -40,7 +42,9 @@ activeValue = null; } name = p.getName(); - provider = p.getProvider() == null ? null : p.getProvider().getBody(); + String fallback = p.getFallback(); + provider = p.getProvider() == null ? null : + p.getProvider().getBody().replace(FALLBACK_TOKEN, fallback); values = Collections.unmodifiableSortedSet(new TreeSet<String>( Arrays.asList(p.getDefinedValues()))); }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java b/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java index af8970f..57a03c3 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java +++ b/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java
@@ -27,9 +27,12 @@ */ public class BindingProperty extends Property { + private static final String EMPTY = ""; + private SortedSet<String> allowedValues; private final SortedSet<String> definedValues = new TreeSet<String>(); private PropertyProvider provider; + private String fallback; { /* @@ -41,6 +44,7 @@ public BindingProperty(String name) { super(name); + fallback = EMPTY; } public void addDefinedValue(String newValue) { @@ -61,6 +65,9 @@ return definedValues.toArray(new String[definedValues.size()]); } + public String getFallback() { + return fallback; + } public PropertyProvider getProvider() { return provider; } @@ -95,6 +102,10 @@ allowedValues = temp; } + public void setFallback(String token) { + fallback = token; + } + public void setProvider(PropertyProvider provider) { this.provider = provider; }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java index a3520f5..ac273e5 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java +++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -107,6 +107,10 @@ protected final String __set_property_2_value = null; + protected final String __set_property_fallback_1_name = null; + + protected final String __set_property_fallback_2_value = null; + protected final String __source_1_path = ""; protected final String __source_2_includes = ""; @@ -497,6 +501,25 @@ return null; } + protected Schema __set_property_fallback_begin(BindingProperty prop, + PropertyValue value) throws UnableToCompleteException { + boolean error = true; + for (String possibleValue : prop.getAllowedValues()) { + if (possibleValue.equals(value.token)) { + error = false; + break; + } + } + if (error) { + logger.log(TreeLogger.ERROR, "The fallback value '" + value.token + + "' was not previously defined for property '" + + prop.getName() + "'"); + throw new UnableToCompleteException(); + } + prop.setFallback(value.token); + return null; + } + /** * Indicates which subdirectories contain translatable source without * necessarily adding a sourcepath entry.
diff --git a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java index c257f62..0d550aa 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java +++ b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
@@ -138,6 +138,7 @@ for (int i = 0; i < orderedProps.length; i++) { final BindingProperty prop = orderedProps[i]; final String name = prop.getName(); + final String fallback = prop.getFallback(); if (name.equals(propertyName)) { final String value = orderedPropValues[i]; String[] values = prop.getDefinedValues(); @@ -151,6 +152,10 @@ return value; } + public String getFallbackValue() { + return fallback; + } + public String getName() { return name; }
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java index 58440f0..b744f20 100644 --- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java +++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
@@ -119,6 +119,7 @@ final BindingProperty cprop = (BindingProperty) prop; final String name = cprop.getName(); final String value = computePropertyValue(logger, propertyName, cprop); + final String fallback = cprop.getFallback(); final SortedSet<String> possibleValues = new TreeSet<String>(); for (String v : cprop.getDefinedValues()) { possibleValues.add(v); @@ -129,6 +130,10 @@ return value; } + public String getFallbackValue() { + return fallback; + } + public String getName() { return name; }
diff --git a/dev/core/test/com/google/gwt/core/ext/linker/impl/StandardSelectionPropertyTest.java b/dev/core/test/com/google/gwt/core/ext/linker/impl/StandardSelectionPropertyTest.java new file mode 100644 index 0000000..2fafd95 --- /dev/null +++ b/dev/core/test/com/google/gwt/core/ext/linker/impl/StandardSelectionPropertyTest.java
@@ -0,0 +1,65 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed 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.google.gwt.core.ext.linker.impl; + +import com.google.gwt.dev.cfg.BindingProperty; +import com.google.gwt.dev.cfg.PropertyProvider; + +import junit.framework.TestCase; + +/** + * Tests for {@link StandardSelectionProperty}. + */ +public class StandardSelectionPropertyTest extends TestCase { + + private static final String FBV = "FBV"; + + private static final String PROVIDER_MULTIFALLBACK = + "provider text with fallback=/*-FALLBACK-*/, repeated /*-FALLBACK-*//*-FALLBACK-*/"; + private static final String PROVIDER_MULTIFALLBACK_EMPTY = + "provider text with fallback=, repeated "; + private static final String PROVIDER_MULTIFALLBACK_FBV = + "provider text with fallback=FBV, repeated FBVFBV"; + + private static final String PROVIDER_NOFALLBACK = "provider text without fallback"; + + public void testNoFallback() { + BindingProperty bp = new BindingProperty("doesNotUseFallback"); + PropertyProvider provider = new PropertyProvider(PROVIDER_NOFALLBACK); + bp.setProvider(provider); + StandardSelectionProperty property = new StandardSelectionProperty(bp); + assertEquals(PROVIDER_NOFALLBACK, property.getPropertyProvider()); + + provider = new PropertyProvider(PROVIDER_MULTIFALLBACK); + bp.setProvider(provider); + property = new StandardSelectionProperty(bp); + assertEquals(PROVIDER_MULTIFALLBACK_EMPTY, property.getPropertyProvider()); + } + + public void testWithFallback() { + BindingProperty bp = new BindingProperty("doesUseFallback"); + bp.setFallback(FBV); + PropertyProvider provider = new PropertyProvider(PROVIDER_NOFALLBACK); + bp.setProvider(provider); + StandardSelectionProperty property = new StandardSelectionProperty(bp); + assertEquals(PROVIDER_NOFALLBACK, property.getPropertyProvider()); + + provider = new PropertyProvider(PROVIDER_MULTIFALLBACK); + bp.setProvider(provider); + property = new StandardSelectionProperty(bp); + assertEquals(PROVIDER_MULTIFALLBACK_FBV, property.getPropertyProvider()); + } +}
diff --git a/samples/i18n/src/com/google/gwt/sample/i18n/I18N.gwt.xml b/samples/i18n/src/com/google/gwt/sample/i18n/I18N.gwt.xml index ae0afae..cf6ecc9 100644 --- a/samples/i18n/src/com/google/gwt/sample/i18n/I18N.gwt.xml +++ b/samples/i18n/src/com/google/gwt/sample/i18n/I18N.gwt.xml
@@ -14,7 +14,8 @@ <module rename-to="i18n"> <inherits name="com.google.gwt.user.User" /> - <inherits name="com.google.gwt.i18n.I18N"/> + <inherits name="com.google.gwt.i18n.I18N"/> <entry-point class="com.google.gwt.sample.i18n.client.I18N" /> <extend-property name="locale" values="en,fr" /> + <set-property-fallback name="locale" value="fr"/> </module>
diff --git a/user/src/com/google/gwt/i18n/I18N.gwt.xml b/user/src/com/google/gwt/i18n/I18N.gwt.xml index 9ce67a7..8829121 100644 --- a/user/src/com/google/gwt/i18n/I18N.gwt.xml +++ b/user/src/com/google/gwt/i18n/I18N.gwt.xml
@@ -23,6 +23,7 @@ <![CDATA[ try { var locale; + var defaultLocale = "/*-FALLBACK-*/" || 'default'; // Look for the locale as a url argument if (locale == null) { @@ -48,22 +49,23 @@ // Look for an override computed by other means in the selection script locale = $wnd['__gwt_Locale']; } else { - $wnd['__gwt_Locale'] = locale || 'default'; + $wnd['__gwt_Locale'] = locale || defaultLocale; } if (locale == null) { - return "default"; + return defaultLocale; } while (!__gwt_isKnownPropertyValue("locale", locale)) { var lastIndex = locale.lastIndexOf("_"); if (lastIndex == -1) { - locale = "default"; + locale = defaultLocale; break; } else { locale = locale.substring(0,lastIndex); } } + return locale; } catch(e){ alert("Unexpected exception in locale detection, using default: " + e); @@ -91,4 +93,16 @@ --> <define-configuration-property name="runtime.locales" is-multi-valued="true"/> <set-configuration-property name="runtime.locales" value=""/> + + <!-- + A "real" locale to be served by default (i.e. if the browser either + doesn't have a requested locale, or it cannot be satisfied with any + of the available locales). The non-internationalized value "default" + is actually deficient for any actual locale, so users should set this + when then either <extend-property> or <define-property> user.agents for + their available translations. You should still have a locale named + "default" (because various tools expect that to be valid), but it will + be generated as the locale specified here. + --> + <set-property-fallback name="locale" value="default"/> </module>
diff --git a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java index b562788..74460f6 100644 --- a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java
@@ -17,7 +17,9 @@ import static com.google.gwt.i18n.rebind.AnnotationUtil.getClassAnnotation; +import com.google.gwt.core.ext.BadPropertyValueException; import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.SelectionProperty; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; @@ -150,6 +152,20 @@ if (genLocales.length != 0) { // verify the current locale is in the list for (String genLocale : genLocales) { + if (GwtLocale.DEFAULT_LOCALE.equals(genLocale)) { + // Locale "default" gets special handling because of property + // fallbacks; "default" might be mapped to any real locale. + try { + SelectionProperty localeProp = + context.getPropertyOracle().getSelectionProperty(logger, "locale"); + String defaultLocale = localeProp.getFallbackValue(); + if (defaultLocale.length() > 0) { + genLocale = defaultLocale; + } + } catch (BadPropertyValueException e) { + throw error(logger, "Could not get 'locale' property"); + } + } if (genLocale.equals(locale.toString())) { found = true; break;