KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > util > SortedBundle


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10
11 package org.mmbase.util;
12
13 import java.util.*;
14 import java.lang.reflect.*;
15 import java.text.Collator JavaDoc;
16 import org.mmbase.cache.Cache;
17 import org.mmbase.util.logging.*;
18 import org.mmbase.datatypes.StringDataType;
19
20 /**
21  * A bit like {@link java.util.ResourceBundle} (on which it is based), but it creates
22  * SortedMap's. The order of the entries of the Map can be influenced in tree ways. You can
23  * associate the keys with JAVA constants (and their natural ordering can be used), you can wrap the
24  * keys in a 'wrapper' (which can be of any type, the sole restriction being that there is a
25  * constructor with String argument or of the type of the assiocated JAVA constant if that happened
26  * too, and the natural order of the wrapper can be used (a wrapper of some Number type would be
27  * logical). Finally you can also explicitely specify a {@link java.util.Comparator} if no natural
28  * order is good.
29  *
30  * @author Michiel Meeuwissen
31  * @since MMBase-1.8
32  * @version $Id: SortedBundle.java,v 1.24 2006/07/17 07:19:15 pierre Exp $
33  */

34 public class SortedBundle {
35
36     private static final Logger log = Logging.getLoggerInstance(SortedBundle.class);
37
38     /**
39      * Constant which can be used as an argument for {@link #getResource}
40      */

41     public static final Class JavaDoc NO_WRAPPER = null;
42     /**
43      * Constant which can be used as an argument for {@link #getResource}
44      */

45     public static final Comparator NO_COMPARATOR = null;
46     /**
47      * Constant which can be used as an argument for {@link #getResource}
48      */

49     public static final Map NO_CONSTANTSPROVIDER = null;
50
51     // cache of maps.
52
private static Cache knownResources = new Cache(100) {
53             public String JavaDoc getName() {
54                 return "ConstantBundles";
55             }
56             public String JavaDoc getDescription() {
57                 return "A cache for constant bundles, to avoid a lot of reflection.";
58             }
59         };
60
61     static {
62         knownResources.putCache();
63     }
64
65     /**
66      * You can specify ValueWrapper.class as a value for the wrapper argument. The keys will be objects with natural order of the values.
67      */

68
69     public static class ValueWrapper implements Comparable JavaDoc {
70         private final Object JavaDoc key;
71         private final Object JavaDoc value;
72         private final Comparator com;
73         public ValueWrapper(Object JavaDoc k, Comparable JavaDoc v) {
74             key = k;
75             value = v;
76             com = null;
77         }
78         public ValueWrapper(Object JavaDoc k, Object JavaDoc v, Comparator c) {
79             key = k;
80             value = v;
81             com = c;
82         }
83         public int compareTo(Object JavaDoc o) {
84             ValueWrapper other = (ValueWrapper) o;
85             int result =
86                 com != null ? com.compare(value, other.value) :
87                 ((Comparable JavaDoc) value).compareTo(other.value);
88             if (result != 0) return result;
89             if (key instanceof Comparable JavaDoc) {
90                 return ((Comparable JavaDoc) key).compareTo(other.key);
91             } else {
92                 return 0;
93             }
94         }
95         public boolean equals(Object JavaDoc o) {
96             if (o == this) return true;
97             if (o == null) return false;
98             if (getClass() == o.getClass()) {
99                 ValueWrapper other = (ValueWrapper) o;
100                 return key.equals(other.key) && (value == null ? other.value == null : value.equals(other.value));
101             }
102             return false;
103         }
104         public String JavaDoc toString() {
105             return Casting.toString(key);
106         }
107         public Object JavaDoc getKey() {
108             return key;
109         }
110         /**
111          * @see java.lang.Object#hashCode()
112          */

113         public int hashCode() {
114             int result = 0;
115             result = HashCodeUtil.hashCode(result, key);
116             result = HashCodeUtil.hashCode(result, value);
117             result = HashCodeUtil.hashCode(result, com);
118             return result;
119         }
120     }
121
122
123     /**
124      * @param baseName A string identifying the resource. See {@link java.util.ResourceBundle#getBundle(java.lang.String, java.util.Locale, java.lang.ClassLoader)} for an explanation of this string.
125      *
126      * @param locale the locale for which a resource bundle is desired
127      * @param loader the class loader from which to load the resource bundle
128      * @param constantsProvider A map representing constants for the value. Can be based on a class using {@link #getConstantsProvider(Class)}, then the class's constants ar used to associate with the elements of this resource.
129      * @param wrapper the keys will be wrapped in objects of this type (which must have a
130      * constructor with the right type (String, or otherwise the type of the variable given by the constantsProvider), and must be Comparable.
131      * You could specify e.g. Integer.class if the keys of the
132      * map are meant to be integers. This can be <code>null</code>, in which case the keys will remain unwrapped (and therefore String).
133      * @param comparator the elements will be sorted (by key) using this comparator or by natural key order if this is <code>null</code>.
134      *
135      * @throws NullPointerException if baseName or locale is <code>null</code> (not if loader is <code>null</code>)
136      * @throws MissingResourceException if no resource bundle for the specified base name can be found
137      * @throws IllegalArgumentExcpetion if wrapper is not Comparable.
138      */

139     public static SortedMap getResource(final String JavaDoc baseName, Locale locale, final ClassLoader JavaDoc loader, final Map constantsProvider, final Class JavaDoc wrapper, Comparator comparator) {
140         String JavaDoc resourceKey = baseName + '/' + locale + (constantsProvider == null ? "" : "" + constantsProvider.hashCode()) + "/" + (comparator == null ? "" : "" + comparator.hashCode()) + "/" + (wrapper == null ? "" : wrapper.getName());
141         SortedMap m = (SortedMap) knownResources.get(resourceKey);
142         if (locale == null) locale = LocalizedString.getDefault();
143
144         if (m == null) { // find and make the resource
145
ResourceBundle bundle;
146             if (loader == null) {
147                 bundle = ResourceBundle.getBundle(baseName, locale);
148             } else {
149                 bundle = ResourceBundle.getBundle(baseName, locale, loader);
150             }
151             if (comparator == null && wrapper != null && ! Comparable JavaDoc.class.isAssignableFrom(wrapper)) {
152                 if (wrapper.equals(Boolean JavaDoc.class)) {
153                     // happens in Java < 1.5, because Boolean is no Comparable then.
154
comparator = new Comparator() {
155                             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
156                                 if (o1 instanceof Boolean JavaDoc && o2 instanceof Boolean JavaDoc) {
157                                     return
158                                         o1.equals(Boolean.FALSE) ?
159                                         (o2.equals(Boolean.FALSE) ? 0 : -1) :
160                                         (o2.equals(Boolean.TRUE) ? 1 : 0);
161                                 }
162                                 return o1.hashCode() - o2.hashCode();
163                             }
164                         };
165                 } else {
166                     throw new IllegalArgumentException JavaDoc("Key wrapper " + wrapper + " is not Comparable");
167                 }
168             }
169
170             m = new TreeMap(comparator);
171
172             Enumeration keys = bundle.getKeys();
173             while (keys.hasMoreElements()) {
174                 String JavaDoc bundleKey = (String JavaDoc) keys.nextElement();
175                 Object JavaDoc value = bundle.getObject(bundleKey);
176                 Object JavaDoc key = castKey(bundleKey, value, constantsProvider, wrapper, locale);
177                 if (key == null) continue;
178                 m.put(key, value);
179             }
180             m = Collections.unmodifiableSortedMap(m);
181             knownResources.put(resourceKey, m);
182         }
183         return m;
184     }
185
186
187     public static Object JavaDoc castKey(final String JavaDoc bundleKey, final Object JavaDoc value, final Map constantsProvider, final Class JavaDoc wrapper) {
188         return castKey(bundleKey, value, constantsProvider, wrapper, null);
189     }
190     /**
191      * Casts a key of the bundle to the specified key-type. This type is defined by
192      * the combination of the arguments. See {@link #getResource}.
193      */

