Source Code Cross Referenced for LocalizedTextUtil.java in  » J2EE » webwork-2.2.6 » com » opensymphony » xwork » util » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » webwork 2.2.6 » com.opensymphony.xwork.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright (c) 2002-2006 by OpenSymphony
003:         * All rights reserved.
004:         */
005:        package com.opensymphony.xwork.util;
006:
007:        import com.opensymphony.xwork.ActionContext;
008:        import com.opensymphony.xwork.ActionInvocation;
009:        import com.opensymphony.xwork.ModelDriven;
010:
011:        import ognl.OgnlRuntime;
012:
013:        import org.apache.commons.logging.Log;
014:        import org.apache.commons.logging.LogFactory;
015:
016:        import java.beans.PropertyDescriptor;
017:        import java.lang.reflect.Field;
018:        import java.lang.reflect.Method;
019:        import java.lang.reflect.InvocationTargetException;
020:        import java.text.MessageFormat;
021:        import java.util.*;
022:
023:        /**
024:         * Provides support for localization in XWork.
025:         *
026:         * <!-- START SNIPPET: searchorder -->
027:         * Resource bundles are searched in the following order:<p/>
028:         * <p/>
029:         * <ol>
030:         * <li>ActionClass.properties</li>
031:         * <li>BaseClass.properties (all the way to Object.properties)</li>
032:         * <li>Interface.properties (every interface and sub-interface)</li>
033:         * <li>ModelDriven's model (if implements ModelDriven), for the model object repeat from 1</li>
034:         * <li>package.properties (of the directory where class is located and every parent directory all the way to the root directory)</li>
035:         * <li>search up the i18n message key hierarchy itself</li>
036:         * <li>global resource properties (webwork.custom.i18n.resources) defined in webwork.properties</li>
037:         * </ol>
038:         * <p/>
039:         * <!-- END SNIPPET: searchorder -->
040:         *
041:         * <!-- START SNIPPET: packagenote -->
042:         * To clarify #5, while traversing the package hierarchy, WW will look for a file package.properties:<p/>
043:         * com/<br/>
044:         * &nbsp; acme/<br/>
045:         * &nbsp; &nbsp; package.properties<br/>
046:         * &nbsp; &nbsp; actions/<br/>
047:         * &nbsp; &nbsp; &nbsp; package.properties<br/>
048:         * &nbsp; &nbsp; &nbsp; FooAction.java<br/>
049:         * &nbsp; &nbsp; &nbsp; FooAction.properties<br/>
050:         * <p/>
051:         * If FooAction.properties does not exist, com/acme/action/package.properties will be searched for, if
052:         * not found com/acme/package.properties, if not found com/package.properties, etc.
053:         * <p/>
054:         * <!-- END SNIPPET: packagenote -->
055:         *
056:         * <!-- START SNIPPET: globalresource -->
057:         * A global resource bundle could be specified through the 'webwork.custom.i18n.resources' property in
058:         * webwork.properties. The locale can be siwtched by 'webwork.locale' in the webwork.properties as well.
059:         * <p/>
060:         * <!-- END SNIPPET: globalresource -->
061:         *
062:         * <!-- START SNIPPET: strutscomparison -->
063:         * Struts users should be familiar with the application.properties resource bundle, where you can put all the messages
064:         * in the application that are going to be translated. WebWork, though, splits the resource bundles per action or model
065:         * class, and you may end up with duplicated messages in those resource bundles. A quick fix for that is to create a
066:         * file called ActionSupport.properties in com/opensymphony/xwork and put it on your classpath. This will only work well
067:         * if all your actions subclass ActionSupport.
068:         * <p/>
069:         * <!-- END SNIPPET: strutscomparison -->
070:         *
071:         * @author Jason Carreira
072:         * @author Mark Woon
073:         * @author Rainer Hermanns
074:         * @author tm_jee
075:         * 
076:         * @author $Date: 2007-06-30 18:15:16 +0200 (Sat, 30 Jun 2007) $ $Id: LocalizedTextUtil.java 1536 2007-06-30 16:15:16Z tm_jee $
077:         */
078:        public class LocalizedTextUtil {
079:
080:            private static final Log _log = LogFactory
081:                    .getLog(LocalizedTextUtil.class);
082:
083:            private static List DEFAULT_RESOURCE_BUNDLES = null;
084:            private static final Log LOG = LogFactory
085:                    .getLog(LocalizedTextUtil.class);
086:            private static boolean reloadBundles = false;
087:            private static final Collection misses = new HashSet();
088:            private static final Map messageFormats = new HashMap();
089:
090:            static {
091:                clearDefaultResourceBundles();
092:            }
093:
094:            /**
095:             * Clears the internal list of resource bundles.
096:             */
097:            public static void clearDefaultResourceBundles() {
098:                if (DEFAULT_RESOURCE_BUNDLES != null) {
099:                    DEFAULT_RESOURCE_BUNDLES.clear();
100:                }
101:                DEFAULT_RESOURCE_BUNDLES = Collections
102:                        .synchronizedList(new ArrayList());
103:                DEFAULT_RESOURCE_BUNDLES
104:                        .add("com/opensymphony/xwork/xwork-messages");
105:            }
106:
107:            /**
108:             * Should resorce bundles be reloaded.
109:             * <p/>
110:             * In WW see <code>webwork.i18n.reload</code> property.
111:             * @param reloadBundles  reload bundles?  
112:             */
113:            public static void setReloadBundles(boolean reloadBundles) {
114:                LocalizedTextUtil.reloadBundles = reloadBundles;
115:            }
116:
117:            /**
118:             * Add's the bundle to the internal list of default bundles.
119:             * <p/>
120:             * If the bundle already exists in the list it will be readded.
121:             * 
122:             * @param resourceBundleName   the name of the bundle to add.
123:             */
124:            public static void addDefaultResourceBundle(
125:                    String resourceBundleName) {
126:                //make sure this doesn't get added more than once
127:                DEFAULT_RESOURCE_BUNDLES.remove(resourceBundleName);
128:                DEFAULT_RESOURCE_BUNDLES.add(0, resourceBundleName);
129:
130:                if (LOG.isDebugEnabled()) {
131:                    LOG.debug("Added default resource bundle '"
132:                            + resourceBundleName
133:                            + "' to default resource bundles = "
134:                            + DEFAULT_RESOURCE_BUNDLES);
135:                }
136:            }
137:
138:            /**
139:             * Builds a {@link java.util.Locale} from a String of the form en_US_foo into a Locale
140:             * with language "en", country "US" and variant "foo". This will parse the output of
141:             * {@link java.util.Locale#toString()}.
142:             *
143:             * @param localeStr The locale String to parse.
144:             * @param defaultLocale The locale to use if localeStr is <tt>null</tt>.
145:             * @return requested Locale
146:             */
147:            public static Locale localeFromString(String localeStr,
148:                    Locale defaultLocale) {
149:                if ((localeStr == null) || (localeStr.trim().length() == 0)
150:                        || (localeStr.equals("_"))) {
151:                    if (defaultLocale != null) {
152:                        return defaultLocale;
153:                    }
154:                    return Locale.getDefault();
155:                }
156:
157:                int index = localeStr.indexOf('_');
158:                if (index < 0) {
159:                    return new Locale(localeStr);
160:                }
161:
162:                String language = localeStr.substring(0, index);
163:                if (index == localeStr.length()) {
164:                    return new Locale(language);
165:                }
166:
167:                localeStr = localeStr.substring(index + 1);
168:                index = localeStr.indexOf('_');
169:                if (index < 0) {
170:                    return new Locale(language, localeStr);
171:                }
172:
173:                String country = localeStr.substring(0, index);
174:                if (index == localeStr.length()) {
175:                    return new Locale(language, country);
176:                }
177:
178:                localeStr = localeStr.substring(index + 1);
179:                return new Locale(language, country, localeStr);
180:            }
181:
182:            /**
183:             * Returns a localized message for the specified key, aTextName.  Neither the key nor the
184:             * message is evaluated.
185:             *
186:             * @param aTextName the message key
187:             * @param locale    the locale the message should be for
188:             * @return a localized message based on the specified key, or null if no localized message can be found for it
189:             */
190:            public static String findDefaultText(String aTextName, Locale locale) {
191:                List localList = DEFAULT_RESOURCE_BUNDLES; // it isn't sync'd, but this is so rare, let's do it anyway
192:
193:                for (Iterator iterator = localList.iterator(); iterator
194:                        .hasNext();) {
195:                    String bundleName = (String) iterator.next();
196:
197:                    ResourceBundle bundle = findResourceBundle(bundleName,
198:                            locale);
199:                    if (bundle != null) {
200:                        reloadBundles();
201:                        try {
202:                            return bundle.getString(aTextName);
203:                        } catch (MissingResourceException e) {
204:                            // ignore and try others
205:                        }
206:                    }
207:                }
208:
209:                return null;
210:            }
211:
212:            /**
213:             * Returns a localized message for the specified key, aTextName, substituting variables from the
214:             * array of params into the message.  Neither the key nor the message is evaluated.
215:             *
216:             * @param aTextName the message key
217:             * @param locale    the locale the message should be for
218:             * @param params    an array of objects to be substituted into the message text
219:             * @return A formatted message based on the specified key, or null if no localized message can be found for it
220:             */
221:            public static String findDefaultText(String aTextName,
222:                    Locale locale, Object[] params) {
223:                String defaultText = findDefaultText(aTextName, locale);
224:                if (defaultText != null) {
225:                    MessageFormat mf = buildMessageFormat(defaultText, locale);
226:                    return mf.format(params);
227:                }
228:                return null;
229:            }
230:
231:            /**
232:             * Finds the given resorce bundle by it's name.
233:             * <p/>
234:             * Will use <code>Thread.currentThread().getContextClassLoader()</code> as the classloader.
235:             * 
236:             * @param aBundleName  the name of the bundle (usually it's FQN classname).
237:             * @param locale       the locale.
238:             * @return  the bundle, <tt>null</tt> if not found.
239:             */
240:            public static ResourceBundle findResourceBundle(String aBundleName,
241:                    Locale locale) {
242:                synchronized (misses) {
243:                    try {
244:                        if (!misses.contains(aBundleName)) {
245:                            return ResourceBundle.getBundle(aBundleName,
246:                                    locale, Thread.currentThread()
247:                                            .getContextClassLoader());
248:                        }
249:                    } catch (MissingResourceException ex) {
250:                        misses.add(aBundleName);
251:                    }
252:                }
253:
254:                return null;
255:            }
256:
257:            /**
258:             * Calls {@link #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)}
259:             * with aTextName as the default message.
260:             *
261:             * @see #findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args)
262:             */
263:            public static String findText(Class aClass, String aTextName,
264:                    Locale locale) {
265:                return findText(aClass, aTextName, locale, aTextName,
266:                        new Object[0]);
267:            }
268:
269:            /**
270:             * Finds a localized text message for the given key, aTextName. Both the key and the message
271:             * itself is evaluated as required.  The following algorithm is used to find the requested
272:             * message:
273:             * <p/>
274:             * <ol>
275:             * <li>Look for message in aClass' class hierarchy.
276:             * <ol>
277:             * <li>Look for the message in a resource bundle for aClass</li>
278:             * <li>If not found, look for the message in a resource bundle for any implemented interface</li>
279:             * <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
280:             * </ol></li>
281:             * <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
282:             * the model's class hierarchy (repeat sub-steps listed above).</li>
283:             * <li>If not found, look for message in child property.  This is determined by evaluating
284:             * the message key as an OGNL expression.  For example, if the key is
285:             * <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
286:             * object.  If so, repeat the entire process fromthe beginning with the object's class as
287:             * aClass and "address.state" as the message key.</li>
288:             * <li>If not found, look for the message in aClass' package hierarchy.</li>
289:             * <li>If still not found, look for the message in the default resource bundles.</li>
290:             * <li>Return defaultMessage</li>
291:             * </ol>
292:             * <p/>
293:             * When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
294:             * message for that specific key cannot be found, the general form will also be looked up
295:             * (i.e. user.phone[*]).
296:             * <p/>
297:             * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
298:             * will be treated as an OGNL expression and evaluated as such.
299:             *
300:             * @param aClass         the class whose name to use as the start point for the search
301:             * @param aTextName      the key to find the text message for
302:             * @param locale         the locale the message should be for
303:             * @param defaultMessage the message to be returned if no text message can be found in any
304:             *                       resource bundle
305:             * @return the localized text, or null if none can be found and no defaultMessage is provided
306:             */
307:            public static String findText(Class aClass, String aTextName,
308:                    Locale locale, String defaultMessage, Object[] args) {
309:                OgnlValueStack valueStack = ActionContext.getContext()
310:                        .getValueStack();
311:                return findText(aClass, aTextName, locale, defaultMessage,
312:                        args, valueStack);
313:
314:            }
315:
316:            /**
317:             * <b>This method call will log a warning message (in debug level) if message is not found</b>
318:             * 
319:             * Finds a localized text message for the given key, aTextName. Both the key and the message
320:             * itself is evaluated as required.  The following algorithm is used to find the requested
321:             * message:
322:             * <p/>
323:             * <ol>
324:             * <li>Look for message in aClass' class hierarchy.
325:             * <ol>
326:             * <li>Look for the message in a resource bundle for aClass</li>
327:             * <li>If not found, look for the message in a resource bundle for any implemented interface</li>
328:             * <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
329:             * </ol></li>
330:             * <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
331:             * the model's class hierarchy (repeat sub-steps listed above).</li>
332:             * <li>If not found, look for message in child property.  This is determined by evaluating
333:             * the message key as an OGNL expression.  For example, if the key is
334:             * <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
335:             * object.  If so, repeat the entire process fromthe beginning with the object's class as
336:             * aClass and "address.state" as the message key.</li>
337:             * <li>If not found, look for the message in aClass' package hierarchy.</li>
338:             * <li>If still not found, look for the message in the default resource bundles.</li>
339:             * <li>Return defaultMessage</li>
340:             * </ol>
341:             * <p/>
342:             * When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
343:             * message for that specific key cannot be found, the general form will also be looked up
344:             * (i.e. user.phone[*]).
345:             * <p/>
346:             * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
347:             * will be treated as an OGNL expression and evaluated as such.
348:             * <p/>
349:             * If a message is <b>not</b> found a WARN log will be logged.
350:             *
351:             * @param aClass         the class whose name to use as the start point for the search
352:             * @param aTextName      the key to find the text message for
353:             * @param locale         the locale the message should be for
354:             * @param defaultMessage the message to be returned if no text message can be found in any
355:             *                       resource bundle
356:             * @param valueStack     the value stack to use to evaluate expressions instead of the
357:             *                       one in the ActionContext ThreadLocal
358:             * @return the localized text, or null if none can be found and no defaultMessage is provided
359:             */
360:            public static String findText(Class aClass, String aTextName,
361:                    Locale locale, String defaultMessage, Object[] args,
362:                    OgnlValueStack valueStack) {
363:                return findText(aClass, aTextName, locale, defaultMessage,
364:                        args, valueStack, true);
365:            }
366:
367:            /**
368:             * Finds a localized text message for the given key, aTextName. Both the key and the message
369:             * itself is evaluated as required.  The following algorithm is used to find the requested
370:             * message:
371:             * <p/>
372:             * <ol>
373:             * <li>Look for message in aClass' class hierarchy.
374:             * <ol>
375:             * <li>Look for the message in a resource bundle for aClass</li>
376:             * <li>If not found, look for the message in a resource bundle for any implemented interface</li>
377:             * <li>If not found, traverse up the Class' hierarchy and repeat from the first sub-step</li>
378:             * </ol></li>
379:             * <li>If not found and aClass is a {@link ModelDriven} Action, then look for message in
380:             * the model's class hierarchy (repeat sub-steps listed above).</li>
381:             * <li>If not found, look for message in child property.  This is determined by evaluating
382:             * the message key as an OGNL expression.  For example, if the key is
383:             * <i>user.address.state</i>, then it will attempt to see if "user" can be resolved into an
384:             * object.  If so, repeat the entire process fromthe beginning with the object's class as
385:             * aClass and "address.state" as the message key.</li>
386:             * <li>If not found, look for the message in aClass' package hierarchy.</li>
387:             * <li>If still not found, look for the message in the default resource bundles.</li>
388:             * <li>Return defaultMessage</li>
389:             * </ol>
390:             * <p/>
391:             * When looking for the message, if the key indexes a collection (e.g. user.phone[0]) and a
392:             * message for that specific key cannot be found, the general form will also be looked up
393:             * (i.e. user.phone[*]).
394:             * <p/>
395:             * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
396:             * will be treated as an OGNL expression and evaluated as such.
397:             * <p/>
398:             * If a message is <b>not</b> found a WARN log will be logged.
399:             *
400:             * @param aClass         the class whose name to use as the start point for the search
401:             * @param aTextName      the key to find the text message for
402:             * @param locale         the locale the message should be for
403:             * @param defaultMessage the message to be returned if no text message can be found in any
404:             *                       resource bundle
405:             * @param valueStack     the value stack to use to evaluate expressions instead of the
406:             *                       one in the ActionContext ThreadLocal
407:             * @param warnIfNoMessageFound log warning message (in debug level) if message is not found.
408:             * @return the localized text, or null if none can be found and no defaultMessage is provided
409:             */
410:            public static String findText(Class aClass, String aTextName,
411:                    Locale locale, String defaultMessage, Object[] args,
412:                    OgnlValueStack valueStack, boolean warnIfNoMessageFound) {
413:                String indexedTextName = null;
414:                if (aTextName == null) {
415:                    LOG.warn("Trying to find text with null key!");
416:                    aTextName = "";
417:                }
418:                // calculate indexedTextName (collection[*]) if applicable
419:                if (aTextName.indexOf("[") != -1) {
420:                    int i = -1;
421:
422:                    indexedTextName = aTextName;
423:
424:                    while ((i = indexedTextName.indexOf("[", i + 1)) != -1) {
425:                        int j = indexedTextName.indexOf("]", i);
426:                        String a = indexedTextName.substring(0, i);
427:                        String b = indexedTextName.substring(j);
428:                        indexedTextName = a + "[*" + b;
429:                    }
430:                }
431:
432:                // search up class hierarchy
433:                String msg = findMessage(aClass, aTextName, indexedTextName,
434:                        locale, args, null, valueStack);
435:
436:                if (msg != null) {
437:                    return msg;
438:                }
439:
440:                if (ModelDriven.class.isAssignableFrom(aClass)) {
441:                    ActionContext context = ActionContext.getContext();
442:                    // search up model's class hierarchy
443:                    ActionInvocation actionInvocation = context
444:                            .getActionInvocation();
445:
446:                    // ActionInvocation may be null if we're being run from a Sitemesh filter, so we won't get model texts if this is null
447:                    if (actionInvocation != null) {
448:                        Object action = actionInvocation.getAction();
449:                        Object model = ((ModelDriven) action).getModel();
450:                        if (model != null) {
451:                            msg = findMessage(model.getClass(), aTextName,
452:                                    indexedTextName, locale, args, null,
453:                                    valueStack);
454:                            if (msg != null) {
455:                                return msg;
456:                            }
457:                        }
458:                    }
459:                }
460:
461:                // nothing still? alright, search the package hierarchy now
462:                for (Class clazz = aClass; (clazz != null)
463:                        && !clazz.equals(Object.class); clazz = clazz
464:                        .getSuperclass()) {
465:
466:                    String basePackageName = clazz.getName();
467:                    while (basePackageName.lastIndexOf('.') != -1) {
468:                        basePackageName = basePackageName.substring(0,
469:                                basePackageName.lastIndexOf('.'));
470:                        String packageName = basePackageName + ".package";
471:                        msg = getMessage(packageName, locale, aTextName,
472:                                valueStack, args);
473:
474:                        if (msg != null) {
475:                            return msg;
476:                        }
477:
478:                        if (indexedTextName != null) {
479:                            msg = getMessage(packageName, locale,
480:                                    indexedTextName, valueStack, args);
481:
482:                            if (msg != null) {
483:                                return msg;
484:                            }
485:                        }
486:                    }
487:                }
488:
489:                // see if it's a child property
490:                int idx = aTextName.indexOf(".");
491:
492:                if (idx != -1) {
493:                    String newKey = null;
494:                    String prop = null;
495:
496:                    if (aTextName
497:                            .startsWith(XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX)) {
498:                        idx = aTextName.indexOf(".",
499:                                XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX
500:                                        .length());
501:
502:                        if (idx != -1) {
503:                            prop = aTextName
504:                                    .substring(
505:                                            XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX
506:                                                    .length(), idx);
507:                            newKey = XWorkConverter.CONVERSION_ERROR_PROPERTY_PREFIX
508:                                    + aTextName.substring(idx + 1);
509:                        }
510:                    } else {
511:                        prop = aTextName.substring(0, idx);
512:                        newKey = aTextName.substring(idx + 1);
513:                    }
514:
515:                    if (prop != null) {
516:                        Object obj = valueStack.findValue(prop);
517:                        try {
518:                            Object realTarget = OgnlUtil.getRealTarget(prop,
519:                                    valueStack.getContext(), valueStack
520:                                            .getRoot());
521:
522:                            if (realTarget != null) {
523:                                PropertyDescriptor propertyDescriptor = OgnlRuntime
524:                                        .getPropertyDescriptor(realTarget
525:                                                .getClass(), prop);
526:                                if (propertyDescriptor != null) {
527:                                    Class clazz = propertyDescriptor
528:                                            .getPropertyType();
529:                                    if (clazz != null) {
530:                                        if (obj != null)
531:                                            valueStack.push(obj);
532:                                        msg = findText(clazz, newKey, locale,
533:                                                null, args);
534:                                        if (obj != null)
535:                                            valueStack.pop();
536:
537:                                        if (msg != null) {
538:                                            return msg;
539:                                        }
540:                                    }
541:                                }
542:                            }
543:                        } catch (Exception e) {
544:                            _log.debug("unable to find property " + prop, e);
545:                        }
546:                    }
547:                }
548:
549:                // get default
550:                GetDefaultMessageReturnArg result = null;
551:                if (indexedTextName == null) {
552:                    result = getDefaultMessage(aTextName, locale, valueStack,
553:                            args, defaultMessage);
554:                } else {
555:                    result = getDefaultMessage(aTextName, locale, valueStack,
556:                            args, null);
557:                    if (result.message != null) {
558:                        return result.message;
559:                    }
560:                    result = getDefaultMessage(indexedTextName, locale,
561:                            valueStack, args, defaultMessage);
562:                }
563:
564:                // could we find the text, if not log a warn
565:                if (warnIfNoMessageFound && unableToFindTextForKey(result)) {
566:                    String warn = "Unable to find text for key '" + aTextName
567:                            + "' ";
568:                    if (indexedTextName != null) {
569:                        warn += " or indexed key '" + indexedTextName + "' ";
570:                    }
571:                    warn += "in class '" + aClass.getName() + "' and locale '"
572:                            + locale + "'";
573:                    LOG.debug(warn);
574:                }
575:
576:                return result != null ? result.message : null;
577:            }
578:
579:            /**
580:             * Determines if we found the text in the bundles.
581:             * 
582:             * @param result   the result so far
583:             * @return  <tt>true</tt> if we could <b>not</b> find the text, <tt>false</tt> if the text was found (=success). 
584:             */
585:            private static boolean unableToFindTextForKey(
586:                    GetDefaultMessageReturnArg result) {
587:                if (result == null || result.message == null) {
588:                    return true;
589:                }
590:
591:                // did we find it in the bundle, then no problem?
592:                if (result.foundInBundle) {
593:                    return false;
594:                }
595:
596:                // not found in bundle
597:                return true;
598:            }
599:
600:            /**
601:             * Finds a localized text message for the given key, aTextName, in the specified resource bundle
602:             * with aTextName as the default message.
603:             * <p/>
604:             * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
605:             * will be treated as an OGNL expression and evaluated as such.
606:             *
607:             * @see #findText(java.util.ResourceBundle, String, java.util.Locale, String, Object[])
608:             */
609:            public static String findText(ResourceBundle bundle,
610:                    String aTextName, Locale locale) {
611:                return findText(bundle, aTextName, locale, aTextName,
612:                        new Object[0]);
613:            }
614:
615:            /**
616:             * Finds a localized text message for the given key, aTextName, in the specified resource
617:             * bundle.
618:             * <p/>
619:             * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
620:             * will be treated as an OGNL expression and evaluated as such.
621:             * <p/>
622:             * If a message is <b>not</b> found a WARN log will be logged.
623:             * 
624:             * @param bundle     the bundle
625:             * @param aTextName  the key
626:             * @param locale     the locale
627:             * @param defaultMessage  the default message to use if no message was found in the bundle
628:             * @param args       arguments for the message formatter.
629:             */
630:            public static String findText(ResourceBundle bundle,
631:                    String aTextName, Locale locale, String defaultMessage,
632:                    Object[] args) {
633:                OgnlValueStack valueStack = ActionContext.getContext()
634:                        .getValueStack();
635:                return findText(bundle, aTextName, locale, defaultMessage,
636:                        args, valueStack);
637:            }
638:
639:            /**
640:             * <b>This method will log a warning (in debug level) if no message is found.</b>
641:             * 
642:             * Finds a localized text message for the given key, aTextName, in the specified resource
643:             * bundle.
644:             * <p/>
645:             * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
646:             * will be treated as an OGNL expression and evaluated as such.
647:             * <p/>
648:             * If a message is <b>not</b> found a WARN log will be logged.
649:             * 
650:             * @param bundle     the bundle
651:             * @param aTextName  the key
652:             * @param locale     the locale
653:             * @param defaultMessage  the default message to use if no message was found in the bundle
654:             * @param args       arguments for the message formatter.
655:             * @param valueStack the OGNL value stack.
656:             */
657:            public static String findText(ResourceBundle bundle,
658:                    String aTextName, Locale locale, String defaultMessage,
659:                    Object[] args, OgnlValueStack valueStack) {
660:                return findText(bundle, aTextName, locale, defaultMessage,
661:                        args, valueStack, true);
662:            }
663:
664:            /**
665:             * Finds a localized text message for the given key, aTextName, in the specified resource
666:             * bundle.
667:             * <p/>
668:             * If a message is found, it will also be interpolated.  Anything within <code>${...}</code>
669:             * will be treated as an OGNL expression and evaluated as such.
670:             * <p/>
671:             * If a message is <b>not</b> found a WARN log will be logged.
672:             * 
673:             * @param bundle     the bundle
674:             * @param aTextName  the key
675:             * @param locale     the locale
676:             * @param defaultMessage  the default message to use if no message was found in the bundle
677:             * @param args       arguments for the message formatter.
678:             * @param valueStack the OGNL value stack.
679:             */
680:            public static String findText(ResourceBundle bundle,
681:                    String aTextName, Locale locale, String defaultMessage,
682:                    Object[] args, OgnlValueStack valueStack,
683:                    boolean warnIfNoMessageFound) {
684:                try {
685:                    reloadBundles();
686:
687:                    String message = TextParseUtil.translateVariables(bundle
688:                            .getString(aTextName), valueStack);
689:                    MessageFormat mf = buildMessageFormat(message, locale);
690:
691:                    return mf.format(args);
692:                } catch (MissingResourceException ex) {
693:                    // ignore
694:                }
695:
696:                GetDefaultMessageReturnArg result = getDefaultMessage(
697:                        aTextName, locale, valueStack, args, defaultMessage);
698:                if (warnIfNoMessageFound && unableToFindTextForKey(result)) {
699:                    LOG.warn("Unable to find text for key '" + aTextName
700:                            + "' in ResourceBundles for locale '" + locale
701:                            + "'");
702:                }
703:                return result == null ? null : result.message;
704:            }
705:
706:            /**
707:             * Gets the default message.
708:             */
709:            private static GetDefaultMessageReturnArg getDefaultMessage(
710:                    String key, Locale locale, OgnlValueStack valueStack,
711:                    Object[] args, String defaultMessage) {
712:                GetDefaultMessageReturnArg result = null;
713:                boolean found = true;
714:
715:                if (key != null) {
716:                    String message = findDefaultText(key, locale);
717:
718:                    if (message == null) {
719:                        message = defaultMessage;
720:                        found = false; // not found in bundles
721:                    }
722:
723:                    // defaultMessage may be null
724:                    if (message != null) {
725:                        MessageFormat mf = buildMessageFormat(TextParseUtil
726:                                .translateVariables(message, valueStack),
727:                                locale);
728:
729:                        String msg = mf.format(args);
730:                        result = new GetDefaultMessageReturnArg(msg, found);
731:                    }
732:                }
733:
734:                return result;
735:            }
736:
737:            /**
738:             * Gets the message from the named resource bundle.
739:             */
740:            private static String getMessage(String bundleName, Locale locale,
741:                    String key, OgnlValueStack valueStack, Object[] args) {
742:                ResourceBundle bundle = findResourceBundle(bundleName, locale);
743:                if (bundle == null) {
744:                    return null;
745:                }
746:
747:                reloadBundles();
748:
749:                try {
750:                    String message = TextParseUtil.translateVariables(bundle
751:                            .getString(key), valueStack);
752:                    MessageFormat mf = buildMessageFormat(message, locale);
753:                    return mf.format(args);
754:                } catch (MissingResourceException e) {
755:                    return null;
756:                }
757:            }
758:
759:            private static MessageFormat buildMessageFormat(String pattern,
760:                    Locale locale) {
761:                MessageFormatKey key = new MessageFormatKey(pattern, locale);
762:                MessageFormat format = (MessageFormat) messageFormats.get(key);
763:                if (format == null) {
764:                    format = new MessageFormat(pattern);
765:                    format.setLocale(locale);
766:                    format.applyPattern(pattern);
767:                    messageFormats.put(key, format);
768:                }
769:
770:                return format;
771:            }
772:
773:            /**
774:             * Traverse up class hierarchy looking for message.  Looks at class, then implemented interface,
775:             * before going up hierarchy.
776:             */
777:            private static String findMessage(Class clazz, String key,
778:                    String indexedKey, Locale locale, Object[] args,
779:                    Set checked, OgnlValueStack valueStack) {
780:                if (checked == null) {
781:                    checked = new TreeSet();
782:                } else if (checked.contains(clazz.getName())) {
783:                    return null;
784:                }
785:
786:                // look in properties of this class
787:                String msg = getMessage(clazz.getName(), locale, key,
788:                        valueStack, args);
789:
790:                if (msg != null) {
791:                    return msg;
792:                }
793:
794:                if (indexedKey != null) {
795:                    msg = getMessage(clazz.getName(), locale, indexedKey,
796:                            valueStack, args);
797:
798:                    if (msg != null) {
799:                        return msg;
800:                    }
801:                }
802:
803:                // look in properties of implemented interfaces
804:                Class[] interfaces = clazz.getInterfaces();
805:
806:                for (int x = 0; x < interfaces.length; x++) {
807:                    msg = getMessage(interfaces[x].getName(), locale, key,
808:                            valueStack, args);
809:
810:                    if (msg != null) {
811:                        return msg;
812:                    }
813:
814:                    if (indexedKey != null) {
815:                        msg = getMessage(interfaces[x].getName(), locale,
816:                                indexedKey, valueStack, args);
817:
818:                        if (msg != null) {
819:                            return msg;
820:                        }
821:                    }
822:                }
823:
824:                // traverse up hierarchy
825:                if (clazz.isInterface()) {
826:                    interfaces = clazz.getInterfaces();
827:
828:                    for (int x = 0; x < interfaces.length; x++) {
829:                        msg = findMessage(interfaces[x], key, indexedKey,
830:                                locale, args, checked, valueStack);
831:
832:                        if (msg != null) {
833:                            return msg;
834:                        }
835:                    }
836:                } else {
837:                    if (!clazz.equals(Object.class) && !clazz.isPrimitive()) {
838:                        return findMessage(clazz.getSuperclass(), key,
839:                                indexedKey, locale, args, checked, valueStack);
840:                    }
841:                }
842:
843:                return null;
844:            }
845:
846:            private static void reloadBundles() {
847:                if (reloadBundles) {
848:                    try {
849:                        clearMap(ResourceBundle.class, null, "cacheList");
850:
851:                        // now, for the true and utter hack, if we're running in tomcat, clear
852:                        // it's class loader resource cache as well.
853:                        clearTomcatCache();
854:                    } catch (Exception e) {
855:                        LOG.error("Could not reload resource bundles", e);
856:                    }
857:                }
858:            }
859:
860:            private static void clearTomcatCache() {
861:                ClassLoader loader = Thread.currentThread()
862:                        .getContextClassLoader();
863:                // no need for compilation here.
864:                Class cl = loader.getClass();
865:
866:                try {
867:                    if ("org.apache.catalina.loader.WebappClassLoader"
868:                            .equals(cl.getName())) {
869:                        clearMap(cl, loader, "resourceEntries");
870:                    } else {
871:                        if (LOG.isDebugEnabled()) {
872:                            LOG.debug("class loader " + cl.getName()
873:                                    + " is not tomcat loader.");
874:                        }
875:                    }
876:                } catch (Exception e) {
877:                    LOG.warn("couldn't clear tomcat cache", e);
878:                }
879:            }
880:
881:            private static void clearMap(Class cl, Object obj, String name)
882:                    throws NoSuchFieldException, IllegalAccessException,
883:                    NoSuchMethodException, InvocationTargetException {
884:                Field field = cl.getDeclaredField(name);
885:                field.setAccessible(true);
886:
887:                Object cache = field.get(obj);
888:
889:                synchronized (cache) {
890:                    Class ccl = cache.getClass();
891:                    Method clearMethod = ccl.getMethod("clear", new Class[0]);
892:                    clearMethod.invoke(cache, new Class[0]);
893:                }
894:
895:            }
896:
897:            /**
898:             * Clears all the internal lists. 
899:             */
900:            public static void reset() {
901:                clearDefaultResourceBundles();
902:
903:                synchronized (misses) {
904:                    misses.clear();
905:                }
906:
907:                synchronized (messageFormats) {
908:                    messageFormats.clear();
909:                }
910:            }
911:
912:            static class MessageFormatKey {
913:                String pattern;
914:                Locale locale;
915:
916:                MessageFormatKey(String pattern, Locale locale) {
917:                    this .pattern = pattern;
918:                    this .locale = locale;
919:                }
920:
921:                public boolean equals(Object o) {
922:                    if (this  == o)
923:                        return true;
924:                    if (!(o instanceof  MessageFormatKey))
925:                        return false;
926:
927:                    final MessageFormatKey messageFormatKey = (MessageFormatKey) o;
928:
929:                    if (locale != null ? !locale
930:                            .equals(messageFormatKey.locale)
931:                            : messageFormatKey.locale != null)
932:                        return false;
933:                    if (pattern != null ? !pattern
934:                            .equals(messageFormatKey.pattern)
935:                            : messageFormatKey.pattern != null)
936:                        return false;
937:
938:                    return true;
939:                }
940:
941:                public int hashCode() {
942:                    int result;
943:                    result = (pattern != null ? pattern.hashCode() : 0);
944:                    result = 29 * result
945:                            + (locale != null ? locale.hashCode() : 0);
946:                    return result;
947:                }
948:            }
949:
950:            static class GetDefaultMessageReturnArg {
951:                String message;
952:                boolean foundInBundle;
953:
954:                public GetDefaultMessageReturnArg(String message,
955:                        boolean foundInBundle) {
956:                    this.message = message;
957:                    this.foundInBundle = foundInBundle;
958:                }
959:            }
960:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.