Source Code Cross Referenced for KeyedMessage.java in  » Portal » jetspeed-2.1.3 » org » apache » jetspeed » i18n » 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 » Portal » jetspeed 2.1.3 » org.apache.jetspeed.i18n 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Licensed to the Apache Software Foundation (ASF) under one or more
003:         * contributor license agreements.  See the NOTICE file distributed with
004:         * this work for additional information regarding copyright ownership.
005:         * The ASF licenses this file to You under the Apache License, Version 2.0
006:         * (the "License"); you may not use this file except in compliance with
007:         * the License.  You may obtain a copy of the License at
008:         * 
009:         *      http://www.apache.org/licenses/LICENSE-2.0
010:         * 
011:         * Unless required by applicable law or agreed to in writing, software
012:         * distributed under the License is distributed on an "AS IS" BASIS,
013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014:         * See the License for the specific language governing permissions and
015:         * limitations under the License.
016:         */
017:        package org.apache.jetspeed.i18n;
018:
019:        import java.io.Serializable;
020:        import java.lang.reflect.Field;
021:        import java.lang.reflect.Modifier;
022:        import java.text.MessageFormat;
023:        import java.util.HashMap;
024:        import java.util.Locale;
025:        import java.util.ResourceBundle;
026:
027:        import org.apache.jetspeed.exception.JetspeedException; // for javadoc ref
028:        import org.apache.jetspeed.security.SecurityException; // for javadoc ref
029:
030:        /**
031:         * KeyedMessage provides an automatically derived i18n message key based on its static instance definition and can be
032:         * used as comparable constant too.
033:         * <h3>Purpose</h3>
034:         * <p>
035:         * With a KeyedMessage a named constant message (format) can be statically defined which automatically translate
036:         * themselves for a specific locale using an automatically derived ResourceBundle or even a specified one.
037:         * </p>
038:         * <h3>Key derivation</h3>
039:         * <p>
040:         * Because KeyedMessages are created with a default message (format), even if no ResourceBundle or its key is defined or
041:         * can't be found, message translation is still possible.
042:         * </p>
043:         * <p>
044:         * A KeyedMessage automatically derives the ResourceBundle lookup key from its (statically defined) instance field name
045:         * using the following format: <br/><br/><code>
046:         *     &nbsp;&nbsp;&lt;containingClass.name&gt;.&lt;staticInstanceField.name&gt;
047:         * </code>
048:         * <br/>
049:         * </p>
050:         * <p>
051:         * The containingClass is derived at construction time by analyzing the StackTraceElements of a thrown exception. This
052:         * <em><b>requires</b></em> the instance to be defined as a public static field!
053:         * </p>
054:         * <p>
055:         * At first access, the key is resolved by inspecting the derived containingClass for the <em>declared</em> field
056:         * defining this instance.
057:         * </p>
058:         * <p>
059:         * If the KeyedMessage instance <em><b>wasn't</b></em> defined as public static field, the key can't be resolved and
060:         * message translation using a ResourceBundle won't be possible. Translation using the default message will still work
061:         * though. Furthermore, this instance can't be used as comparable named constant as the {@link #equals(Object)}method
062:         * will always return false in this case.
063:         * </p>
064:         * <h3>Default ResourceBundle name derivation</h3>
065:         * <p>
066:         * When the key of a KeyedMessage is resolved, the default ResourceBundle name for message translation is retrieved from
067:         * the defined public static String field named {@link #KEYED_MESSAGE_BUNDLE_FIELD_NAME "KEYED_MESSAGE_BUNDLE"}defined
068:         * in its containingClass or one of its superClasses or interfaces.
069:         * </p>
070:         * <p>
071:         * If this field cannot be found, the fully qualified name of the containingClass is used.
072:         * </p>
073:         * <p>
074:         * ResourceBundle names are cached in a Map for each containingClass and only derived for the first KeyedMessage defined
075:         * in a containingClass.
076:         * </p>
077:         * <p>
078:         * <em>Again: only <b>resolved</b> instances can use a ResourceBundle for message translation.</em>
079:         * </p>
080:         * <h3>Default Locale lookup</h3>
081:         * <p>
082:         * When a message is translated without a specified Locale, {@link CurrentLocale#get()}is used to determine the default
083:         * Locale for the current Thread.
084:         * </p>
085:         * <p>
086:         * In Jetspeed, the <code>LocalizationValve</code> initializes the {@link CurrentLocale} on each request.
087:         * KeyedMessages accessed within the context of an Jetspeed request therefore will always be translated using the
088:         * current user Locale with the {@link #getMessage()}or {@link #toString()}methods.
089:         * </p>
090:         * <h3>Default ResourceBundle lookup</h3>
091:         * <p>
092:         * If a message translation is done using the default ResourceBundle name the ResourceBundle is retrieved using the
093:         * ClassLoader of the containingClass. This means the bundle(s) must be provided in the same context as from where the
094:         * containingClass is loaded. Usually (and preferably), this will be from the shared classpath of the webserver.
095:         * </p>
096:         * <h3>MessageFormat parameters</h3>
097:         * <p>
098:         * MessageFormat patterns can also be used for a KeyedMessage.<br/>
099:         * With the {@link #create(Object[])}method a specialized copy of a KeyedMessage instance can be created containing the
100:         * arguments to be used during message translation.
101:         * </p>
102:         * <p>
103:         * This new copy remains {@link equals(Object)}to its source and can still be used for named constant comparison.
104:         * </p>
105:         * <p>
106:         * For simplified usage, three {@link #create(Object)},{@link #create(Object, Object)}and
107:         * {@link #create(Object, Object, Object)}methods are provided which delegate to {@link #create(Object[])}with their
108:         * argument(s) transformed into an Object array.
109:         * </p>
110:         * <h3>Extending KeyedMessage</h3>
111:         * <p>
112:         * An statically defined KeyedMessage can be used as a "simple" named constant. <br/>If additional metadata is required
113:         * like some kind of status, level or type indication, the KeyedMessage class can easily be extended by providing a
114:         * specialized version of the {@link #create(KeyedMessage, Object[])}copy factory.
115:         * </p>
116:         * <h3>Usage</h3>
117:         * <p>
118:         * KeyedMessage has been used to replace the hardcoded {@link SecurityException} String constants. <br/>The
119:         * ResourceBundle name used is defined by {@link JetspeedException#KEYED_MESSAGE_BUNDLE} which is the superClass of
120:         * {@link SecurityException}.<br/>
121:         * <p>
122:         * <em>For a different ResourceBundle to be used for SecurityException messages a KEYED_MESSAGE_BUNDLE field can be defined
123:         * in {@link SecurityException} too, overriding the one in {@link JetspeedException}.</em>
124:         * </p>
125:         * <p>
126:         * Example:
127:         * </p>
128:         * <pre>
129:         *       public class JetspeedException extends Exception {
130:         *           public static final String KEYED_MESSAGE_BUNDLE = &quot;org.apache.jetspeed.exception.JetspeedExceptionMessages&quot;;
131:         *           ...
132:         *    
133:         *           public String getMessage() {
134:         *                if ( keyedMessage != null ) {
135:         *                   return keyedMessage.getMessage(); // translated using current Locale and default ResourceBundle
136:         *                }
137:         *                return super.getMessage();
138:         *           }
139:         *       }
140:         *    
141:         *       public class SecurityException extends JetspeedException {
142:         *           public static final KeyedMessage USER_DOES_NOT_EXIST = new KeyedMessage(&quot;The user {0} does not exist.&quot;);
143:         *           ...
144:         *       }
145:         *    
146:         *       // resource file: org.apache.jetspeed.exception.JetspeedExceptionMessages_nl.properties
147:         *       org.apache.jetspeed.security.SecurityException.USER_DOES_NOT_EXIST = De gebruiker {0} bestaat niet.
148:         *       ...
149:         *    
150:         *       public class UserManagerImpl implements UserManager {
151:         *           public User getUser(String username) throws SecurityException {
152:         *               ...
153:         *               if (null == userPrincipal) { 
154:         *                   throw new SecurityException(SecurityException.USER_DOES_NOT_EXIST.create(username));
155:         *               }
156:         *               ...
157:         *           }
158:         *           ...
159:         *       }
160:         *    
161:         *       // example get User
162:         *       try {
163:         *           User user = userManager.getUser(userName);
164:         *       } catch (SecurityException sex) {
165:         *           if ( SecurityException.USER_DOES_NOT_EXISTS.equals(sex.getKeyedMessage()) {
166:         *               // handle USER_DOES_NOT_EXISTS error
167:         *           }
168:         *       }    
169:         * </pre>
170:         * 
171:         * @author <a href="mailto:ate@douma.nu">Ate Douma</a>
172:         * @version $Id: KeyedMessage.java 516448 2007-03-09 16:25:47Z ate $
173:         */
174:        public class KeyedMessage implements  Serializable {
175:            /**
176:             * Static String Field name searched for in the class defining a KeyedMessage containing the default resource bundle
177:             * to use for translation. <br/><em>Note: this Field is looked up using definingClass.getField thus it may also be
178:             * defined in a superclass or interface of the definingClass.</em>
179:             */
180:            public static final String KEYED_MESSAGE_BUNDLE_FIELD_NAME = "KEYED_MESSAGE_BUNDLE";
181:
182:            /**
183:             * Key value for an unresolved KeyMessage.
184:             */
185:            private static final String UNRESOLVED_KEY = KeyedMessage.class
186:                    .getName()
187:                    + ".<unresolved>";
188:
189:            /**
190:             * Map caching default resource bundle names keyed on containingClass
191:             */
192:            private static final HashMap resourceNameMap = new HashMap();
193:
194:            /**
195:             * Default message used when key couldn't be looked up in the default or a specified resource bundle
196:             */
197:            private String message;
198:
199:            /**
200:             * Dynamically derived key based on the definingClass name, postfixed with the static field name of this instance
201:             * </br>
202:             * 
203:             * @see #getKey()
204:             */
205:            private String key;
206:
207:            /**
208:             * Optional message format arguments which can only be set using a derived KeyedMessage using the
209:             * {@link #create(Object[])}method(s).
210:             */
211:            private Object[] arguments;
212:
213:            /**
214:             * The class in which this instance is defined as a static Field.
215:             */
216:            private Class containingClass;
217:
218:            /**
219:             * Indicates if this instance could be {@link #resolve() resolved}.
220:             */
221:            private boolean resolved;
222:
223:            /**
224:             * Constructs a derived KeyedMessage from another KeyedMessage to provide additional message format arguments.
225:             * 
226:             * @see #create(Object[])
227:             * @param source the KeyedMessage to derive this instance from
228:             * @param arguments this instance specific message format arguments
229:             */
230:            protected KeyedMessage(KeyedMessage source, Object[] arguments) {
231:                this .key = source.getKey();
232:                this .message = source.message;
233:                this .resolved = source.resolved;
234:                this .containingClass = source.containingClass;
235:                this .arguments = arguments;
236:            }
237:
238:            /**
239:             * Constructs a new KeyedMessage which will dynamically derive its own {@link #getKey()}.
240:             * 
241:             * @param message the default message used when the {@link #getKey()}could not be found in the default or a
242:             *            specified resource bundle.
243:             */
244:            public KeyedMessage(String message) {
245:                try {
246:                    throw new Exception();
247:                } catch (Exception e) {
248:                    StackTraceElement[] elements = e.getStackTrace();
249:                    if (elements.length >= 2) {
250:                        String containingClassName = elements[1].getClassName();
251:                        try {
252:                            containingClass = Thread.currentThread()
253:                                    .getContextClassLoader().loadClass(
254:                                            containingClassName);
255:                        } catch (ClassNotFoundException e1) {
256:                            key = UNRESOLVED_KEY;
257:                        }
258:                    }
259:                }
260:                this .message = message;
261:            }
262:
263:            private String getResourceName() {
264:                synchronized (resourceNameMap) {
265:                    return (String) resourceNameMap.get(containingClass);
266:                }
267:            }
268:
269:            /**
270:             * @see KeyedMessage
271:             */
272:            private void resolve() {
273:                if (key == null) {
274:                    // search for this instance as a statically declared field in the containingClass to find out the name
275:                    // to use.
276:                    Field[] fields = containingClass.getDeclaredFields();
277:                    for (int i = 0; i < fields.length; i++) {
278:                        try {
279:                            if (fields[i].getType() == this .getClass()
280:                                    && Modifier.isStatic(fields[i]
281:                                            .getModifiers())
282:                                    && fields[i].get(null) == this ) {
283:                                // resolved: save the key
284:                                key = containingClass.getName() + "."
285:                                        + fields[i].getName();
286:                                resolved = true;
287:
288:                                // Now derive the default resource bundle if not already done before
289:                                synchronized (resourceNameMap) {
290:                                    if (getResourceName() == null) {
291:                                        // Find resource bundle name by looking up the statically defined
292:                                        // KEYED_MESSAGE_BUNDLE_FIELD_NAME String field in the containingClass.
293:                                        String resourceName = null;
294:                                        try {
295:                                            Field field = containingClass
296:                                                    .getField(KEYED_MESSAGE_BUNDLE_FIELD_NAME);
297:                                            if (field != null
298:                                                    && field.getType() == String.class
299:                                                    && Modifier.isStatic(field
300:                                                            .getModifiers())) {
301:                                                resourceName = (String) field
302:                                                        .get(null);
303:                                            }
304:                                        } catch (Exception e) {
305:                                        }
306:                                        if (resourceName == null) {
307:                                            // fallback to containingClass name as resource bundle name
308:                                            resourceName = containingClass
309:                                                    .getName();
310:                                        }
311:                                        resourceNameMap.put(containingClass,
312:                                                resourceName);
313:                                    }
314:                                }
315:
316:                                break;
317:                            }
318:                        } catch (Exception e) {
319:                        }
320:                    }
321:                    if (key == null) {
322:                        key = UNRESOLVED_KEY;
323:                    }
324:                }
325:            }
326:
327:            /**
328:             * Formats a message using MessageFormat if arguments are defined, otherwise simply returns the argument.
329:             * 
330:             * @param message the message format
331:             * @return formatted message
332:             */
333:            private String format(String message) {
334:                if (arguments != null && arguments.length > 0) {
335:                    return new MessageFormat(message).format(arguments);
336:                } else {
337:                    return message;
338:                }
339:            }
340:
341:            /**
342:             * Extendable KeyedMessage factory
343:             * 
344:             * @param source the source to copy from
345:             * @param arguments the optional message format arguments
346:             * @return copied instance with new arguments set
347:             */
348:            protected KeyedMessage create(KeyedMessage source,
349:                    Object[] arguments) {
350:                return new KeyedMessage(this , arguments);
351:            }
352:
353:            /**
354:             * Creates a derived KeyedMessage from this instance to provide additional message format arguments. <br/>The new
355:             * instance will be {@link #equals(Object)}to this instance with only different arguments. <br/><br/>Note: the
356:             * argument objects should be lightweight types and preferably Serializable instances
357:             * 
358:             * @param arguments The derived instance specific message format arguments
359:             * @return derived KeyedMessage {@link #equals(Object) equal}to this with its own message format arguments
360:             */
361:            public KeyedMessage create(Object[] arguments) {
362:                return new KeyedMessage(this , arguments);
363:            }
364:
365:            /**
366:             * Simplied version of {@link #create(Object[])}with only one argument
367:             * 
368:             * @param single message format argument
369:             * @see #create(Object[])
370:             * @return derived KeyedMessage {@link #equals(Object) equal}to this with its own message format argument
371:             */
372:            public KeyedMessage create(Object o) {
373:                return create(new Object[] { o });
374:            }
375:
376:            /**
377:             * Simplied version of {@link #create(Object[])}with only two arguments
378:             * 
379:             * @param single message format argument
380:             * @see #create(Object[])
381:             * @return derived KeyedMessage {@link #equals(Object) equal}to this with its own message format arguments
382:             */
383:            public KeyedMessage create(Object o1, Object o2) {
384:                return create(new Object[] { o1, o2 });
385:            }
386:
387:            /**
388:             * Simplied version of {@link #create(Object[])}with only three arguments
389:             * 
390:             * @param single message format argument
391:             * @see #create(Object[])
392:             * @return derived KeyedMessage {@link #equals(Object) equal}to this with its own message format arguments
393:             */
394:            public KeyedMessage create(Object o1, Object o2, Object o3) {
395:                return create(new Object[] { o1, o2, o3 });
396:            }
397:
398:            /**
399:             * Dynamically derived key based on the definingClass name, postfixed with the static field name of this instance.
400:             * <br/><br/>Format: <br/><code>
401:             *     &nbsp;&nbsp;&lt;containingClass.name&gt;.&lt;staticInstanceField.name&gt;
402:             * </code>
403:             * <br/><br/>If this instance couldn't be resolved, generic value UNRESOLVED_KEY will have been set.
404:             * 
405:             * @return derived key
406:             */
407:            public final String getKey() {
408:                resolve();
409:                return key;
410:            }
411:
412:            /**
413:             * Loads and returns a Locale specific default ResourceBundle for this instance. <br/>If this instance couldn't be
414:             * {@link #resolve() resolved}or the bundle couldn't be loadednull will be returned. <br/>The ResourceBundle will
415:             * be loaded using the {@link #containingClass}its ClassLoader.
416:             * 
417:             * @param locale the Locale to lookup the locale specific default ResourceBundle
418:             * @return a Locale specific default ResourceBundle
419:             */
420:            public ResourceBundle getBundle(Locale locale) {
421:                resolve();
422:                if (resolved) {
423:                    try {
424:                        return ResourceBundle.getBundle(getResourceName(),
425:                                locale, containingClass.getClassLoader());
426:                    } catch (RuntimeException e) {
427:                    }
428:
429:                }
430:                return null;
431:            }
432:
433:            /**
434:             * Loads and returns the default ResourceBundle for this instance using the
435:             * {@link CurrentLocale#get() current Locale}.
436:             * 
437:             * @see #getBundle(Locale)
438:             * @see CurrentLocale
439:             * @return the default ResourceBundle for the current Locale
440:             */
441:            public ResourceBundle getBundle() {
442:                return getBundle(CurrentLocale.get());
443:            }
444:
445:            /**
446:             * @return formatted message using the default ResourceBundle using the {@link CurrentLocale current Locale}.
447:             * @see #getBundle()
448:             */
449:            public String getMessage() {
450:                return getMessage(getBundle());
451:            }
452:
453:            /**
454:             * @param bundle a specific ResourceBundle defining this instance {@link #getKey() key}
455:             * @return formatted message using a specific ResourceBundle.
456:             */
457:            public String getMessage(ResourceBundle bundle) {
458:                resolve();
459:                String message = this .message;
460:                if (resolved && bundle != null) {
461:                    try {
462:                        message = bundle.getString(key);
463:                    } catch (RuntimeException e) {
464:                        // ignore: fallback to default message
465:                    }
466:                }
467:                return format(message);
468:            }
469:
470:            /**
471:             * @param locale a specific Locale
472:             * @return formatted message using the default ResourceBundle using a specific Locale.
473:             */
474:            public String getMessage(Locale locale) {
475:                return getMessage(getBundle(locale));
476:            }
477:
478:            /**
479:             * @return the arguments defined for this {@link #create(Object[]) derived}instance
480:             * @see #create(Object[])
481:             */
482:            public Object[] getArguments() {
483:                return arguments;
484:            }
485:
486:            /**
487:             * @param index argument number
488:             * @return an argument defined for this {@link #create(Object[]) derived}instance
489:             */
490:            public Object getArgument(int index) {
491:                return arguments[index];
492:            }
493:
494:            /**
495:             * @return formatted message using the default ResourceBundle using the {@link CurrentLocale current Locale}.
496:             * @see #getMessage()
497:             */
498:            public String toString() {
499:                return getMessage();
500:            }
501:
502:            /**
503:             * @param otherObject KeyedMessage instance to compare with
504:             * @return true only if otherObject is a KeyedMessage {@link create(Object[]) derived}from this instance (or visa
505:             *         versa) and (thus both are) {@link #resolve() resolved}.
506:             * @see #create(Object[])
507:             * @see #resolve()
508:             */
509:            public boolean equals(Object otherObject) {
510:                if (otherObject != null && otherObject instanceof  KeyedMessage) {
511:                    resolve();
512:                    return (resolved && key.equals(((KeyedMessage) otherObject)
513:                            .getKey()));
514:                }
515:                return false;
516:            }
517:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.