| /* | |
| * 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); | |
| } | |
| }-*/; | |
| } |