blob: e7951117c9246c857c7f9d5f22a7689397fc220e [file] [log] [blame]
/*
* Copyright 2007 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.i18n.client;
import com.google.gwt.core.client.JavaScriptObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;
/**
* Provides dynamic string lookup of key/value string pairs defined in a
* module's host HTML page. Each unique instance of <code>Dictionary</code> is
* bound to a named JavaScript object that resides in the global namespace of
* the host page's window object. The bound JavaScript object is used directly
* as an associative array.
*
* <p>
* For example, suppose you define the following JavaScript object in your host
* page:
*
* {@gwt.include com/google/gwt/examples/i18n/ThemeDictionaryExample.js}
*
* You can then use a <code>Dictionary</code> to access the key/value pairs
* above:
*
* {@example com.google.gwt.examples.i18n.ThemeDictionaryExample#useThemeDictionary()}
* </p>
*
* <p>
* Unlike the family of interfaces that extend
* {@link com.google.gwt.i18n.client.Localizable} which support static
* internationalization, the <code>Dictionary</code> class is fully dynamic.
* As a result, a variety of error conditions (particularly those involving key
* mismatches) cannot be caught until runtime. Similarly, the GWT compiler is
* unable discard unused dictionary values since the structure cannot be
* statically analyzed.
* </p>
*
* <h3>A Caveat Regarding Locale</h3>
* The module's host page completely determines the mappings defined for each
* dictionary without regard to the <code>locale</code> client property. Thus,
* <code>Dictionary</code> is the most flexible of the internationalization
* types and may provide the simplest form of integration with existing
* localization systems which were not specifically designed to use GWT's
* <code>locale</code> client property.
*
* <p>
* See {@link com.google.gwt.i18n.client.Localizable} for background on the
* <code>locale</code> client property.
* </p>
*
* <h3>Required Module</h3>
* Modules that use this interface should inherit
* <code>com.google.gwt.i18n.I18N</code>.
*
* {@gwt.include com/google/gwt/examples/i18n/InheritsExample.gwt.xml}
*/
public final class Dictionary {
private static Map<String, Dictionary> cache =
new HashMap<String, Dictionary>();
private static final int MAX_KEYS_TO_SHOW = 20;
/**
* Returns the <code>Dictionary</code> object associated with the given
* name.
*
* @param name
* @return specified dictionary
* @throws MissingResourceException
*/
public static Dictionary getDictionary(String name) {
Dictionary target = cache.get(name);
if (target == null) {
target = new Dictionary(name);
cache.put(name, target);
}
return target;
}
static void resourceErrorBadType(String name) {
throw new MissingResourceException("'" + name
+ "' is not a JavaScript object and cannot be used as a Dictionary",
null, name);
}
private JavaScriptObject dict;
private String label;
/**
* Constructor for <code>Dictionary</code>.
*
* @param name name of linked JavaScript Object
*/
private Dictionary(String name) {
if (name == null || "".equals(name)) {
throw new IllegalArgumentException(
"Cannot create a Dictionary with a null or empty name");
}
this.label = "Dictionary " + name;
attach(name);
if (dict == null) {
throw new MissingResourceException(
"Cannot find JavaScript object with the name '" + name + "'", name,
null);
}
}
/**
* Get the value associated with the given Dictionary key.
*
* We have to call Object.hasOwnProperty to verify that the value is
* defined on this object, rather than a superclass, since normal Object
* properties are also visible on this object.
*
* @param key to lookup
* @return the value
* @throws MissingResourceException if the value is not found
*/
public native String get(String key) /*-{
// In Firefox, jsObject.hasOwnProperty(key) requires a primitive string
key = String(key);
var map = this.@com.google.gwt.i18n.client.Dictionary::dict;
var value = map[key];
if (value == null || !map.hasOwnProperty(key)) {
this.@com.google.gwt.i18n.client.Dictionary::resourceError(Ljava/lang/String;)(key);
}
return String(value);
}-*/;
/**
* The set of keys associated with this dictionary.
*
* @return the Dictionary set
*/
public Set<String> keySet() {
HashSet<String> s = new HashSet<String>();
addKeys(s);
return s;
}
@Override
public String toString() {
return label;
}
/**
* Collection of values associated with this dictionary.
*
* @return the values
*/
public Collection<String> values() {
ArrayList<String> s = new ArrayList<String>();
addValues(s);
return s;
}
void resourceError(String key) {
String error = "Cannot find '" + key + "' in " + this;
throw new MissingResourceException(error, this.toString(), key);
}
private native void addKeys(HashSet<String> s) /*-{
var map = this.@com.google.gwt.i18n.client.Dictionary::dict
for (var key in map) {
if (map.hasOwnProperty(key)) {
s.@java.util.HashSet::add(Ljava/lang/Object;)(key);
}
}
}-*/;
private native void addValues(ArrayList<String> s) /*-{
var map = this.@com.google.gwt.i18n.client.Dictionary::dict
for (var key in map) {
if (map.hasOwnProperty(key)) {
var value = this.@com.google.gwt.i18n.client.Dictionary::get(Ljava/lang/String;)(key);
s.@java.util.ArrayList::add(Ljava/lang/Object;)(value);
}
}
}-*/;
private native void attach(String name)/*-{
try {
if (typeof($wnd[name]) != "object") {
@com.google.gwt.i18n.client.Dictionary::resourceErrorBadType(Ljava/lang/String;)(name);
}
this.@com.google.gwt.i18n.client.Dictionary::dict = $wnd[name];
} catch(e) {
@com.google.gwt.i18n.client.Dictionary::resourceErrorBadType(Ljava/lang/String;)(name);
}
}-*/;
}