001: /* Copyright 2003 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.i18n;
007:
008: import java.io.Serializable;
009: import java.util.ArrayList;
010: import java.util.List;
011: import java.util.Locale;
012: import java.util.StringTokenizer;
013:
014: import org.apache.commons.logging.Log;
015: import org.apache.commons.logging.LogFactory;
016: import org.jasig.portal.properties.PropertiesManager;
017: import org.jasig.portal.security.IPerson;
018: import org.jasig.portal.utils.CommonUtils;
019: import org.jasig.portal.utils.DocumentFactory;
020: import org.w3c.dom.Document;
021: import org.w3c.dom.Element;
022:
023: /**
024: * Manages locales on behalf of a user.
025: * This class currently keeps track of locales at the following levels:<br>
026: * <ol>
027: * <li>User's locale preferences (associated with a user ID)</li>
028: * <li>Browser's locale preferences (from the Accept-Language request header)</li>
029: * <li>Session's locale preferences (set via the portal request parameter uP_locales)</li>
030: * <li>Portal's locale preferences (set in portal.properties)</li>
031: * </ol>
032: * Eventually, this class will also keep track of locale preferences at
033: * the following levels:<br>
034: * <ol>
035: * <li>Layout node's locale preferences</li>
036: * <li>User profile's locale preferences</li>
037: * </ol>
038: * @author Shoji Kajita <a href="mailto:">kajita@itc.nagoya-u.ac.jp</a>
039: * @author Ken Weiner, kweiner@unicon.net
040: * @version $Revision: 35884 $
041: */
042: public class LocaleManager implements Serializable {
043:
044: private static final Log log = LogFactory
045: .getLog(LocaleManager.class);
046:
047: /**
048: * Default value for localeAware.
049: * This value will be used when the corresponding property cannot be loaded.
050: */
051: public static final boolean DEFAULT_LOCALE_AWARE = false;
052:
053: private IPerson person;
054: private static boolean localeAware = PropertiesManager
055: .getPropertyAsBoolean(
056: "org.jasig.portal.i18n.LocaleManager.locale_aware",
057: DEFAULT_LOCALE_AWARE);
058: private static Locale jvmLocale;
059: private static Locale[] portalLocales;
060: private Locale[] sessionLocales;
061: private Locale[] browserLocales;
062: private Locale[] userLocales;
063:
064: /**
065: * Constructor that associates a locale manager with a user.
066: * @param person the user
067: */
068: public LocaleManager(IPerson person) {
069: this .person = person;
070: jvmLocale = Locale.getDefault();
071: if (localeAware) {
072: portalLocales = loadPortalLocales();
073: try {
074: userLocales = LocaleStoreFactory.getLocaleStoreImpl()
075: .getUserLocales(person);
076: } catch (Exception e) {
077: log.error("Error populating userLocals", e);
078: }
079: }
080: }
081:
082: /**
083: * Constructor that sets up locales according to
084: * the <code>Accept-Language</code> request header
085: * from a user's browser.
086: * @param person the user
087: * @param acceptLanguage the Accept-Language request header from a user's browser
088: */
089: public LocaleManager(IPerson person, String acceptLanguage) {
090: this (person);
091: this .browserLocales = parseLocales(acceptLanguage);
092: }
093:
094: // Getters
095: public static boolean isLocaleAware() {
096: return localeAware;
097: }
098:
099: public static Locale getJvmLocale() {
100: return jvmLocale;
101: }
102:
103: public static Locale[] getPortalLocales() {
104: return portalLocales;
105: }
106:
107: public Locale[] getBrowserLocales() {
108: return browserLocales;
109: }
110:
111: public Locale[] getUserLocales() {
112: return userLocales;
113: }
114:
115: public Locale[] getSessionLocales() {
116: return sessionLocales;
117: }
118:
119: // Setters
120: public static void setJvmLocale(Locale jvmLocale) {
121: LocaleManager.jvmLocale = jvmLocale;
122: }
123:
124: public static void setPortalLocales(Locale[] portalLocales) {
125: LocaleManager.portalLocales = portalLocales;
126: }
127:
128: public void setBrowserLocales(Locale[] browserLocales) {
129: this .browserLocales = browserLocales;
130: }
131:
132: public void setUserLocales(Locale[] userLocales) {
133: this .userLocales = userLocales;
134: }
135:
136: public void setSessionLocales(Locale[] sessionLocales) {
137: this .sessionLocales = sessionLocales;
138: }
139:
140: /**
141: * Read and parse portal_locales from portal.properties.
142: * portal_locales will be in the form of a comma-separated
143: * list, e.g. en_US,ja_JP,sv_SE
144: */
145: private Locale[] loadPortalLocales() {
146: String portalLocalesString = PropertiesManager
147: .getProperty("org.jasig.portal.i18n.LocaleManager.portal_locales");
148: return parseLocales(portalLocalesString);
149: }
150:
151: /**
152: * Produces a sorted list of locales according to locale preferences
153: * obtained from several places. The following priority is given:
154: * session, user, browser, portal, and jvm.
155: * @return the sorted list of locales
156: */
157: public Locale[] getLocales() {
158: // Need logic to construct ordered locale list.
159: // Consider creating a separate ILocaleResolver
160: // interface to do this work.
161: List locales = new ArrayList();
162: // Add highest priority locales first
163: addToLocaleList(locales, sessionLocales);
164: addToLocaleList(locales, userLocales);
165: // We will ignore browser locales until we know how to
166: // translate them into proper java.util.Locales
167: //addToLocaleList(locales, browserLocales);
168: addToLocaleList(locales, portalLocales);
169: addToLocaleList(locales, new Locale[] { jvmLocale });
170: return (Locale[]) locales.toArray(new Locale[0]);
171: }
172:
173: /**
174: * Add locales to the locale list if they aren't in there already
175: */
176: private void addToLocaleList(List localeList, Locale[] locales) {
177: if (locales != null) {
178: for (int i = 0; i < locales.length; i++) {
179: if (locales[i] != null
180: && !localeList.contains(locales[i]))
181: localeList.add(locales[i]);
182: }
183: }
184: }
185:
186: /**
187: * Helper method to produce a <code>java.util.Locale</code> array from
188: * a comma-delimited locale string list, e.g. "en_US,ja_JP"
189: * @param localeStringList the locales to parse
190: * @return an array of locales representing the locale string list
191: */
192: public static Locale[] parseLocales(String localeStringList) {
193: Locale[] locales = null;
194: if (localeStringList != null) {
195: StringTokenizer st = new StringTokenizer(localeStringList,
196: ",");
197: locales = new Locale[st.countTokens()];
198: for (int i = 0; st.hasMoreTokens(); i++) {
199: String localeString = st.nextToken().trim();
200: locales[i] = parseLocale(localeString);
201: }
202: }
203: return locales;
204: }
205:
206: /**
207: * Helper method to produce a <code>java.util.Locale</code> object from
208: * a locale string such as en_US or ja_JP.
209: * @param localeString a locale string such as en_US
210: * @return a java.util.Locale object representing the locale string
211: */
212: public static Locale parseLocale(String localeString) {
213: String language = null;
214: String country = null;
215: String variant = null;
216:
217: // Sometimes people specify "en-US" instead of "en_US", so
218: // we'll try to clean that up.
219: localeString = CommonUtils.replaceText(localeString, "-", "_");
220:
221: StringTokenizer st = new StringTokenizer(localeString, "_");
222:
223: if (st.hasMoreTokens()) {
224: language = st.nextToken();
225: }
226: if (st.hasMoreTokens()) {
227: country = st.nextToken();
228: }
229: if (st.hasMoreTokens()) {
230: variant = st.nextToken();
231: }
232:
233: Locale locale = null;
234:
235: if (variant != null) {
236: locale = new Locale(language, country, variant);
237: } else if (country != null) {
238: locale = new Locale(language, country);
239: } else if (language != null) {
240: // Uncomment the following line
241: // when we can count on JDK 1.4!
242: //locale = new Locale(language);
243: locale = new Locale(language, "");
244: }
245:
246: return locale;
247: }
248:
249: /**
250: * Constructs a comma-delimited list of locales
251: * that could be parsed back into a Locale
252: * array with parseLocales(String localeStringList).
253: * @param locales the list of locales
254: * @return a string representing the list of locales
255: */
256: public static String stringValueOf(Locale[] locales) {
257: StringBuffer sb = new StringBuffer();
258: for (int i = 0; i < locales.length; i++) {
259: Locale locale = locales[i];
260: sb.append(locale.toString());
261: if (i < locales.length - 1) {
262: sb.append(",");
263: }
264: }
265: return sb.toString();
266: }
267:
268: /**
269: * Stores the user locales persistantly.
270: * @param userLocales the user locales preference
271: * @throws Exception
272: */
273: public void persistUserLocales(Locale[] userLocales)
274: throws Exception {
275: setUserLocales(userLocales);
276: LocaleStoreFactory.getLocaleStoreImpl().updateUserLocales(
277: person, userLocales);
278: }
279:
280: /**
281: * Creates an XML representation of a list of locales.
282: * @param locales the locale list
283: * @return the locale list as XML
284: */
285: public static Document xmlValueOf(Locale[] locales) {
286: return xmlValueOf(locales, null);
287: }
288:
289: /**
290: * Creates an XML representation of a list of locales.
291: * If a selected locale is supplied, the XML element representing
292: * the selected locale will have an attribute of selected with value
293: * of true. This is helpful when constructing user interfaces that
294: * indicate which locale is selected.
295: * @param locales the locale list
296: * @param selectedLocale a locale that should be selected if it is in the list
297: * @return the locale list as XML
298: */
299: public static Document xmlValueOf(Locale[] locales,
300: Locale selectedLocale) {
301: Document doc = DocumentFactory.getNewDocument();
302:
303: // <locales>
304: Element localesE = doc.createElement("locales");
305: for (int i = 0; i < locales.length; i++) {
306: Element locE = doc.createElement("locale");
307: locE.setAttribute("displayName", locales[i]
308: .getDisplayName(locales[0]));
309: locE.setAttribute("code", locales[i].toString());
310:
311: // Mark which locale is the user's preference
312: if (selectedLocale != null
313: && selectedLocale.equals(locales[i])) {
314: locE.setAttribute("selected", "true");
315: }
316:
317: // <language iso2="..." iso3="..." displayName="..."/>
318: Element languageE = doc.createElement("language");
319: languageE.setAttribute("iso2", locales[i].getLanguage());
320: try {
321: languageE.setAttribute("iso3", locales[i]
322: .getISO3Language());
323: } catch (Exception e) {
324: // Do nothing
325: }
326: languageE.setAttribute("displayName", locales[i]
327: .getDisplayLanguage(locales[0]));
328: locE.appendChild(languageE);
329:
330: // <country iso2="..." iso3="..." displayName="..."/>
331: Element countryE = doc.createElement("country");
332: countryE.setAttribute("iso2", locales[i].getCountry());
333: try {
334: countryE.setAttribute("iso3", locales[i]
335: .getISO3Country());
336: } catch (Exception e) {
337: // Do nothing
338: }
339: countryE.setAttribute("displayName", locales[i]
340: .getDisplayCountry(locales[0]));
341: locE.appendChild(countryE);
342:
343: // <variant code="..." displayName="..."/>
344: Element variantE = doc.createElement("variant");
345: variantE.setAttribute("code", locales[i].getVariant());
346: variantE.setAttribute("displayName", locales[i]
347: .getDisplayVariant(locales[0]));
348: locE.appendChild(variantE);
349:
350: localesE.appendChild(locE);
351: }
352: doc.appendChild(localesE);
353: return doc;
354: }
355:
356: public String toString() {
357: StringBuffer sb = new StringBuffer(1024);
358: sb.append("LocaleManager's locales").append("\n");
359: sb.append("-----------------------").append("\n");
360: sb.append("Session locales: ");
361: if (sessionLocales != null) {
362: sb.append(stringValueOf(sessionLocales));
363: }
364: sb.append("\n");
365: sb.append("User locales: ");
366: if (userLocales != null) {
367: sb.append(stringValueOf(userLocales));
368: }
369: sb.append("\n");
370: sb.append("Browser locales: ");
371: if (browserLocales != null) {
372: sb.append(stringValueOf(browserLocales));
373: }
374: sb.append("\n");
375: sb.append("Portal locales: ");
376: if (portalLocales != null) {
377: sb.append(stringValueOf(portalLocales));
378: }
379: sb.append("\n");
380: sb.append("JVM locale: ");
381: if (jvmLocale != null) {
382: sb.append(jvmLocale.toString());
383: }
384: sb.append("\n");
385: sb.append("Sorted locales: ");
386: Locale[] sortedLocales = getLocales();
387: if (sortedLocales != null) {
388: sb.append(stringValueOf(sortedLocales));
389: }
390: sb.append("\n");
391: return sb.toString();
392: }
393:
394: }
|