194     protected static Object JavaDoc castKey(final String JavaDoc bundleKey, final Object JavaDoc value, final Map constantsProvider, final Class JavaDoc wrapper, final Locale locale) {
195         if (bundleKey == null) return null;
196         Object JavaDoc key;
197         // if the key is numeric then it will be sorted by number
198
//key Double
199

200         Map provider = constantsProvider; // default class (may be null)
201
int lastDot = bundleKey.lastIndexOf('.');
202         if (lastDot > 0) {
203             Class JavaDoc providerClass;
204             String JavaDoc className = bundleKey.substring(0, lastDot);
205             try {
206                 providerClass = Class.forName(className);
207                 provider = getConstantsProvider(providerClass);
208             } catch (ClassNotFoundException JavaDoc cnfe) {
209                 if (log.isDebugEnabled()) {
210                     log.debug("No class found with name " + className + " found from " + bundleKey);
211                 }
212             }
213         }
214
215         if (provider != null) {
216             key = provider.get(bundleKey.toUpperCase());
217             if (key == null) key = bundleKey;
218         } else {
219             key = bundleKey;
220         }
221
222         if (wrapper != null && ! wrapper.isAssignableFrom(key.getClass())) {
223             try {
224                 if (ValueWrapper.class.isAssignableFrom(wrapper)) {
225                     log.debug("wrapper is a valueWrapper");
226                     if (locale == null) {
227                         Constructor c = wrapper.getConstructor(new Class JavaDoc[] { Object JavaDoc.class, Comparable JavaDoc.class });
228                         key = c.newInstance(new Object JavaDoc[] { key, (Comparable JavaDoc) value});
229                     } else {
230                         Constructor c = wrapper.getConstructor(new Class JavaDoc[] { Object JavaDoc.class, Object JavaDoc.class, Comparator.class });
231                         Collator JavaDoc comp = Collator.getInstance(locale);
232                         comp.setStrength(Collator.PRIMARY);
233                         key = c.newInstance(new Object JavaDoc[] { key, value, comp});
234                     }
235                 } else if (Number JavaDoc.class.isAssignableFrom(wrapper)) {
236                     if (key instanceof String JavaDoc) {
237                         if (StringDataType.DOUBLE_PATTERN.matcher((String JavaDoc) key).matches()) {
238                             key = Casting.toType(wrapper, key);
239                         }
240                     } else {
241                         key = Casting.toType(wrapper, key);
242                         log.debug("wrapper is a Number, that can simply be cast " + value + " --> " + key + "(" + wrapper + ")");
243                     }
244                 } else if (Boolean JavaDoc.class.isAssignableFrom(wrapper)) {
245                     if (key instanceof String JavaDoc) {
246                         if (StringDataType.BOOLEAN_PATTERN.matcher((String JavaDoc) key).matches()) {
247                             key = Casting.toType(wrapper, key);
248                         }
249                     } else {
250                         key = Casting.toType(wrapper, key);
251                         log.debug("wrapper is a Boolean, that can simply be cast " + value + " --> " + key + "(" + wrapper + ")");
252                     }
253
254                 } else {
255                     log.debug("wrapper is unrecognized, suppose constructor " + key.getClass());
256                     Constructor c = wrapper.getConstructor(new Class JavaDoc[] {key.getClass()});
257                     key = c.newInstance(new Object JavaDoc[] { key });
258                 }
259             } catch (NoSuchMethodException JavaDoc nsme) {
260                 log.warn(nsme.getClass().getName() + ". Could not convert " + key.getClass().getName() + " " + key + " to " + wrapper.getName() + " : " + nsme.getMessage() + " locale " + locale);
261             } catch (SecurityException JavaDoc se) {
262                 log.error(se.getClass().getName() + ". Could not convert " + key.getClass().getName() + " " + key + " to " + wrapper.getName() + " : " + se.getMessage());
263              } catch (InstantiationException JavaDoc ie) {
264                 log.error(ie.getClass().getName() + ". Could not convert " + key.getClass().getName() + " " + key + " to " + wrapper.getName() + " : " + ie.getMessage());
265              } catch (InvocationTargetException ite) {
266                 log.debug(ite.getClass().getName() + ". Could not convert " + key.getClass().getName() + " " + key + " to " + wrapper.getName() + " : " + ite.getMessage());
267              } catch (IllegalAccessException JavaDoc iae) {
268                 log.error(iae.getClass().getName() + ". Could not convert " + key.getClass().getName() + " " + key + " to " + wrapper.getName() + " : " + iae.getMessage());
269              }
270         }
271         return key;
272     }
273
274     /**
275      * Returns a (serializable) Map representing all accessible static public members of given class (so, all constants).
276      * @since MMBase-1.8
277      */

278     public static HashMap getConstantsProvider(Class JavaDoc clazz) {
279         if (clazz == null) return null;
280         HashMap map = new HashMap();
281         fillConstantsProvider(clazz, map);
282         return map;
283     }
284     private static void fillConstantsProvider(Class JavaDoc clazz, HashMap map) {
285         while(clazz != null) {
286             Field[] fields = clazz.getDeclaredFields();
287             for (int i = 0 ; i < fields.length; i++) {
288                 Field constant = fields[i];
289                 if (Modifier.isStatic(constant.getModifiers())) {
290                     String JavaDoc key = constant.getName().toUpperCase();
291                     if (! map.containsKey(key)) { // super should not override this.
292
try {
293                             Object JavaDoc value = constant.get(null);
294                             map.put(key, value);
295                         } catch (IllegalAccessException JavaDoc ieae) {
296                             log.debug("The java constant with name " + key + " is not accessible");
297                         }
298                     }
299                 }
300             }
301             Class JavaDoc[] interfaces = clazz.getInterfaces();
302             for (int i = 0 ; i < interfaces.length; i ++) {
303                 fillConstantsProvider(interfaces[i], map);
304             }
305             clazz = clazz.getSuperclass();
306         }
307     }
308 }
309
Popular Tags