001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package mx4j.tools.i18n;
010:
011: import java.util.Collection;
012: import java.util.Collections;
013: import java.util.Enumeration;
014: import java.util.HashMap;
015: import java.util.HashSet;
016: import java.util.Locale;
017: import java.util.Map;
018: import java.util.MissingResourceException;
019: import java.util.ResourceBundle;
020: import java.util.Set;
021: import java.util.StringTokenizer;
022: import javax.management.MBeanAttributeInfo;
023: import javax.management.MBeanConstructorInfo;
024: import javax.management.MBeanInfo;
025: import javax.management.MBeanOperationInfo;
026: import javax.management.MBeanParameterInfo;
027: import javax.management.NotCompliantMBeanException;
028: import javax.management.StandardMBean;
029:
030: /**
031: * An extension of StandardMBean to support internationalization. <p>
032: * <p/>
033: * The I18N information is taken from a property bundle named MyImplMBeanResources
034: * where "MyImpl" is the fully qualified class implementing the MBean. <p>
035: * These bundles are nested following the class hierachy of the <b>implementation</b>
036: * class. This means that a superclass of the real implementing class can
037: * define the resource bundle for the common attributes and operations.
038: * <p/>
039: * The resource bundle naming rules defined by {@link java.util.ResourceBundle}
040: * are used; in particular : <UL>
041: * <LI>If a <b>class</b> called MyPackage.MyImplMBeanResources_localInfo exists it is used (programmatic methd)
042: * <LI>Otherwise the <b>file</b> called MyPackage.MyImplMBeanResources_localInfo.properties is used.
043: * </UL>
044: * <p/>
045: * localInfo consists of one or more sections of "language_country_variant" (eg en_GB or fr_FR).
046: * <p/>
047: * The locale to be used is determined by one of the following mechanisms (in this order) <UL>
048: * <LI>The locale object explicitly passed to the constructor (if not null)
049: * <LI>The static method {@link #setDefaultLocale}
050: * <LI>The system property "mx4j.descriptionLocale"
051: * <LI>The current system default locale
052: * </UL>
053: * <p/>
054: * The bundle should contain keys as described below :
055: * <H2>Global bean description</H2>
056: * The global bean description is given by the single key "descr": <pre>
057: * descr=The MBean Description
058: * </pre>
059: * <p/>
060: * <H2>Attributes</H2>
061: * Attribute desciptions are given by keys of form "attr.Name" where Name
062: * is the attribute name (the method name minus the get/set prefix) : <pre>
063: * attr.Counter=The counter
064: * </pre>
065: * <p/>
066: * <H2>Constructors</H2>
067: * <H3>Non ambiguous case</H3>
068: * All constructors having a different <b>number</b> of arguments may be described in this way: <pre>
069: * cons.N=desciption of constructor N
070: * cons.N.param.1=Description of first parameter of constructor N
071: * cons.N.paramName.1=paramName1
072: * cons.N.param.2=Description of first parameter of constructor N
073: * cons.N.paramName.2=paramName2
074: * </pre>
075: * Where N is a sequential number starting at one.
076: * <p/>
077: * <H3>Ambiguous case</H3>
078: * Where several constructors exist with the same number of arguments an explicit
079: * signature must be given. The signature is a comma separated list of class descriptions
080: * (as returned by {@link java.lang.Class#getName} and has the key cons.N.sig : <pre>
081: * cons.N.sig=int,java.lang.Object
082: * cons.N.param.1=The int parameter
083: * cons.N.param.2=The Object parameter
084: * </pre>
085: * <p/>
086: * <H2>Operations</H2>
087: * <H3>No overloading</H3>
088: * When no overloaded versions of an operation exist (same method name but different parameters)
089: * the simplest case shown below can be used : <pre>
090: * op.<I>operationName</I>=The description
091: * op.<I>operationName</I>.param.1=The first parameter
092: * op.<I>operationName</I>.paramName.1=param1
093: * </pre>
094: * <p/>
095: * <H3>Non ambiguous overloading case</H3>
096: * When operation overloading is used but the overloaded versions differ in the number
097: * of parameters the format below can be used : <pre>
098: * op.<I>operationName</I>.1=The first version of operationName
099: * op.<I>operationName</I>.1.param.1=parameter for first version
100: * op.<I>operationName</I>.1.paramName.1=param1
101: * op.<I>operationName</I>.2=The second version of operationName
102: * op.<I>operationName</I>.2.paramName.1=param1
103: * op.<I>operationName</I>.2.param.1=first parameter for second version
104: * op.<I>operationName</I>.2.param.2=second parameter for second version
105: * op.<I>operationName</I>.2.paramName.2=param2
106: * </pre>
107: * <p/>
108: * <H3>Ambiguous overloading case</H3>
109: * When operations with the same name have the same number of arguments an explicit
110: * signature must be used : <pre>
111: * op.<I>operationName</I>.1.sig=int
112: * op.<I>operationName</I>.1=The first version of operationName (takes int)
113: * op.<I>operationName</I>.1.param.1=parameter for first version
114: * op.<I>operationName</I>.1.paramName.1=param1
115: * op.<I>operationName</I>.2.sig=java.lang.Object
116: * op.<I>operationName</I>.2=The second version of operationName (take Object)
117: * op.<I>operationName</I>.2.paramName.1=param1
118: * op.<I>operationName</I>.2.param.1=first parameter for second version
119: * </pre>
120: * <p/>
121: * <H2>Restrictions</H2>
122: * Parameter names must only contain characters allowed in a Java identifier
123: * (in particular spaces are <b>not</b> allowed). This is required by the JMX specifications.
124: * No such restrictions exist for the other descriptions.
125: * <p/>
126: * <H2>Behaviour with missing data</H2>
127: * If no resource bunde exists for the MBean a java.util.MissingResourceException
128: * will be thrown by the constructor. <p>
129: * <p/>
130: * If the resouce bundle is found but the bean description, constructor description or
131: * parameter name is missing the String "??(key)" will be returned instead (eg
132: * "??(op.myOperation)". <p>
133: * <p/>
134: * If a paramName key is missing (for constructor or operation) the version normally
135: * given by StandardMBean is used (generally "pN").<p>
136: * <p/>
137: * If a non ambiguous description cannot be determined the fixed (non translatable)
138: * descriptions "ambiguous constructor", "parameter for ambiguous constructor",
139: * "ambiguous operation", "parameter for ambiguous operation" are returned.
140: */
141: public class I18NStandardMBean extends StandardMBean {
142: private static final String IDPROP_DEFAULT_LOCALE = "mx4j.descriptionLocale";
143: private static final String RESOURCE_SUFFIX = "MBeanResources";
144: private static final String KEY_DESCR = "descr";
145: private static final String KEY_CONS = "cons";
146: private static final String KEY_ATTR = "attr";
147: private static final String KEY_OP = "op";
148: private static final String KEY_PARAM = "param";
149: private static final String KEY_PARAM_NAME = "paramName";
150: private static final String KEY_SIG = "sig";
151:
152: private static Locale g_defaultLocale = null;
153:
154: private NestedResourceBundle m_bundle;
155: private Map m_mapConstructorSignatureToResourceIndex;
156: private Map m_mapConstructorParamCountToResourceIndex;
157: private Map m_mapConstructorToResourceIndex = new HashMap();
158: private Map m_mapOperationNameToSignatures = new HashMap();
159: private Map m_mapOperationNameToParamCounts = new HashMap();
160: private Set m_setAmbiguousConstructors = new HashSet();
161: private Set m_setAmbiguousOperations = new HashSet();
162:
163: /**
164: * Makes an I18NStandardMBean for the default locale with a separate implementation class.
165: *
166: * @see javax.management.StandardMBean#StandardMBean(java.lang.Object, java.lang.Class)
167: */
168: public I18NStandardMBean(Object implementation, Class mbeanInterface)
169: throws NotCompliantMBeanException {
170: this (implementation, mbeanInterface, null);
171: }
172:
173: /**
174: * Makes an I18NStandardMBean for the given locale with a separate implementation class.
175: *
176: * @see javax.management.StandardMBean#StandardMBean(java.lang.Object, java.lang.Class)
177: */
178: public I18NStandardMBean(Object implementation,
179: Class mbeanInterface, Locale locale)
180: throws NotCompliantMBeanException {
181: super (implementation, mbeanInterface);
182: setupBundle(implementation, locale);
183: }
184:
185: /**
186: * Makes a I18NStandardMBean for the default locale implemented by a subclass.
187: *
188: * @see javax.management.StandardMBean#StandardMBean(java.lang.Class)
189: */
190: protected I18NStandardMBean(Class mbeanInterface)
191: throws NotCompliantMBeanException {
192: super (mbeanInterface);
193: setupBundle(this , null);
194: }
195:
196: /**
197: * Makes a I18NStandardMBean for the given locale implemented by a subclass.
198: *
199: * @see javax.management.StandardMBean#StandardMBean(java.lang.Class)
200: */
201: protected I18NStandardMBean(Class mbeanInterface, Locale locale)
202: throws NotCompliantMBeanException {
203: super (mbeanInterface);
204: setupBundle(this , locale);
205: }
206:
207: private void setupBundle(Object implementation, Locale locale) {
208: // calculate the effective locale:
209: if (locale == null) {
210: locale = g_defaultLocale;
211: }
212: if (locale == null) {
213: locale = getLocaleFromSystemProperties();
214: }
215:
216: // create bundle
217: NestedResourceBundle cur = null;
218: MissingResourceException ex = null;
219: for (Class c = implementation.getClass(); c != null; c = c
220: .getSuperclass()) {
221: String bundleName = c.getName() + RESOURCE_SUFFIX;
222: try {
223: ResourceBundle b = ResourceBundle.getBundle(bundleName,
224: locale);
225: NestedResourceBundle nb = new NestedResourceBundle(b);
226: if (cur == null) {
227: m_bundle = nb;
228: } else {
229: cur.setParent(nb);
230: }
231: cur = nb;
232: } catch (MissingResourceException e) {
233: if (m_bundle == null)
234: ex = e; // save first exception
235: }
236: }
237: if (m_bundle == null) {
238: ex.fillInStackTrace();
239: throw ex;
240: }
241: }
242:
243: // Obtain the default locale from system properties
244: private Locale getLocaleFromSystemProperties() {
245: Locale locale = Locale.getDefault();
246: String stdLocale = System.getProperty(IDPROP_DEFAULT_LOCALE);
247: if (stdLocale != null && stdLocale.length() > 0) {
248: StringTokenizer st = new StringTokenizer(stdLocale, "_");
249: switch (st.countTokens()) {
250: case 2:
251: locale = new Locale(st.nextToken(), st.nextToken());
252: break;
253: case 3:
254: locale = new Locale(st.nextToken(), st.nextToken(), st
255: .nextToken());
256: break;
257: default:
258: throw new IllegalArgumentException("Invalid locale in "
259: + IDPROP_DEFAULT_LOCALE + ":" + stdLocale);
260: }
261: }
262: return locale;
263: }
264:
265: /**
266: * Set the locale which will be used for future I18NStandardMBeans. <p>
267: * The locale specified can be overridden on a per class basis via the
268: * constructors but overrides other means of setting the Locale (system properties). <p>
269: * <p/>
270: * Changing the locale has no effect on already constructed MBeans.
271: *
272: * @param locale the Locale for future MBeans
273: */
274: public static void setDefaultLocale(Locale locale) {
275: g_defaultLocale = locale;
276: }
277:
278: /**
279: * Initialise internal data structures. <p>
280: * This method is always called first during getMBeanInfo processing.
281: * We use this to avoid keeping all our internal Maps in memory too long.
282: *
283: * @see javax.management.StandardMBean#getCachedMBeanInfo
284: */
285: protected MBeanInfo getCachedMBeanInfo() {
286: MBeanInfo info = super .getCachedMBeanInfo();
287: if (info == null) {
288: // only setup if we are going to be called!
289: m_mapConstructorToResourceIndex = new HashMap();
290: m_mapOperationNameToSignatures = new HashMap();
291: m_mapOperationNameToParamCounts = new HashMap();
292: m_setAmbiguousConstructors = new HashSet();
293: m_setAmbiguousOperations = new HashSet();
294: m_mapConstructorSignatureToResourceIndex = getSignatureMap(KEY_CONS);
295: m_mapConstructorParamCountToResourceIndex = getParamCountMap(KEY_CONS);
296: }
297: return info;
298: }
299:
300: /**
301: * Once the MBeanInfo has been obtained discard our caches.
302: *
303: * @see javax.management.StandardMBean#cacheMBeanInfo(javax.management.MBeanInfo)
304: */
305: protected void cacheMBeanInfo(MBeanInfo info) {
306: super .cacheMBeanInfo(info);
307: m_mapConstructorToResourceIndex = null;
308: m_mapOperationNameToSignatures = null;
309: m_mapOperationNameToParamCounts = null;
310: m_setAmbiguousConstructors = null;
311: m_setAmbiguousOperations = null;
312: m_mapConstructorSignatureToResourceIndex = null;
313: m_mapConstructorParamCountToResourceIndex = null;
314: }
315:
316: /*
317: * Initialises internal structures based on available constructors. <p>
318: * Return value is supplied by superclass.<p>
319: *
320: * For all the constructors :<ul>
321: * <li>Create a map of MBeanConstructorInfo=>resource bundle index for explicit signatures
322: * <li>Create list of "ambiguous" constructors based on number of arguments.
323: * </ul>
324: * Note we assume that this metthod will be called BEFORE the constructor related
325: * getDesciption methods. The spec does not say anything about this.
326: */
327: protected MBeanConstructorInfo[] getConstructors(
328: MBeanConstructorInfo[] cstrs, Object impl) {
329: Map argCountToCstr = new HashMap();
330: for (int i = 0; i < cstrs.length; i++) {
331: MBeanConstructorInfo ci = cstrs[i];
332: MBeanParameterInfo[] params = ci.getSignature();
333:
334: // update potentially ambiguous constructors (same number of arguments)
335: Integer count = new Integer(params.length);
336: Object first = argCountToCstr.get(count);
337: if (first != null) {
338: // already have a constructor with this number of args
339: m_setAmbiguousConstructors.add(first); // Set so no duplication
340: m_setAmbiguousConstructors.add(ci);
341: // this one is ambiguous too
342: } else {
343: argCountToCstr.put(count, ci);
344: }
345:
346: // update signature=>resource index mapping (if explicit signature provided)
347: String sig = makeSignatureString(params);
348: Integer idx = (Integer) m_mapConstructorSignatureToResourceIndex
349: .get(sig);
350: if (idx != null) {
351: m_mapConstructorToResourceIndex.put(ci, idx);
352: }
353: }
354: return super .getConstructors(cstrs, impl);
355: }
356:
357: /**
358: * Obtain global description for MBean. <p>
359: * Taken from "descr" key in resource bundle. <p>
360: * <p/>
361: * Also performs internal initialisations requiring the MBeanInfo obtained
362: * by introspection. Therefore the superclass must call this method BEFORE
363: * the other hooks.
364: *
365: * @see javax.management.StandardMBean#getDescription(javax.management.MBeanInfo)
366: */
367: protected String getDescription(MBeanInfo info) {
368: findAmbiguousOperations(info); // assume called first
369: return getValueFromBundle(KEY_DESCR);
370: }
371:
372: /**
373: * Obtain the constructor description. <p>
374: * Taken from "cons.N" key in resource bundle. <p>
375: * <p/>
376: * Maybe "ambiguous constructor" if correct index cannot be determined by
377: * an explicit signature or parameter counts.
378: *
379: * @see javax.management.StandardMBean#getDescription(javax.management.MBeanConstructorInfo)
380: */
381: protected String getDescription(MBeanConstructorInfo cstr) {
382: int idx = getConstructorIndex(cstr);
383: if (idx < 1) {
384: return "ambiguous constructor";
385: }
386: return getValueFromBundle(KEY_CONS + "." + idx);
387: }
388:
389: /**
390: * Obtain the constructor parameter description. <p>
391: * Taken from "cons.N.param.<I>seq</I>" key in resource bundle. <p>
392: * <p/>
393: * Maybe "parameter for ambiguous constructor" if correct index cannot be determined by
394: * an explicit signature or parameter counts.
395: *
396: * @see javax.management.StandardMBean#getDescription(javax.management.MBeanConstructorInfo, javax.management.MBeanParameterInfo, int)
397: */
398: protected String getDescription(MBeanConstructorInfo cstr,
399: MBeanParameterInfo param, int seq) {
400: int idx = getConstructorIndex(cstr);
401: if (idx < 1) {
402: return "parameter for ambiguous constructor";
403: }
404: return getValueFromBundle(KEY_CONS + "." + idx + ".param."
405: + (seq + 1));
406: }
407:
408: /**
409: * Obtain constructor parameter name. <p>
410: * Taken from "cons.N.paramName.<I>seq</I>" key in resource bundle. <p>
411: * <p/>
412: * If this key does not exist or if the correct index N cannot be determined by
413: * an explicit signature or parameter counts the superclass method is called.
414: *
415: * @see javax.management.StandardMBean#getParameterName(javax.management.MBeanConstructorInfo, javax.management.MBeanParameterInfo, int)
416: */
417: protected String getParameterName(MBeanConstructorInfo cstr,
418: MBeanParameterInfo param, int seq) {
419: int idx = getConstructorIndex(cstr);
420: String name = null;
421: if (idx >= 1) {
422: name = getValueOrNullFromBundle(KEY_CONS + "." + idx
423: + ".paramName." + (seq + 1));
424: }
425: if (name == null) {
426: name = super .getParameterName(cstr, param, seq);
427: }
428: return name;
429: }
430:
431: /**
432: * Obtain the attribute description. <p>
433: * Taken from the "attr.<I>attributeName</I>" key in resource bundle.
434: *
435: * @see javax.management.StandardMBean#getDescription(javax.management.MBeanAttributeInfo)
436: */
437: protected String getDescription(MBeanAttributeInfo attr) {
438: return getValueFromBundle(KEY_ATTR + "." + attr.getName());
439: }
440:
441: /**
442: * Obtain the operation description. <p>
443: * Taken from the "op.<I>operationName</I>.N" or the "op.<I>operationName</I>"
444: * key in the resource bundle. <p>
445: * May be "ambiguous operation" if the correct key cannot be determined by
446: * signature or parameter counts.
447: *
448: * @see javax.management.StandardMBean#getDescription(javax.management.MBeanOperationInfo)
449: */
450: protected String getDescription(MBeanOperationInfo op) {
451: try {
452: return getValueFromBundle(getOperationKey(op));
453: } catch (IllegalStateException e) {
454: return e.getMessage();
455: }
456: }
457:
458: /**
459: * Obtain the operation parameter description. <p>
460: * Taken from the "op.<I>operationName</I>.N.param.M" or the "op.<I>operationName</I>.param"
461: * key in the resource bundle. <p>
462: * May be "parameter for ambiguous operation" if the correct key cannot be determined by
463: * signature or parameter counts.
464: *
465: * @see javax.management.StandardMBean#getDescription(javax.management.MBeanOperationInfo, javax.management.MBeanParameterInfo, int)
466: */
467: protected String getDescription(MBeanOperationInfo op,
468: MBeanParameterInfo param, int seq) {
469: try {
470: return getValueFromBundle(getOperationKey(op) + "."
471: + KEY_PARAM + "." + (seq + 1));
472: } catch (IllegalStateException e) {
473: return "parameter for " + e.getMessage();
474: }
475: }
476:
477: /**
478: * Obtain operation parameter name. <p>
479: * Taken from the "op.<I>operationName</I>.N.paramName.M" or the "op.<I>operationName.paramName</I>.M"
480: * key in the resource bundle. <p>
481: * <p/>
482: * If this key does not exist or if the correct index N cannot be determined by
483: * an explicit signature or parameter counts the superclass method is called.
484: *
485: * @see javax.management.StandardMBean#getParameterName(javax.management.MBeanOperationInfo, javax.management.MBeanParameterInfo, int)
486: */
487: protected String getParameterName(MBeanOperationInfo op,
488: MBeanParameterInfo param, int seq) {
489: String name = null;
490: try {
491: name = getValueOrNullFromBundle(getOperationKey(op) + "."
492: + KEY_PARAM_NAME + "." + (seq + 1));
493: } catch (IllegalStateException e) {
494: }
495:
496: if (name == null) {
497: name = super .getParameterName(op, param, seq);
498: }
499: return name;
500: }
501:
502: /*
503: * Obtain 1 based index of constructor in resource bundle.
504: * First look for a signature match (.sig in bundle)
505: * If not found and constuctor is potentially ambiguous (another constructor with the same number of params exists) return -1
506: * If not found try a parameter number match.
507: * If parameter number match is ambiguous return -1
508: * If no match found return 0
509: */
510: private int getConstructorIndex(MBeanConstructorInfo cons) {
511: Integer idx = (Integer) m_mapConstructorToResourceIndex
512: .get(cons);
513: if (idx != null) {
514: return idx.intValue();
515: }
516:
517: // do multiple constuctors with the same arg count exist?
518: if (m_setAmbiguousConstructors.contains(cons))
519: return -1;
520:
521: // no signature match - try using parameter count
522: int nbParams = cons.getSignature().length;
523: idx = (Integer) m_mapConstructorParamCountToResourceIndex
524: .get(new Integer(nbParams));
525: if (idx != null) {
526: return idx.intValue();
527: }
528: return 0;
529: }
530:
531: /*
532: * Obtain the root bundle key for the given operation.
533: * If a matching signature entry exists for this operation
534: * is of form : "op.operationName.N"
535: * otherwise it is of form "op.operationName"
536: * where N is the index of the matching signature.
537: * If the operation is ambiguous throw an IllegalStateException.
538: */
539: private String getOperationKey(MBeanOperationInfo op) {
540: String operationName = op.getName();
541:
542: // lookup by signature
543: Map sigMap = getOperationSignatureMap(operationName);
544: MBeanParameterInfo[] params = op.getSignature();
545: String sig = makeSignatureString(params);
546: Integer idx = (Integer) sigMap.get(sig);
547:
548: StringBuffer sbRet = new StringBuffer(KEY_OP + ".");
549: sbRet.append(operationName);
550:
551: if (idx == null) {
552: if (m_setAmbiguousOperations.contains(op)) {
553: throw new IllegalStateException("ambiguous operation");
554: }
555:
556: // no direct signature mapping, try matching by parameter counts
557: Map countMap = getOperationParamCountMap(operationName);
558: idx = (Integer) countMap.get(new Integer(params.length));
559: if (idx != null && idx.intValue() < 1) {
560: throw new IllegalStateException("ambiguous operation");
561: }
562: }
563:
564: if (idx != null) {
565: sbRet.append(".");
566: sbRet.append(idx);
567: }
568: return sbRet.toString();
569: }
570:
571: /*
572: * Initialise the set m_setAmbiguousOperations with those operations
573: * that have the same name and same number of parameters.
574: */
575: private void findAmbiguousOperations(MBeanInfo info) {
576: // obtain potentially ambiguous operations (same name, same number parameters)
577: MBeanOperationInfo[] ops = info.getOperations();
578: Map mapNameToArgCountMap = new HashMap();
579: for (int i = 0; i < ops.length; i++) {
580: MBeanOperationInfo op = ops[i];
581: String name = op.getName();
582: Map argCountToOp = (Map) mapNameToArgCountMap.get(name);
583: if (argCountToOp == null) {
584: argCountToOp = new HashMap();
585: mapNameToArgCountMap.put(name, argCountToOp);
586: }
587:
588: Integer count = new Integer(op.getSignature().length);
589: Object first = argCountToOp.get(count);
590: if (first != null) {
591: // already have an operation with this number of args
592: m_setAmbiguousOperations.add(first); // Set so no duplication
593: m_setAmbiguousOperations.add(op); // this one is ambiguous too
594: } else {
595: argCountToOp.put(count, op);
596: }
597: }
598: }
599:
600: /*
601: * Obtain Map of operation signature=>resource bundle index.
602: * Use lazy instantiation and caching.
603: * The entries in resource bundle have form :
604: * op.operationName.1.sig=xxx
605: * op.operationName.2.sig=yyy
606: *
607: * The above example would give xxx=>1, yyy=>2
608: */
609: private Map getOperationSignatureMap(String operationName) {
610: // look up in cache
611: Map m = (Map) m_mapOperationNameToSignatures.get(operationName);
612: if (m != null) {
613: return m;
614: }
615:
616: // construct map
617: m = getSignatureMap(KEY_OP + "." + operationName);
618: m_mapOperationNameToSignatures.put(operationName, m); // cache
619: return m;
620: }
621:
622: /*
623: * Obtain Map of parameter count =>resource bundle index for the given operation
624: * Use lazy instantiation and caching.
625: */
626: private Map getOperationParamCountMap(String operationName) {
627: // look up in cache
628: Map m = (Map) m_mapOperationNameToParamCounts
629: .get(operationName);
630: if (m != null) {
631: return m;
632: }
633:
634: // construct map
635: m = getParamCountMap(KEY_OP + "." + operationName);
636: m_mapOperationNameToParamCounts.put(operationName, m); // cache
637: return m;
638: }
639:
640: /*
641: * Obtain a Map of parameter count => Integer index from resource bundle
642: * The entries in resource bundle have form :
643: * prefix.1.param.1=xxx
644: * prefix.1.param.2=yyy
645: * prefix.2.param.1=zzz
646: *
647: * The above example would give 2=>1, 1=>2 (index1 has 2 parameter, index 2 has 1 parameter)
648: * If there are duplicate parameter counts map to -1
649: */
650: private Map getParamCountMap(String prefix) {
651: int nb;
652: Map m = new HashMap();
653:
654: for (int i = 1;; i++) {
655: String key = prefix + "." + i;
656: String sig = getValueOrNullFromBundle(key);
657: if (sig == null) {
658: break;
659: }
660: nb = 0;
661: for (int j = 1;; j++) {
662: key = prefix + "." + i + "." + KEY_PARAM + "." + j;
663: if (getValueOrNullFromBundle(key) != null) {
664: nb = j;
665: } else {
666: break;
667: }
668: }
669: Integer nbObj = new Integer(nb);
670: int idx = m.containsKey(nbObj) ? -1 : i;
671: m.put(nbObj, new Integer(idx));
672: }
673: return m;
674: }
675:
676: /*
677: * Create a map of signature string=>Integer index from resource bundle.
678: * The entries in resource bundle have form :
679: * prefix.1.sig=signature1
680: * prefix.2.sig=signature2
681: * ..
682: * The list stops at the first non existant index.
683: * The signatures are comma separated types of the form returned by
684: * Class.getName(), eg: java.lang.Object,Z,[Z;
685: */
686: private Map getSignatureMap(String prefix) {
687: Map m = new HashMap();
688: for (int i = 1;; i++) {
689: String key = prefix + "." + i + "." + KEY_SIG;
690: String sig = getValueOrNullFromBundle(key);
691: if (sig == null) {
692: break;
693: }
694: m.put(sig, new Integer(i));
695: }
696: return m;
697: }
698:
699: // create a comma separated list of signatures.
700: private String makeSignatureString(MBeanParameterInfo[] params) {
701: StringBuffer sb = new StringBuffer();
702: for (int i = 0; i < params.length; i++) {
703: if (i > 0) {
704: sb.append(",");
705: }
706: sb.append(params[i].getType());
707: }
708: return sb.toString();
709: }
710:
711: private String getValueFromBundle(String key) {
712: String value;
713: try {
714: value = m_bundle.getString(key);
715: } catch (MissingResourceException e) {
716: value = "??(" + key + ")";
717: }
718: return value;
719: }
720:
721: private String getValueOrNullFromBundle(String key) {
722: String value = null;
723: try {
724: value = m_bundle.getString(key);
725: } catch (MissingResourceException e) {
726: }
727: return value;
728: }
729:
730: private static class NestedResourceBundle extends ResourceBundle {
731: private ResourceBundle _impl;
732:
733: NestedResourceBundle(ResourceBundle impl) {
734: _impl = impl;
735: }
736:
737: void setParent(NestedResourceBundle parent) {
738: super .setParent(parent);
739: }
740:
741: /* (non-Javadoc)
742: * @see java.util.ResourceBundle#handleGetObject(java.lang.String)
743: */
744: protected Object handleGetObject(String key) {
745: try {
746: return _impl.getString(key);
747: } catch (MissingResourceException e) {
748: return null; // Resource bundle will ask parent
749: }
750: }
751:
752: /* (non-Javadoc)
753: * @see java.util.ResourceBundle#getKeys()
754: */
755: public Enumeration getKeys() {
756: // obtain union of all keys in bundle hierachy (no doublons)
757: HashSet hs = new HashSet();
758: addEnumeration(hs, _impl.getKeys());
759: if (parent != null) {
760: addEnumeration(hs, parent.getKeys());
761: }
762: return Collections.enumeration(hs);
763: }
764:
765: private void addEnumeration(Collection col, Enumeration e) {
766: while (e.hasMoreElements()) {
767: col.add(e.nextElement());
768: }
769: }
770:
771: }
772:
773: }
|