001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.gwt.client.util; 029 030import org.opencms.util.CmsStringUtil; 031 032import java.util.Date; 033import java.util.HashMap; 034import java.util.Map; 035import java.util.MissingResourceException; 036 037import com.google.gwt.i18n.client.Dictionary; 038 039/** 040 * Reads localized resource Strings from a <code>java.util.ResourceBundle</code> 041 * and provides convenience methods to access the Strings from a template.<p> 042 * 043 * @since 8.0.0 044 * 045 * @see org.opencms.i18n.CmsMessages 046 */ 047public class CmsMessages { 048 049 /** The suffix of a "short" localized key name. */ 050 public static final String KEY_SHORT_SUFFIX = ".short"; 051 052 /** Prefix / Suffix for unknown keys. */ 053 public static final String UNKNOWN_KEY_EXTENSION = "???"; 054 055 /** Cached dictionaries. */ 056 private static Map<String, Dictionary> m_dictionaries; 057 058 /** The name of the resource bundle. */ 059 private String m_bundleName; 060 061 /** The current dictionary. */ 062 private Dictionary m_dictionary; 063 064 /** 065 * Default constructor.<p> 066 * 067 * @param bundleName the localized bundle name 068 */ 069 public CmsMessages(String bundleName) { 070 071 if (m_dictionaries == null) { 072 m_dictionaries = new HashMap<String, Dictionary>(); 073 } 074 m_bundleName = bundleName; 075 m_dictionary = m_dictionaries.get(m_bundleName); 076 if (m_dictionary == null) { 077 m_dictionary = Dictionary.getDictionary(m_bundleName.replace('.', '_')); 078 m_dictionaries.put(m_bundleName, m_dictionary); 079 } 080 } 081 082 /** 083 * Helper method for formatting message parameters.<p> 084 * 085 * @param result the raw message containing placeholders like {0} 086 * @param args the parameters to insert into the placeholders 087 * 088 * @return the formatted message 089 */ 090 public static String formatMessage(String result, Object... args) { 091 092 // key was found in the bundle - create and apply the formatter 093 for (int i = 0; i < args.length; i++) { 094 if (args[i] instanceof Date) { 095 Date date = (Date)args[i]; 096 result = result.replace(getRegEx(i), CmsDateTimeUtil.getDateTime(date, CmsDateTimeUtil.Format.MEDIUM)); 097 result = result.replace( 098 getRegEx(i, "time"), 099 CmsDateTimeUtil.getTime(date, CmsDateTimeUtil.Format.MEDIUM)); 100 result = result.replace( 101 getRegEx(i, "time", "short"), 102 CmsDateTimeUtil.getTime(date, CmsDateTimeUtil.Format.SHORT)); 103 result = result.replace( 104 getRegEx(i, "time", "medium"), 105 CmsDateTimeUtil.getTime(date, CmsDateTimeUtil.Format.MEDIUM)); 106 result = result.replace( 107 getRegEx(i, "time", "long"), 108 CmsDateTimeUtil.getTime(date, CmsDateTimeUtil.Format.LONG)); 109 result = result.replace( 110 getRegEx(i, "time", "full"), 111 CmsDateTimeUtil.getTime(date, CmsDateTimeUtil.Format.FULL)); 112 result = result.replace( 113 getRegEx(i, "date"), 114 CmsDateTimeUtil.getDate(date, CmsDateTimeUtil.Format.MEDIUM)); 115 result = result.replace( 116 getRegEx(i, "date", "short"), 117 CmsDateTimeUtil.getDate(date, CmsDateTimeUtil.Format.SHORT)); 118 result = result.replace( 119 getRegEx(i, "date", "medium"), 120 CmsDateTimeUtil.getDate(date, CmsDateTimeUtil.Format.MEDIUM)); 121 result = result.replace( 122 getRegEx(i, "date", "long"), 123 CmsDateTimeUtil.getDate(date, CmsDateTimeUtil.Format.LONG)); 124 result = result.replace( 125 getRegEx(i, "date", "full"), 126 CmsDateTimeUtil.getDate(date, CmsDateTimeUtil.Format.FULL)); 127 } else { 128 result = result.replace(getRegEx(i), String.valueOf(args[i])); 129 } 130 } 131 return result; 132 } 133 134 /** 135 * Formats an unknown key.<p> 136 * 137 * @param keyName the key to format 138 * 139 * @return the formatted unknown key 140 * 141 * @see #isUnknownKey(String) 142 */ 143 public static String formatUnknownKey(String keyName) { 144 145 StringBuffer buf = new StringBuffer(64); 146 buf.append(UNKNOWN_KEY_EXTENSION); 147 buf.append(" "); 148 buf.append(keyName); 149 buf.append(" "); 150 buf.append(UNKNOWN_KEY_EXTENSION); 151 return buf.toString(); 152 } 153 154 /** 155 * Returns <code>true</code> if the provided value matches the scheme 156 * <code>"??? " + keyName + " ???"</code>, that is the value appears to be an unknown key.<p> 157 * 158 * Also returns <code>true</code> if the given value is <code>null</code>.<p> 159 * 160 * @param value the value to check 161 * 162 * @return true if the value is matches the scheme for unknown keys 163 * 164 * @see #formatUnknownKey(String) 165 */ 166 public static boolean isUnknownKey(String value) { 167 168 return (value == null) || (value.startsWith(UNKNOWN_KEY_EXTENSION)); 169 } 170 171 /** 172 * Returns a regular expression for replacement.<p> 173 * 174 * @param position the parameter number 175 * @param options the optional options 176 * 177 * @return the regular expression for replacement 178 */ 179 private static String getRegEx(int position, String... options) { 180 181 String value = "" + position; 182 for (int i = 0; i < options.length; i++) { 183 value += "," + options[i]; 184 } 185 return "{" + value + "}"; 186 } 187 188 /** 189 * Returns the localized message bundle wrapped in this instance.<p> 190 * 191 * Mainly for API compatibility with the core localization methods.<p> 192 * 193 * @return the localized message bundle wrapped in this instance 194 */ 195 public CmsMessages getBundle() { 196 197 return this; 198 } 199 200 /** 201 * Returns the name of the resource bundle.<p> 202 * 203 * @return the name of the resource bundle 204 */ 205 public String getBundleName() { 206 207 return m_bundleName; 208 } 209 210 /** 211 * Returns a formated date String from a Date value, 212 * the format being {@link CmsDateTimeUtil.Format#SHORT} and the locale 213 * based on this instance.<p> 214 * 215 * @param date the Date object to format as String 216 * 217 * @return the formatted date 218 */ 219 public String getDate(Date date) { 220 221 return CmsDateTimeUtil.getDate(date, CmsDateTimeUtil.Format.SHORT); 222 } 223 224 /** 225 * Returns a formated date String from a Date value, 226 * the formatting based on the provided option and the locale 227 * based on this instance.<p> 228 * 229 * @param date the Date object to format as String 230 * @param format the format to use, see {@link CmsDateTimeUtil.Format} for possible values 231 * 232 * @return the formatted date 233 */ 234 public String getDate(Date date, CmsDateTimeUtil.Format format) { 235 236 return CmsDateTimeUtil.getDate(date, format); 237 } 238 239 /** 240 * Returns a formated date String from a timestamp value, 241 * the format being {@link CmsDateTimeUtil.Format#SHORT} and the locale 242 * based on this instance.<p> 243 * 244 * @param time the time value to format as date 245 * 246 * @return the formatted date 247 */ 248 public String getDate(long time) { 249 250 return CmsDateTimeUtil.getDate(new Date(time), CmsDateTimeUtil.Format.SHORT); 251 } 252 253 /** 254 * Returns a formated date and time String from a Date value, 255 * the format being {@link CmsDateTimeUtil.Format#SHORT} and the locale 256 * based on this instance.<p> 257 * 258 * @param date the Date object to format as String 259 * @return the formatted date and time 260 */ 261 public String getDateTime(Date date) { 262 263 return CmsDateTimeUtil.getDateTime(date, CmsDateTimeUtil.Format.SHORT); 264 } 265 266 /** 267 * Returns a formated date and time String from a Date value, 268 * the formatting based on the provided option and the locale 269 * based on this instance.<p> 270 * 271 * @param date the Date object to format as String 272 * @param format the format to use, see {@link CmsDateTimeUtil.Format} for possible values 273 * @return the formatted date and time 274 */ 275 public String getDateTime(Date date, CmsDateTimeUtil.Format format) { 276 277 return CmsDateTimeUtil.getDateTime(date, format); 278 } 279 280 /** 281 * Returns a formated date and time String from a timestamp value, 282 * the format being {@link CmsDateTimeUtil.Format#SHORT} and the locale 283 * based on this instance.<p> 284 * 285 * @param time the time value to format as date 286 * @return the formatted date and time 287 */ 288 public String getDateTime(long time) { 289 290 return CmsDateTimeUtil.getDateTime(new Date(time), CmsDateTimeUtil.Format.SHORT); 291 } 292 293 /** 294 * Returns the internal dictionary.<p> 295 * 296 * @return the internal dictionary 297 */ 298 public Dictionary getDictionary() { 299 300 return m_dictionary; 301 } 302 303 /** 304 * Returns the localized resource string for a given message key.<p> 305 * 306 * If the key was not found in the bundle, the return value is 307 * <code>"??? " + keyName + " ???"</code>. This will also be returned 308 * if the bundle was not properly initialized first. 309 * 310 * @param keyName the key for the desired string 311 * 312 * @return the resource string for the given key 313 */ 314 public String key(String keyName) { 315 316 return key(keyName, false); 317 } 318 319 /** 320 * Returns the localized resource string for a given message key.<p> 321 * 322 * If the key was not found in the bundle, the return value 323 * depends on the setting of the allowNull parameter. If set to false, 324 * the return value is always a String in the format 325 * <code>"??? " + keyName + " ???"</code>. 326 * If set to true, null is returned if the key is not found. 327 * This will also be returned 328 * if the bundle was not properly initialized first. 329 * 330 * @param keyName the key for the desired string 331 * @param allowNull if true, 'null' is an allowed return value 332 * 333 * @return the resource string for the given key 334 */ 335 public String key(String keyName, boolean allowNull) { 336 337 try { 338 if (m_dictionary != null) { 339 return m_dictionary.get(keyName); 340 } 341 } catch (@SuppressWarnings("unused") MissingResourceException e) { 342 // not found, return warning 343 if (allowNull) { 344 return null; 345 } 346 } 347 return formatUnknownKey(keyName); 348 } 349 350 /** 351 * Returns the selected localized message for the initialized resource bundle and locale.<p> 352 * 353 * If the key was found in the bundle, it will be formatted using 354 * a <code>{@link java.text.MessageFormat}</code> using the provided parameters.<p> 355 * 356 * If the key was not found in the bundle, the return value is 357 * <code>"??? " + keyName + " ???"</code>. This will also be returned 358 * if the bundle was not properly initialized first. 359 * 360 * @param key the message key 361 * @param args the message arguments 362 * 363 * @return the selected localized message for the initialized resource bundle and locale 364 */ 365 public String key(String key, Object... args) { 366 367 if ((args == null) || (args.length == 0)) { 368 // no parameters available, use simple key method 369 return key(key); 370 } 371 372 String result = key(key, true); 373 if (result == null) { 374 // key was not found 375 result = formatUnknownKey(key); 376 } else { 377 result = formatMessage(result, args); 378 } 379 // return the result 380 return result; 381 } 382 383 /** 384 * Returns the localized resource string for a given message key.<p> 385 * 386 * If the key was not found in the bundle, the provided default value 387 * is returned.<p> 388 * 389 * @param keyName the key for the desired string 390 * @param defaultValue the default value in case the key does not exist in the bundle 391 * 392 * @return the resource string for the given key it it exists, or the given default if not 393 */ 394 public String keyDefault(String keyName, String defaultValue) { 395 396 String result = key(keyName, true); 397 return (result == null) ? defaultValue : result; 398 } 399 400 /** 401 * Returns the localized resource string for a given message key, 402 * treating all values appended with "|" as replacement parameters.<p> 403 * 404 * If the key was found in the bundle, it will be formatted using 405 * a <code>{@link java.text.MessageFormat}</code> using the provided parameters. 406 * The parameters have to be appended to the key separated by a "|". 407 * For example, the keyName <code>error.message|First|Second</code> 408 * would use the key <code>error.message</code> with the parameters 409 * <code>First</code> and <code>Second</code>. This would be the same as calling 410 * <code>{@link CmsMessages#key(String, Object[])}</code>.<p> 411 * 412 * If no parameters are appended with "|", this is the same as calling 413 * <code>{@link CmsMessages#key(String)}</code>.<p> 414 * 415 * If the key was not found in the bundle, the return value is 416 * <code>"??? " + keyName + " ???"</code>. This will also be returned 417 * if the bundle was not properly initialized first. 418 * 419 * @param keyName the key for the desired string, optionally containing parameters appended with a "|" 420 * 421 * @return the resource string for the given key 422 * 423 * @see #key(String, Object[]) 424 * @see #key(String) 425 */ 426 public String keyWithParams(String keyName) { 427 428 if (keyName.indexOf('|') == -1) { 429 // no separator found, key has no parameters 430 return key(keyName, false); 431 } else { 432 // this key contains parameters 433 String[] values = CmsStringUtil.splitAsArray(keyName, "|"); 434 String cutKeyName = values[0]; 435 String[] params = new String[values.length - 1]; 436 System.arraycopy(values, 1, params, 0, params.length); 437 return key(cutKeyName, (Object[])params); 438 } 439 } 440}