001: /* LabelLoader.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Mon Jun 12 13:05:05 2006, Created by tomyeh
010: }}IS_NOTE
011:
012: Copyright (C) 2006 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: }}IS_RIGHT
016: */
017: package org.zkoss.util.resource.impl;
018:
019: import java.util.Iterator;
020: import java.util.List;
021: import java.util.LinkedList;
022: import java.util.Map;
023: import java.util.HashMap;
024: import java.util.Locale;
025: import java.util.Enumeration;
026: import java.net.URL;
027: import java.io.InputStream;
028: import java.io.IOException;
029:
030: import org.zkoss.mesg.MCommon;
031: import org.zkoss.lang.SystemException;
032: import org.zkoss.util.Maps;
033: import org.zkoss.util.Locales;
034: import org.zkoss.util.resource.LabelLocator;
035: import org.zkoss.util.resource.ClassLocator;
036: import org.zkoss.util.logging.Log;
037: import org.zkoss.util.WaitLock;
038: import org.zkoss.xel.Expressions;
039: import org.zkoss.xel.XelContext;
040: import org.zkoss.xel.VariableResolver;
041: import org.zkoss.xel.util.SimpleXelContext;
042:
043: /**
044: * The label loader (implementation only).
045: *
046: * Used to implement {@link org.zkoss.util.resource.Labels}.
047: *
048: * @author tomyeh
049: */
050: public class LabelLoader {
051: private static final Log log = Log.lookup(LabelLoader.class);
052:
053: /** A map of (Locale l, Map(String key, String label)). */
054: private final Map _labels = new HashMap(6);
055: /** A list of LabelLocator. */
056: private final List _locators = new LinkedList();
057: /** The XEL context. */
058: private XelContext _xelc;
059:
060: /** Returns the label of the specified key, or null if not found.
061: */
062: public String getLabel(String key) {
063: final String label = getProperty(Locales.getCurrent(), key);
064: if (label == null || label.length() == 0
065: || label.indexOf("${") < 0)
066: return label;
067:
068: //Interpret it
069: try {
070: return (String) Expressions.evaluate(_xelc, label,
071: String.class);
072: } catch (Throwable ex) {
073: log.error("Illegal label: key=" + key + " value=" + label,
074: ex);
075: return label; //recover it
076: }
077: }
078:
079: /** Sets the variable resolver, which is used if an EL expression
080: * is specified.
081: *
082: * @since 3.0.0
083: */
084: public VariableResolver setVariableResolver(VariableResolver resolv) {
085: final VariableResolver old = _xelc != null ? _xelc
086: .getVariableResolver() : null;
087: _xelc = resolv != null ? new SimpleXelContext(resolv, null)
088: : null;
089: return old;
090: }
091:
092: /** Registers a locator which is used to load i3-label*.properties
093: * from other resource, such as servlet contexts.
094: */
095: public void register(LabelLocator locator) {
096: if (locator == null)
097: throw new NullPointerException("locator");
098:
099: synchronized (_locators) {
100: //no need to use hashset because # of locators are few
101: for (Iterator it = _locators.iterator(); it.hasNext();)
102: if (it.next().equals(locator)) {
103: log.warning("Ignored because of replication: "
104: + locator);
105: return; //replicated
106: }
107: _locators.add(locator);
108: }
109:
110: reset(); //Labels might be loaded before, so...
111: }
112:
113: /** Resets all cached labels and next call to {@link #getLabel}
114: * will cause re-loading i3-label*.proerties.
115: */
116: public void reset() {
117: synchronized (_labels) {
118: _labels.clear();
119: }
120: }
121:
122: //-- deriver to override --//
123: /** Returns the property without interprets any expression.
124: * It searches properties defined in i3-label*.properties
125: * All label accesses are eventually done by this method.
126: *
127: * <p>To alter its behavior, you might override this method.
128: */
129: protected String getProperty(Locale locale, String key) {
130: String label = (String) getLabels(locale).get(key);
131: if (label != null)
132: return label;
133:
134: final String lang = locale.getLanguage();
135: final String cnty = locale.getCountry();
136: final String var = locale.getVariant();
137: if (var != null && var.length() > 0) {
138: label = (String) getLabels(new Locale(lang, cnty)).get(key);
139: if (label != null)
140: return label;
141: }
142: if (cnty != null && cnty.length() > 0) {
143: label = (String) getLabels(new Locale(lang, "")).get(key);
144: if (label != null)
145: return label;
146: }
147: return "en".equals(lang) ? null : (String) getLabels(
148: Locale.ENGLISH).get(key);
149: }
150:
151: //-- private utilities --//
152: /** Returns Map(String key, String label) of the specified locale. */
153: private final Map getLabels(Locale locale) {
154: WaitLock lock = null;
155: for (;;) {
156: final Object o;
157: synchronized (_labels) {
158: o = _labels.get(locale);
159: if (o == null)
160: _labels.put(locale, lock = new WaitLock()); //lock it
161: }
162:
163: if (o instanceof Map)
164: return (Map) o;
165: if (o == null)
166: break; //go to load the page
167:
168: //wait because some one is creating the servlet
169: if (!((WaitLock) o).waitUntilUnlock(5 * 60 * 1000))
170: log.warning("Take too long to wait loading labels: "
171: + locale
172: + "\nTry to load again automatically...");
173: } //for(;;)
174:
175: try {
176: //get the class name
177: log.info("Loading labels for " + locale);
178: final Map labels = new HashMap(617);
179:
180: //1. load from modules
181: final ClassLocator locator = new ClassLocator();
182: //if (log.finerable()) log.finer("Resources found: "+xmls);
183: for (Enumeration en = locator
184: .getResources(getI3LabelPath(locale)); en
185: .hasMoreElements();) {
186: final URL url = (URL) en.nextElement();
187: load(labels, url);
188: }
189:
190: //2. load from extra resource
191: for (Iterator it = _locators.iterator(); it.hasNext();) {
192: final URL url = ((LabelLocator) it.next())
193: .locate(locale);
194: if (url != null)
195: load(labels, url);
196: }
197:
198: //add to map
199: synchronized (_labels) {
200: _labels.put(locale, labels);
201: }
202:
203: return labels;
204: } catch (Throwable ex) {
205: synchronized (_labels) {
206: _labels.remove(locale);
207: }
208: throw SystemException.Aide.wrap(ex);
209: } finally {
210: lock.unlock(); //unlock (always unlock to avoid deadlock)
211: }
212: }
213:
214: /** Returns the path of metainfo/i3-label.properties. */
215: private static final String getI3LabelPath(Locale locale) {
216: return locale.equals(Locale.ENGLISH) ? "metainfo/i3-label.properties"
217: : "metainfo/i3-label_" + locale + ".properties";
218: }
219:
220: /** Loads all labels from the specified URL. */
221: private static final void load(Map labels, URL url)
222: throws IOException {
223: log.info(MCommon.FILE_OPENING, url);
224: final Map news = new HashMap();
225: Maps.load(news, url.openStream());
226: for (Iterator it = news.entrySet().iterator(); it.hasNext();) {
227: final Map.Entry me = (Map.Entry) it.next();
228: final Object key = me.getKey();
229: if (labels.put(key, me.getValue()) != null)
230: log.warning("Label of " + key + " is replaced by "
231: + url);
232: }
233: }
234: }
|