001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util;
011:
012: import java.util.*;
013: import java.io.*;
014: import org.w3c.dom.*;
015: import org.mmbase.bridge.*;
016: import org.mmbase.bridge.Node;
017: import org.mmbase.bridge.util.Queries;
018: import org.mmbase.bridge.util.xml.query.*;
019: import org.mmbase.util.xml.DocumentSerializable;
020: import org.mmbase.util.xml.DocumentReader;
021: import org.mmbase.util.logging.*;
022:
023: /**
024: * These factories can produce Collections based on a Locale (The {@link #get} method is
025: * essential). The other methods besides get are methods to define these lists. There are two ways
026: * to add entries to the produced collections. The first one is to explicitely add them, using the
027: * {@link #add} method. This gives precise control, and the collections can have different orders
028: * for different languages. The size of the collections are always the same, so if for a certain
029: * locale less entries are added, these are completed with the unused keys. If for a certain locale
030: * <em>no</em> entries are added, it will behave itself like as the default locale of {@link
031: * LocalizedString#getDefault}.
032: *
033: * It is also possible to add entire 'bundles'. For this use {@link #addBundle}. When a Collection instance
034: * for a certain Locale is requested these informations are used to call {@link
035: * SortedBundle#getResource}.
036: *
037: * It is possible to mix both methods, so having an enumeration partially defined by a bundle,
038: * partially by explicit values, though this is not recommended.
039: *
040: * @author Michiel Meeuwissen
041: * @version $Id: LocalizedEntryListFactory.java,v 1.48 2008/02/03 17:33:57 nklasens Exp $
042: * @since MMBase-1.8
043: */
044: public class LocalizedEntryListFactory<C> implements Serializable,
045: Cloneable {
046:
047: private static final Logger log = Logging
048: .getLoggerInstance(LocalizedEntryListFactory.class);
049: private static final long serialVersionUID = 1L; // increase this if object serialization changes (which we shouldn't do!)
050:
051: private int size = 0; // number of explicitely added keys
052: private boolean usesCloud = false;
053:
054: // we don't use interfaces here, because of Serializability
055: private static class LocalizedEntry implements Serializable,
056: PublicCloneable {
057: private static final long serialVersionUID = 1L;
058: ArrayList entries = new ArrayList(); // List of Map.Entries, Bundles and DocumentSerializable's
059: ArrayList<Serializable> unusedKeys = new ArrayList<Serializable>(); // List of unused keys;
060:
061: public Object clone() {
062: try {
063: LocalizedEntry clone = (LocalizedEntry) super .clone();
064: Iterator<PublicCloneable> i = clone.entries.iterator();
065: clone.entries = new ArrayList();
066: while (i.hasNext()) {
067: clone.entries.add(i.next().clone());
068: }
069: clone.unusedKeys = (ArrayList) unusedKeys.clone();
070: return clone;
071: } catch (CloneNotSupportedException cnse) {
072: log.error(cnse);
073: return new LocalizedEntry();
074: }
075: }
076:
077: public String toString() {
078: return "entries:" + entries + "uu:" + unusedKeys;
079: }
080: }
081:
082: private HashMap<Locale, LocalizedEntry> localized = new HashMap<Locale, LocalizedEntry>();
083: private ArrayList<Bundle> bundles = new ArrayList<Bundle>(); // contains all Bundles
084: private ArrayList<Serializable> fallBack = new ArrayList<Serializable>(); // List of known keys, used as fallback, if nothing defined for a certain locale
085:
086: private DocumentSerializable xml = null;
087:
088: public LocalizedEntryListFactory() {
089: localized.put(LocalizedString.getDefault(),
090: new LocalizedEntry());
091: }
092:
093: public boolean isEmpty() {
094: return bundles.size() == 0 && fallBack.size() == 0
095: && !usesCloud;
096: }
097:
098: /**
099: * Adds a value for a certain key and Locale
100: * @return The created Map.Entry.
101: */
102: public Map.Entry<Serializable, Serializable> add(Locale locale,
103: Serializable key, Serializable value) {
104: if (locale == null) {
105: locale = LocalizedString.getDefault();
106: }
107:
108: Entry<Serializable, Serializable> entry = new Entry(key, value);
109: List<Serializable> unused = add(locale, entry);
110: if (!fallBack.contains(key)) {
111: // this is an as yet unknown key.
112: size++;
113: fallBack.add(key);
114: for (Map.Entry<Locale, LocalizedEntry> e : localized
115: .entrySet()) {
116: if (!e.getKey().equals(locale)) {
117: LocalizedEntry loc = e.getValue();
118: loc.unusedKeys.add(key);
119: }
120: }
121: }
122: unused.remove(key);
123: return entry;
124: }
125:
126: /**
127: * Add entry to 'localized'
128: * @param entry the object, which has not Locale support of itself (Entry, DocumentSerializable)
129: * @param locale Can be <code>null</code> too, in which case the default locale is used
130: * @return List of currently unused keys for this locale.
131: */
132:
133: protected List<Serializable> add(Locale locale, Object entry) {
134: if (locale == null) {
135: locale = LocalizedString.getDefault();
136: }
137: LocalizedEntry local = localized.get(locale);
138: if (local == null) {
139: Locale loc = locale;
140: loc = LocalizedString.degrade(loc, locale);
141: while (loc != null && local == null) {
142: local = localized.get(loc);
143: loc = LocalizedString.degrade(loc, locale);
144: }
145: if (local == null) {
146: local = new LocalizedEntry();
147: local.entries.addAll(bundles);
148: local.unusedKeys.addAll(fallBack);
149: } else {
150: local = (LocalizedEntry) local.clone();
151: }
152: localized.put(locale, local);
153: }
154:
155: // If this locale with variant is added but the parent locale was not yet in the map, then
156: // we first add the parent locale.
157: if (locale.getVariant() != null
158: && !"".equals(locale.getVariant())) {
159: Locale l = new Locale(locale.getLanguage(), locale
160: .getCountry());
161: if (!localized.containsKey(l)) {
162: add(l, entry);
163: }
164: }
165:
166: // If this locale with country is added, but the parent locale (only language) was not yet in
167: // the map, we first add the parent language locale.
168: if (locale.getCountry() != null
169: && !"".equals(locale.getCountry())) {
170: Locale l = new Locale(locale.getLanguage());
171: if (!localized.containsKey(l)) {
172: add(l, entry);
173: }
174: }
175: local.entries.add(entry);
176: return local.unusedKeys;
177: }
178:
179: /**
180: * Adds a bundle, to the (current) end of all maintained collections. Actually, only the
181: * definition of the bundle is added, it is instantiated only later, when requested for a
182: * specific locale.
183: */
184: public void addBundle(String baseName, ClassLoader classLoader,
185: Class constantsProvider, Class wrapper,
186: Comparator comparator) {
187: Bundle b = new Bundle(baseName, classLoader, SortedBundle
188: .getConstantsProvider(constantsProvider), wrapper,
189: comparator);
190: if (bundles.contains(b)) {
191: log.info("Adding bundle " + b + " for second time in " + b
192: + ", because " + Logging.stackTrace());
193: }
194: bundles.add(b);
195: Iterator<LocalizedEntry> i = localized.values().iterator();
196: if (!i.hasNext()) {
197: // adding very first localizedlist
198: Locale locale = LocalizedString.getDefault();
199: LocalizedEntry local = new LocalizedEntry();
200: local.entries.add(b);
201: local.unusedKeys.addAll(fallBack);
202: } else
203: while (i.hasNext()) {
204: LocalizedEntry local = i.next();
205: local.entries.add(b);
206: }
207: }
208:
209: /**
210: */
211: public void addQuery(Locale locale, Document queryElement) {
212: DocumentSerializable doc = new DocumentSerializable(
213: queryElement);
214: add(locale, doc);
215: usesCloud = true;
216: }
217:
218: /**
219: * Defaulting version of {@link #get(Locale, Cloud)}. Using default anonymous cloud.
220: */
221: public List<Map.Entry<C, String>> get(final Locale locale) {
222: return get(locale, usesCloud ? getCloud(locale) : null);
223: }
224:
225: protected Cloud getCloud(Locale locale) {
226: CloudContext context = ContextProvider.getDefaultCloudContext();
227: if (context.isUp()) {
228: try {
229: Cloud cloud = context.getCloud("mmbase", "class", null);
230: if (locale != null)
231: cloud.setLocale(locale);
232: return cloud;
233: } catch (SecurityException se) {
234: log.warn("" + se.getMessage());
235: try {
236: Cloud cloud = context.getCloud("mmbase");
237: if (locale != null && cloud != null)
238: cloud.setLocale(locale);
239: return cloud;
240: } catch (SecurityException se2) {
241: return null;
242: }
243: }
244: } else {
245: return null;
246: }
247: }
248:
249: /**
250: * Returns a Collection of Map.Entries for the given Locale. The collection is kind of 'virtual',
251: * it only reflects the underlying memory structures.
252: *
253: * This collection does have a well defined iteration order.
254: *
255: * @param locale The locale of <code>null</code> for the default locale.
256: * @param cloud The cloud to use. Can be <code>null</code> if no queries added (see {@link #addQuery}).
257: * If Locale is <code>null</code>, but cloud isn't, the locale of the cloud is used.
258: */
259: public List<Map.Entry<C, String>> get(final Locale locale,
260: final Cloud cloud) {
261: return new AbstractSequentialList<Map.Entry<C, String>>() {
262:
263: public int size() {
264: return LocalizedEntryListFactory.this .size(cloud);
265: }
266:
267: public ListIterator<Map.Entry<C, String>> listIterator(
268: final int index) {
269: return new ListIterator<Map.Entry<C, String>>() {
270: int i = -1;
271: Locale useLocale = locale;
272: Cloud useCloud = cloud;
273:
274: {
275: if (useLocale == null) {
276: useLocale = useCloud != null ? useCloud
277: .getLocale() : LocalizedString
278: .getDefault();
279: }
280: log.debug("using locale " + useLocale);
281: }
282: private ChainedIterator iterator = new ChainedIterator();
283: private Iterator<Map.Entry<C, String>> subIterator = null;
284: private Map.Entry<C, String> next = null;
285:
286: {
287: Locale orgLocale = useLocale;
288:
289: LocalizedEntry loc = localized.get(useLocale);
290: while (loc == null && useLocale != null) {
291: useLocale = LocalizedString.degrade(
292: useLocale, orgLocale);
293: if (log.isDebugEnabled()) {
294: log.debug("Degraded to " + useLocale);
295: }
296: loc = localized.get(useLocale);
297: }
298: if (loc == null) {
299: useLocale = orgLocale;
300: loc = localized.get(LocalizedString
301: .getDefault());
302: }
303:
304: if (loc == null) {
305:
306: iterator.addIterator(bundles.iterator());
307: iterator.addIterator(fallBack.iterator());
308: } else {
309: iterator
310: .addIterator(loc.entries.iterator());
311: iterator.addIterator(loc.unusedKeys
312: .iterator());
313: }
314:
315: findNext();
316: while (i < index)
317: next();
318: }
319:
320: protected void findNext() {
321: next = null;
322: i++;
323: while (next == null && iterator.hasNext()) {
324: Object candidate = iterator.next();
325: if (candidate instanceof Map.Entry) {
326: next = (Map.Entry) candidate;
327: } else if (candidate instanceof Bundle) {
328: subIterator = ((Bundle) candidate).get(
329: useLocale).iterator();
330: if (subIterator.hasNext()) {
331: break;
332: } else {
333: subIterator = null;
334: }
335: } else if (candidate instanceof DocumentSerializable) {
336: Element element = ((DocumentSerializable) candidate)
337: .getDocument()
338: .getDocumentElement();
339: try {
340: if (useCloud == null) {
341: useCloud = getCloud(useLocale);
342: if (useCloud == null) {
343: if (log.isDebugEnabled()) {
344: log
345: .debug("Defined query for "
346: + this
347: + " but no cloud provided. Skipping results.");
348: }
349: continue;
350: }
351: }
352: Query query = QueryReader
353: .parseQuery(element,
354: useCloud, null).query;
355: final org.mmbase.bridge.NodeList list = query
356: .getList();
357: subIterator = new Iterator() {
358: final NodeIterator nodeIterator = list
359: .nodeIterator();
360:
361: public boolean hasNext() {
362: return nodeIterator
363: .hasNext();
364: }
365:
366: public Object next() {
367: org.mmbase.bridge.Node next = nodeIterator
368: .nextNode();
369: return new Entry<Node, FieldValue>(
370: next,
371: next
372: .getFunctionValue(
373: "gui",
374: null));
375: }
376:
377: public void remove() {
378: throw new UnsupportedOperationException();
379: }
380: };
381: if (subIterator.hasNext()) {
382: break;
383: } else {
384: subIterator = null;
385: }
386: } catch (Exception e) {
387: log.error(e.getMessage(), e);
388: }
389: } else {
390: next = new Entry(candidate, candidate);
391: }
392: }
393: }
394:
395: public boolean hasNext() {
396: return next != null || subIterator != null;
397: }
398:
399: public Map.Entry<C, String> next() {
400: Map.Entry<C, String> res;
401: if (subIterator != null) {
402: res = subIterator.next();
403: Object key = res.getKey();
404: if (key != null
405: && key instanceof SortedBundle.ValueWrapper) {
406: res = new Entry(
407: ((SortedBundle.ValueWrapper) key)
408: .getKey(), res
409: .getValue());
410: }
411: if (!subIterator.hasNext()) {
412: subIterator = null;
413: findNext();
414: }
415: } else {
416: res = next;
417: findNext();
418: }
419: return res;
420: }
421:
422: public int nextIndex() {
423: return i;
424: }
425:
426: public int previousIndex() {
427: return i - 1;
428: }
429:
430: public boolean hasPrevious() {
431: // TODO
432: throw new UnsupportedOperationException();
433: }
434:
435: public Map.Entry<C, String> previous() {
436: // TODO
437: throw new UnsupportedOperationException();
438: }
439:
440: // this is why we hate java:
441:
442: public void remove() {
443: throw new UnsupportedOperationException();
444: }
445:
446: public void add(Map.Entry<C, String> o) {
447: throw new UnsupportedOperationException();
448: }
449:
450: public void set(Map.Entry<C, String> o) {
451: throw new UnsupportedOperationException();
452: }
453: };
454: }
455: };
456: }
457:
458: /**
459: * The size of the collections returned by {@link #get}
460: */
461: public int size(Cloud cloud) {
462: if (cloud == null)
463: cloud = getCloud(LocalizedString.getDefault());
464: int queriesSize = size();
465: Locale locale = cloud == null ? LocalizedString.getDefault()
466: : cloud.getLocale();
467: LocalizedEntry localizedList = localized.get(locale);
468: if (localizedList == null) {
469: locale = LocalizedString.getDefault();
470: localizedList = localized.get(locale);
471: }
472: if (localizedList != null) {
473: Iterator i = localizedList.entries.iterator();
474: while (i.hasNext()) {
475: Object o = i.next();
476: if (o instanceof Bundle) {
477: // already in size();
478: } else if (o instanceof DocumentSerializable) {
479: if (cloud == null) {
480: cloud = getCloud(null);
481: if (cloud == null) {
482: log
483: .debug("Found query but didn't provide cloud, skipping");
484: continue;
485: }
486: }
487: Element element = ((DocumentSerializable) o)
488: .getDocument().getDocumentElement();
489: try {
490: queriesSize += Queries
491: .count(QueryReader.parseQuery(element,
492: cloud, null).query);
493: } catch (Exception e) {
494: log.warn(e);
495: }
496: } else {
497: queriesSize++;
498: }
499: }
500: //queriesSize += localizedList.unusedKeys.size();
501: }
502:
503: return queriesSize;
504: }
505:
506: public int size() {
507: int bundleSize = 0;
508: Iterator i = bundles.iterator();
509: while (i.hasNext()) {
510: Bundle b = (Bundle) i.next();
511: try {
512: bundleSize += b.get(null).size();
513: } catch (MissingResourceException mre) {
514: log.error(mre);
515: }
516: }
517: return size + bundleSize;
518: }
519:
520: public Object castKey(final Object key) {
521: return castKey(key, null);
522: }
523:
524: /**
525: * Since keys may be somehow wrapped, you can also 'unwrap' by this. If e.g. a constants
526: * provider was used, that values can be indicated by the name of the constants and this method
527: * casts to the value.
528: */
529: public Object castKey(final Object key, final Cloud cloud) {
530: String string = null;
531: Iterator<Bundle> i = bundles.iterator();
532: if (i.hasNext()) {
533: string = Casting.toString(key);
534: while (i.hasNext()) {
535: Bundle b = i.next();
536: Class wrapper = b.wrapper;
537: Map<String, Object> constants = b.constantsProvider;
538: Object nk = SortedBundle.castKey(string, null,
539: constants, wrapper);
540: if (string != nk) {
541: if (log.isDebugEnabled()) {
542: log.debug("Cast " + key + " to " + nk);
543: }
544: return nk;
545: }
546: }
547: }
548: if (usesCloud && cloud != null) {
549: if (string == null)
550: string = Casting.toString(key);
551: if (cloud.hasNode(string)) {
552: return cloud.getNode(string);
553: }
554: }
555: return key;
556:
557: }
558:
559: public Object clone() {
560: try {
561: LocalizedEntryListFactory clone = (LocalizedEntryListFactory) super
562: .clone();
563: Iterator<Bundle> j = clone.bundles.iterator();
564: clone.bundles = new ArrayList<Bundle>();
565: while (j.hasNext()) {
566: clone.bundles.add((j.next().clone()));
567: }
568: Iterator<Map.Entry<Locale, LocalizedEntry>> i = clone.localized
569: .entrySet().iterator();
570: clone.localized = new HashMap();
571: while (i.hasNext()) {
572: Map.Entry<Locale, LocalizedEntry> entry = i.next();
573: clone.localized.put(entry.getKey(), (entry.getValue())
574: .clone());
575: }
576: clone.fallBack = (ArrayList) fallBack.clone();
577: return clone;
578: } catch (Exception e) {
579: log.error(e.getMessage(), e);
580: return new LocalizedEntryListFactory();
581: }
582: }
583:
584: /**
585: * Clears all added keys, bundles and queries.
586: */
587: public void clear() {
588: localized.clear();
589: bundles.clear();
590: fallBack.clear();
591: usesCloud = false;
592: size = 0;
593: }
594:
595: /**
596: * Given a certain DOM parent element, it configures this LocalizedEntryListFactory with
597: * sub tags of type 'entry' and 'query'
598: */
599:
600: public void fillFromXml(final Element enumerationElement,
601: Class wrapperDefault) {
602: xml = new DocumentSerializable(DocumentReader
603: .toDocument(enumerationElement));
604: org.w3c.dom.NodeList childNodes = enumerationElement
605: .getElementsByTagName("query");
606: for (int i = 0; i < childNodes.getLength(); i++) {
607: Element queryElement = (Element) childNodes.item(i);
608: Locale locale = LocalizedString.getLocale(queryElement);
609: addQuery(locale, DocumentReader.toDocument(queryElement));
610: }
611:
612: childNodes = enumerationElement.getElementsByTagName("entry");
613: for (int i = 0; i < childNodes.getLength(); i++) {
614: Element entryElement = (Element) childNodes.item(i);
615: if (entryElement.hasAttribute("value")) {
616: String value = entryElement.getAttribute("value");
617: Locale locale = LocalizedString.getLocale(entryElement);
618: String display = entryElement.getAttribute("display");
619: if (display.equals(""))
620: display = value;
621: Object key = wrapperDefault != null ? Casting.toType(
622: wrapperDefault, null, value) : value;
623: if (key instanceof Serializable) {
624: if (log.isDebugEnabled()) {
625: log.debug("Added " + key + "/" + display
626: + " for " + locale);
627: }
628: add(locale, (Serializable) key, display);
629: } else {
630: log
631: .error("key "
632: + key
633: + " for "
634: + wrapperDefault
635: + " is not serializable, cannot be added to entrylist factory.");
636: }
637: } else {
638: String resource = entryElement.getAttribute("basename");
639: if (!resource.equals("")) {
640: Comparator comparator = null;
641: Class wrapper = wrapperDefault;
642: if (wrapper != null
643: && (!Comparable.class
644: .isAssignableFrom(wrapper))
645: && (!Boolean.class.equals(wrapper)) // in java < 1.5 Boolean is not comparable
646: ) {
647: wrapper = null;
648: }
649:
650: {
651: String sorterClass = entryElement
652: .getAttribute("sorterclass");
653: if (!sorterClass.equals("")) {
654: try {
655: Class sorter = Class
656: .forName(sorterClass);
657: if (Comparator.class
658: .isAssignableFrom(sorter)) {
659: comparator = (Comparator) sorter
660: .newInstance();
661: } else {
662: wrapper = sorter;
663: }
664: } catch (Exception e) {
665: log.error(e);
666: }
667: }
668: }
669: Class constantsClass = null;
670: {
671: String javaConstants = entryElement
672: .getAttribute("javaconstants");
673: if (!javaConstants.equals("")) {
674: try {
675: constantsClass = Class
676: .forName(javaConstants);
677: } catch (Exception e) {
678: log.error(e);
679: }
680: }
681: }
682: try {
683: addBundle(resource,
684: getClass().getClassLoader(),
685: constantsClass, wrapper, comparator);
686: } catch (MissingResourceException mre) {
687: log.error(mre);
688: }
689: } else {
690: throw new IllegalArgumentException(
691: "no 'value' or 'basename' attribute on enumeration entry element");
692: }
693: }
694: if (log.isDebugEnabled()) {
695: log.debug("Found enumeration values now " + this );
696: }
697: }
698:
699: }
700:
701: public Element toXml() {
702: if (xml == null) {
703: // TODO: generate xml.
704: return null;
705: } else {
706: return xml.getDocument().getDocumentElement();
707: }
708: }
709:
710: public String toString() {
711: return "(localized: " + localized + "bundles: " + bundles
712: + "fallBack: " + fallBack + ")";
713: }
714:
715: private static class Bundle implements Serializable,
716: PublicCloneable {
717: private static final long serialVersionUID = 1L; // increase this if object serialization changes (which we shouldn't do!)
718:
719: private String resource;
720: private ClassLoader classLoader;
721: private HashMap<String, Object> constantsProvider;
722: private Class wrapper;
723: private Comparator comparator;
724:
725: // implementation of serializable
726: private void writeObject(ObjectOutputStream out)
727: throws IOException {
728: out.writeUTF(resource);
729: // dont'r write class-loader, it is not serializable
730: out.writeObject(constantsProvider);
731: out.writeObject(wrapper);
732: if (comparator instanceof Serializable) {
733: out.writeObject(comparator);
734: } else {
735: out.writeObject((Comparator) null);
736: }
737: }
738:
739: // implementation of serializable
740: private void readObject(ObjectInputStream in)
741: throws IOException, ClassNotFoundException {
742: resource = in.readUTF();
743: classLoader = getClass().getClassLoader();
744: constantsProvider = (HashMap) in.readObject();
745: wrapper = (Class) in.readObject();
746: comparator = (Comparator) in.readObject();
747: }
748:
749: Bundle(String r, ClassLoader cl, HashMap<String, Object> cp,
750: Class w, Comparator comp) {
751: resource = r;
752: classLoader = cl;
753: constantsProvider = cp;
754: wrapper = w;
755: comparator = comp;
756: }
757:
758: /**
759: * Collection of Map.Entry's
760: */
761: Collection get(Locale loc) throws MissingResourceException {
762: try {
763: return SortedBundle.getResource(resource, loc,
764: classLoader, constantsProvider, wrapper,
765: comparator).entrySet();
766: } catch (IllegalArgumentException iae) {
767: log.error(iae);
768: return Collections.emptyList();
769: }
770: }
771:
772: public String toString() {
773: return resource + " " + constantsProvider + " " + wrapper
774: + " " + comparator;
775: }
776:
777: public boolean equals(Object o) {
778: if (o instanceof Bundle) {
779: Bundle b = (Bundle) o;
780: return (resource == null ? b.resource == null
781: : resource.equals(b.resource))
782: && (classLoader == null ? b.classLoader == null
783: : classLoader.equals(b.classLoader))
784: && (constantsProvider == null ? b.constantsProvider == null
785: : constantsProvider
786: .equals(b.constantsProvider))
787: && (wrapper == null ? b.wrapper == null
788: : wrapper.equals(b.wrapper))
789: && (comparator == null ? b.comparator == null
790: : comparator.equals(b.comparator));
791:
792: } else {
793: return false;
794: }
795: }
796:
797: public int hashCode() {
798: int result = 0;
799: result = HashCodeUtil.hashCode(result, resource);
800: result = HashCodeUtil.hashCode(result, classLoader);
801: result = HashCodeUtil.hashCode(result, constantsProvider);
802: result = HashCodeUtil.hashCode(result, wrapper);
803: result = HashCodeUtil.hashCode(result, comparator);
804: return result;
805: }
806:
807: public Object clone() {
808: log.debug("Cloning bundle " + this );
809: try {
810: Bundle clone = (Bundle) super .clone();
811: clone.constantsProvider = constantsProvider != null ? (HashMap<String, Object>) constantsProvider
812: .clone()
813: : null;
814: return clone;
815: } catch (Exception e) {
816: log.error(e.getMessage(), e);
817: return this ;
818: }
819: }
820:
821: }
822:
823: /**
824: * For testing only.
825: */
826: public static void main(String argv[]) {
827: LocalizedEntryListFactory fact = new LocalizedEntryListFactory();
828: String resource1 = "org.mmbase.datatypes.resources.boolean.onoff";
829: String resource2 = "org.mmbase.datatypes.resources.boolean.yesno";
830: Locale nl = new Locale("nl");
831: Locale en = new Locale("en");
832: Locale dk = new Locale("dk");
833: Locale eo = new Locale("eo");
834: fact.add(nl, "a", "hallo");
835: System.out.println("nou " + fact);
836: fact.add(new Locale("nl"), "b", "daag");
837: fact.add(en, "b", "hello");
838: fact.add(en, "a", "good bye");
839: fact.addBundle(resource1, null, null, Boolean.class,
840: SortedBundle.NO_COMPARATOR);
841: fact.add(nl, "c", "doegg");
842: fact.add(dk, 5, "dk");
843: fact.add(null, "e", "oi");
844: fact.addBundle(resource2, null, null, String.class,
845: SortedBundle.NO_COMPARATOR);
846:
847: System.out.println("size: " + fact.size() + " " + fact);
848: System.out.println("en" + fact.get(en));
849: System.out.println("nl" + fact.get(nl));
850: System.out.println("dk" + fact.get(dk));
851: System.out.println("eo" + fact.get(eo));
852:
853: LocalizedEntryListFactory fact2 = new LocalizedEntryListFactory();
854: fact2.addBundle("org.mmbase.datatypes.resources.states", null,
855: org.mmbase.module.builders.MMServers.class,
856: SortedBundle.NO_WRAPPER, SortedBundle.NO_COMPARATOR);
857:
858: System.out.println("size: " + fact2.size());
859: System.out.println("" + fact2.get(en));
860: System.out.println("" + fact2.get(nl));
861: Object error = fact2.castKey("ERROR", null);
862: System.out.println("ERROR=" + error.getClass().getName() + " "
863: + error);
864:
865: }
866:
867: }
|