View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.common.assembly.data;
17  
18  import java.io.Serializable;
19  import java.sql.Time;
20  import java.sql.Timestamp;
21  import java.util.Date;
22  import java.util.HashMap;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.LinkedHashMap;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.Map.Entry;
29  
30  import javax.xml.bind.annotation.XmlRootElement;
31  import javax.xml.bind.annotation.XmlType;
32  
33  /**
34   * Kuali Student data representation.  Data is essentially a map of values.
35   * 
36   * @author Kuali Student Team
37   * @see Value
38   */
39  @SuppressWarnings({"serial", "unchecked"})
40  public class Data implements Serializable, Iterable<Data.Property>, HasChangeCallbacks {
41      @XmlType(name = "lookUpDataType")
42      public enum DataType {
43          STRING, INTEGER, LONG, FLOAT, DOUBLE, BOOLEAN, DATE, TRUNCATED_DATE, DATA, LIST
44      }
45  
46      @XmlRootElement
47      public static class BooleanValue implements Value {
48          private Boolean value;
49  
50          protected BooleanValue() {
51  
52          }
53  
54          public BooleanValue(final Boolean value) {
55              this.value = value;
56          }
57  
58          @Override
59          public <T> T get() {
60              return (T) value;
61          }
62  
63          @Override
64          public Class getType() {
65              return Boolean.class;
66          }
67  
68          @Override
69          public String toString() {
70              if (value == null) {
71                  return "";
72              } else {
73                  return String.valueOf(value);
74              }
75          }
76      }
77  
78      @XmlRootElement
79      public static class DataValue implements Value {
80          private Data value;
81  
82          protected DataValue() {
83  
84          }
85  
86          public DataValue(final Data value) {
87              this.value = value;
88          }
89  
90          @Override
91          public <T> T get() {
92              return (T) value;
93          }
94  
95          @Override
96          public Class getType() {
97              return Data.class;
98          }
99  
100         @Override
101         public String toString() {
102             if (value == null) {
103                 return null;
104             } else {
105                 return value.toString();
106             }
107         }
108     }
109 
110     @XmlRootElement
111     public static class DateValue implements Value {
112         private Date value;
113 
114         protected DateValue() {
115 
116         }
117 
118         public DateValue(final Date value) {
119             this.value = value;
120         }
121 
122         @Override
123         public <T> T get() {
124             return (T) value;
125         }
126 
127         @Override
128         public Class getType() {
129             return Date.class;
130         }
131 
132         @Override
133         public String toString() {
134             if (value == null) {
135                 return null;
136             } else {
137                 return String.valueOf(value);
138             }
139         }
140     }
141 
142     @XmlRootElement
143     public static class DoubleValue implements Value {
144         private Double value;
145 
146         protected DoubleValue() {
147 
148         }
149 
150         public DoubleValue(final Double value) {
151             this.value = value;
152         }
153 
154         @Override
155         public <T> T get() {
156             return (T) value;
157         }
158 
159         @Override
160         public Class getType() {
161             return Double.class;
162         }
163 
164         @Override
165         public String toString() {
166             if (value == null) {
167                 return null;
168             } else {
169                 return String.valueOf(value);
170             }
171         }
172     }
173 
174     @XmlRootElement
175     public static class FloatValue implements Value {
176         private Float value;
177 
178         protected FloatValue() {
179 
180         }
181 
182         public FloatValue(final Float value) {
183             this.value = value;
184         }
185 
186         @Override
187         public <T> T get() {
188             return (T) value;
189         }
190 
191         @Override
192         public Class getType() {
193             return Float.class;
194         }
195 
196         @Override
197         public String toString() {
198             if (value == null) {
199                 return null;
200             } else {
201                 return String.valueOf(value);
202             }
203         }
204     }
205 
206     public static class IntegerKey implements Key {
207         private Integer key;
208 
209         protected IntegerKey() {
210 
211         }
212 
213         public IntegerKey(final Integer key) {
214             this.key = key;
215         }
216 
217         /*
218          * (non-Javadoc)
219          * 
220          * @see java.lang.Object#equals(java.lang.Object)
221          */
222 
223         @Override
224         public boolean equals(final Object obj) {
225             return obj instanceof IntegerKey && key.equals(((IntegerKey) obj).key);
226         }
227 
228         @Override
229         public <T> T get() {
230             return (T) key;
231         }
232 
233         @Override
234         public Class getType() {
235             return Integer.class;
236         }
237 
238         /*
239          * (non-Javadoc)
240          * 
241          * @see java.lang.Object#hashCode()
242          */
243 
244         @Override
245         public int hashCode() {
246             return key.hashCode();
247         }
248 
249         /*
250          * (non-Javadoc)
251          * 
252          * @see java.lang.Object#toString()
253          */
254 
255         @Override
256         public String toString() {
257             return String.valueOf(key);
258         }
259 
260     }
261 
262     @XmlRootElement
263     public static class IntegerValue implements Value {
264         private Integer value;
265 
266         protected IntegerValue() {
267 
268         }
269 
270         public IntegerValue(final Integer value) {
271             this.value = value;
272         }
273 
274         @Override
275         public <T> T get() {
276             return (T) value;
277         }
278 
279         @Override
280         public Class getType() {
281             return Integer.class;
282         }
283 
284         @Override
285         public String toString() {
286             if (value == null) {
287                 return null;
288             } else {
289                 return String.valueOf(value);
290             }
291         }
292     }
293 
294     public interface Key extends Serializable {
295 
296         <T> T get();
297 
298         Class getType();
299     }
300 
301     @XmlRootElement
302     public static class LongValue implements Value {
303         private Long value;
304 
305         protected LongValue() {
306 
307         }
308 
309         public LongValue(final Long value) {
310             this.value = value;
311         }
312 
313         @Override
314         public <T> T get() {
315             return (T) value;
316         }
317 
318         @Override
319         public Class getType() {
320             return Long.class;
321         }
322 
323         @Override
324         public String toString() {
325             if (value == null) {
326                 return null;
327             } else {
328                 return String.valueOf(value);
329             }
330         }
331     }
332 
333     public interface Property {
334         <T> T getKey();
335 
336         Class getKeyType();
337 
338         Key getWrappedKey();
339 
340         <T> T getValue();
341 
342         Class getValueType();
343 
344         Value getWrappedValue();
345     }
346 
347     @XmlRootElement
348     public static class ShortValue implements Value {
349         private Short value;
350 
351         protected ShortValue() {
352 
353         }
354 
355         public ShortValue(final Short value) {
356             this.value = value;
357         }
358 
359         @Override
360         public <T> T get() {
361             return (T) value;
362         }
363 
364         @Override
365         public Class getType() {
366             return Short.class;
367         }
368 
369         @Override
370         public String toString() {
371             if (value == null) {
372                 return null;
373             } else {
374                 return String.valueOf(value);
375             }
376         }
377     }
378 
379     @XmlRootElement
380     public static class StringKey implements Key {
381         private String key;
382 
383         protected StringKey() {
384 
385         }
386 
387         public StringKey(final String key) {
388             this.key = key;
389         }
390 
391         /*
392          * (non-Javadoc)
393          * 
394          * @see java.lang.Object#equals(java.lang.Object)
395          */
396 
397         @Override
398         public boolean equals(final Object obj) {
399             return obj instanceof StringKey && key.equals(((StringKey) obj).key);
400         }
401 
402         @Override
403         public <T> T get() {
404             return (T) key;
405         }
406 
407         @Override
408         public Class<?> getType() {
409             return String.class;
410         }
411 
412         /*
413          * (non-Javadoc)
414          * 
415          * @see java.lang.Object#hashCode()
416          */
417 
418         @Override
419         public int hashCode() {
420             return key.hashCode();
421         }
422 
423         /*
424          * (non-Javadoc)
425          * 
426          * @see java.lang.Object#toString()
427          */
428 
429         @Override
430         public String toString() {
431             return key;
432         }
433 
434     }
435 
436     @XmlRootElement
437     public static class StringValue implements Value {
438         private String value;
439 
440         protected StringValue() {
441 
442         }
443 
444         public StringValue(final String value) {
445             this.value = value;
446         }
447 
448         @Override
449         public <T> T get() {
450             return (T) value;
451         }
452 
453         @Override
454         public Class getType() {
455             return String.class;
456         }
457 
458         @Override
459         public String toString() {
460             return value;
461         }
462 
463         public void setValue(String value) {
464             this.value = value;
465         }
466     }
467 
468     @XmlRootElement
469     public static class TimestampValue implements Value {
470         private Timestamp value;
471 
472         protected TimestampValue() {
473 
474         }
475 
476         public TimestampValue(final Timestamp value) {
477             this.value = value;
478         }
479 
480         @Override
481         public <T> T get() {
482             return (T) value;
483         }
484 
485         @Override
486         public Class getType() {
487             return Timestamp.class;
488         }
489 
490         @Override
491         public String toString() {
492             if (value == null) {
493                 return null;
494             } else {
495                 return String.valueOf(value);
496             }
497         }
498     }
499 
500     @XmlRootElement
501     public static class TimeValue implements Value {
502         private Time value;
503 
504         protected TimeValue() {
505 
506         }
507 
508         public TimeValue(final Time value) {
509             this.value = value;
510         }
511 
512         @Override
513         public <T> T get() {
514             return (T) value;
515         }
516 
517         @Override
518         public Class getType() {
519             return Time.class;
520         }
521 
522         @Override
523         public String toString() {
524             if (value == null) {
525                 return null;
526             } else {
527                 return String.valueOf(value);
528             }
529         }
530     }
531 
532     /**
533      * Kuali Student data value interface for use in Data
534      * 
535      * @author Kuali Student Team
536      *
537      */
538     public interface Value extends Serializable {
539         <T> T get();
540 
541         Class getType();
542     }
543 
544     public static final Key WILDCARD_KEY = new Data.StringKey(QueryPath.getWildCard());
545 
546     private transient Set<ChangeCallback> changeCallbacks;
547 
548     private String className;
549 
550     private Map<Key, Value> map;
551 
552     private Data parent = null;
553 
554     private Key parentKey = null;
555 
556     public Data() {
557         this(Data.class.getName());
558     }
559 
560     public Data(final String className) {
561         this.className = className;
562         map = new LinkedHashMap<Key, Value>();
563     }
564 
565     protected void _getQueryPath(final QueryPath path) {
566         if (parent != null) {
567             parent._getQueryPath(path);
568             path.add(parentKey);
569         }
570     }
571 
572     protected void execChangeCallbacks(ChangeType type, QueryPath path) {
573         if (changeCallbacks != null) {
574             for (ChangeCallback c : changeCallbacks) {
575                 c.onChange(type, path);
576             }
577         }
578         if (parent != null) {
579             parent.execChangeCallbacks(type, path);
580         }
581     }
582 
583     public ChangeCallbackRegistration addChangeCallback(final ChangeCallback callback) {
584         if (changeCallbacks == null) {
585             changeCallbacks = new HashSet<ChangeCallback>();
586         }
587         changeCallbacks.add(callback);
588         return new ChangeCallbackRegistration() {
589             @Override
590             public void remove() {
591                 if (changeCallbacks != null) {
592                     changeCallbacks.remove(callback);
593                 }
594             }
595         };
596     }
597 
598     private void put(Key key, Value value) {
599         Value existing = map.put(key, value);
600         ChangeType type = existing == null ? ChangeType.ADD : ChangeType.UPDATE;
601         QueryPath path = getQueryPath();
602         path.add(key);
603         execChangeCallbacks(type, path);
604     }
605 
606     public void remove(Key key) {
607         // TODO probably need to add all of the other remove(type) methods
608         map.remove(key);
609         QueryPath path = getQueryPath();
610         path.add(key);
611         execChangeCallbacks(ChangeType.REMOVE, path);
612     }
613 
614     public void add(final Boolean value) {
615         put(new IntegerKey(map.size()), new BooleanValue(value));
616     }
617 
618     public void add(final Data value) {
619         final Key k = new IntegerKey(map.size());
620         put(k, new DataValue(value));
621         if (value != null) {
622             value.parent = this;
623             value.parentKey = k;
624         }
625     }
626 
627     public void add(final Date value) {
628         put(new IntegerKey(map.size()), new DateValue(value));
629     }
630 
631     public void add(final Double value) {
632         put(new IntegerKey(map.size()), new DoubleValue(value));
633     }
634 
635     public void add(final Float value) {
636         put(new IntegerKey(map.size()), new FloatValue(value));
637     }
638 
639     public void add(final Integer value) {
640         put(new IntegerKey(map.size()), new IntegerValue(value));
641     }
642 
643     public void add(final Long value) {
644         put(new IntegerKey(map.size()), new LongValue(value));
645     }
646 
647     public void add(final Short value) {
648         put(new IntegerKey(map.size()), new ShortValue(value));
649     }
650 
651     public void add(final String value) {
652         put(new IntegerKey(map.size()), new StringValue(value));
653     }
654 
655     public void add(final Time value) {
656         put(new IntegerKey(map.size()), new TimeValue(value));
657     }
658 
659     public void add(final Timestamp value) {
660         put(new IntegerKey(map.size()), new TimestampValue(value));
661     }
662 
663     public Data copy() {
664         return copy(new Data(this.className), true);
665     }
666 
667     public Data copy(Data target, boolean recurse) {
668         // note, this was the clone() method, but my eclipse code cleanup insists on @Override, and the compiler gives an
669         // error
670         for (final Entry<Key, Value> e : map.entrySet()) {
671             if (recurse && e.getValue().getType().equals(Data.class)) {
672                 Data value = e.getValue().get();
673                 if (value != null) {
674                     value = value.copy();
675                 }
676                 target.map.put(e.getKey(), new DataValue(value));
677             } else {
678                 target.map.put(e.getKey(), e.getValue());
679             }
680         }
681         return target;
682     }
683 
684     public <T> T get(final Integer key) {
685         final Value v = map.get(new IntegerKey(key));
686         T result = null;
687         if (v != null) {
688             result = (T) v.get();
689         }
690         return result;
691     }
692 
693     public <T> T get(final Key key) {
694         final Value v = map.get(key);
695         T result = null;
696         if (v != null) {
697             result = (T) v.get();
698         }
699         return result;
700     }
701 
702     public <T> T get(final String key) {
703         final Value v = map.get(new StringKey(key));
704         T result = null;
705         if (v != null) {
706             result = (T) v.get();
707         }
708         return result;
709     }
710 
711     public String getClassName() {
712         return this.className;
713     }
714 
715     public Data getParent() {
716         return parent;
717     }
718 
719     public QueryPath getQueryPath() {
720         final QueryPath result = new QueryPath();
721         _getQueryPath(result);
722         return result;
723     }
724 
725 
726     /**
727      * @return an Iterator that does not contain any _runtimeData
728      */
729     public Iterator<Property> realPropertyIterator() {
730         HashMap<Key, Value> propertyMap = new HashMap<Key, Value>(map);
731 //    	propertyMap.remove("_runtimeData");
732         propertyMap.remove(new StringKey("_runtimeData"));
733 
734         final Iterator<Map.Entry<Key, Value>> impl = propertyMap.entrySet().iterator();
735 
736         return new Iterator<Property>() {
737             Map.Entry<Key, Value> current;
738 
739             @Override
740             public boolean hasNext() {
741                 return impl.hasNext();
742             }
743 
744             @Override
745             public Property next() {
746                 final Map.Entry<Key, Value> entry = impl.next();
747                 current = entry;
748                 return new Property() {
749                     @Override
750                     public <T> T getKey() {
751                         return (T) entry.getKey().get();
752                     }
753 
754                     @Override
755                     public Class<?> getKeyType() {
756                         return entry.getKey().getType();
757                     }
758 
759                     @Override
760                     public <T> T getValue() {
761                         return (T) entry.getValue().get();
762                     }
763 
764                     @Override
765                     public Class<?> getValueType() {
766                         return entry.getValue().getType();
767                     }
768 
769                     @Override
770                     public Key getWrappedKey() {
771                         return entry.getKey();
772                     }
773 
774                     @Override
775                     public Value getWrappedValue() {
776                         return entry.getValue();
777                     }
778                 };
779             }
780 
781             @Override
782             public void remove() {
783                 impl.remove();
784                 QueryPath path = getQueryPath();
785                 path.add(current.getKey());
786                 execChangeCallbacks(ChangeType.REMOVE, path);
787             }
788         };
789     }
790 
791     /*
792     * (non-Javadoc)
793     *
794     * @see java.lang.Iterable#iterator()
795     */
796 
797     @Override
798     public Iterator<Property> iterator() {
799         final Iterator<Map.Entry<Key, Value>> impl = map.entrySet().iterator();
800 
801         return new Iterator<Property>() {
802             Map.Entry<Key, Value> current;
803 
804             @Override
805             public boolean hasNext() {
806                 return impl.hasNext();
807             }
808 
809             @Override
810             public Property next() {
811                 final Map.Entry<Key, Value> entry = impl.next();
812                 current = entry;
813                 return new Property() {
814                     @Override
815                     public <T> T getKey() {
816                         return (T) entry.getKey().get();
817                     }
818 
819                     @Override
820                     public Class<?> getKeyType() {
821                         return entry.getKey().getType();
822                     }
823 
824                     @Override
825                     public <T> T getValue() {
826                         return (T) entry.getValue().get();
827                     }
828 
829                     @Override
830                     public Class<?> getValueType() {
831                         return entry.getValue().getType();
832                     }
833 
834                     @Override
835                     public Key getWrappedKey() {
836                         return entry.getKey();
837                     }
838 
839                     @Override
840                     public Value getWrappedValue() {
841                         return entry.getValue();
842                     }
843                 };
844             }
845 
846             @Override
847             public void remove() {
848                 impl.remove();
849                 QueryPath path = getQueryPath();
850                 path.add(current.getKey());
851                 execChangeCallbacks(ChangeType.REMOVE, path);
852             }
853         };
854     }
855 
856     public <T> T query(final QueryPath path) {
857         T result = null;
858         Data d = this;
859         for (final Iterator itr = path.iterator(); itr.hasNext() && d != null;) {
860             final Key k = (Key) itr.next();
861             if (itr.hasNext()) {
862                 Object obj = d.get(k);
863                 if (obj != null && !(obj instanceof Data)) {
864                     // TODO what should be done if we try to query
865                     // cluset/clus/0/_runtimeData where cluset/0 returns a string instead of Data
866                     // throw an exception here?
867                     throw new java.lang.IllegalArgumentException();
868                 } else {
869                     d = d.get(k);
870                 }
871             } else {
872                 result = (T) d.get(k);
873             }
874         }
875         return result;
876     }
877 
878     public <T> T query(final String path) {
879         return (T) query(QueryPath.parse(path));
880     }
881 
882     public Class<?> getType(final QueryPath path) {
883         Value result = null;
884         Data d = this;
885         for (final Iterator itr = path.iterator(); itr.hasNext();) {
886             final Key k = (Key) itr.next();
887             if (itr.hasNext()) {
888                 d = d.get(k);
889             } else {
890                 result = map.get(k);
891             }
892         }
893         return result.getType();
894 
895     }
896 
897     public void set(final Integer key, final Boolean value) {
898         put(new IntegerKey(key), new BooleanValue(value));
899     }
900 
901     public void set(final Integer key, final Data value) {
902         final Key k = new IntegerKey(key);
903         put(k, new DataValue(value));
904         if (value != null) {
905             value.parent = this;
906             value.parentKey = k;
907         }
908     }
909 
910     public void set(final Integer key, final Date value) {
911         put(new IntegerKey(key), new DateValue(value));
912     }
913 
914     public void set(final Integer key, final Double value) {
915         put(new IntegerKey(key), new DoubleValue(value));
916     }
917 
918     public void set(final Integer key, final Float value) {
919         put(new IntegerKey(key), new FloatValue(value));
920     }
921 
922     public void set(final Integer key, final Integer value) {
923         put(new IntegerKey(key), new IntegerValue(value));
924     }
925 
926     public void set(final Integer key, final Long value) {
927         put(new IntegerKey(key), new LongValue(value));
928     }
929 
930     public void set(final Integer key, final Short value) {
931         put(new IntegerKey(key), new ShortValue(value));
932     }
933 
934     public void set(final Integer key, final String value) {
935         put(new IntegerKey(key), new StringValue(value));
936     }
937 
938     public void set(final Integer key, final Time value) {
939         put(new IntegerKey(key), new TimeValue(value));
940     }
941 
942     public void set(final Integer key, final Timestamp value) {
943         put(new IntegerKey(key), new TimestampValue(value));
944     }
945 
946     public void set(final Key key, final Boolean value) {
947         put(key, new BooleanValue(value));
948     }
949 
950     public void set(final Key key, final Data value) {
951         put(key, new DataValue(value));
952         if (value != null) {
953             value.parent = this;
954             value.parentKey = key;
955         }
956     }
957 
958     public void set(final Key key, final Date value) {
959         put(key, new DateValue(value));
960     }
961 
962     public void set(final Key key, final Double value) {
963         put(key, new DoubleValue(value));
964     }
965 
966     public void set(final Key key, final Float value) {
967         put(key, new FloatValue(value));
968     }
969 
970     public void set(final Key key, final Integer value) {
971         put(key, new IntegerValue(value));
972     }
973 
974     public void set(final Key key, final Long value) {
975         put(key, new LongValue(value));
976     }
977 
978     public void set(final Key key, final Short value) {
979         put(key, new ShortValue(value));
980     }
981 
982     public void set(final Key key, final String value) {
983         put(key, new StringValue(value));
984     }
985 
986     public void set(final Key key, final Time value) {
987         put(key, new TimeValue(value));
988     }
989 
990     public void set(final Key key, final Timestamp value) {
991         put(key, new TimestampValue(value));
992     }
993 
994     public void set(final Key key, final Value value) {
995         put(key, value);
996         if (value instanceof DataValue) {
997             final Data d = value.get();
998             if (d != null) {
999                 d.parent = this;
1000                 d.parentKey = key;
1001             }
1002         }
1003     }
1004 
1005     public void set(final String key, final Boolean value) {
1006         put(new StringKey(key), new BooleanValue(value));
1007     }
1008 
1009     public void set(final String key, final Data value) {
1010         final Key k = new StringKey(key);
1011         put(k, new DataValue(value));
1012         if (value != null) {
1013             value.parent = this;
1014             value.parentKey = k;
1015         }
1016     }
1017 
1018     public void set(final String key, final Date value) {
1019         put(new StringKey(key), new DateValue(value));
1020     }
1021 
1022     public void set(final String key, final Double value) {
1023         put(new StringKey(key), new DoubleValue(value));
1024     }
1025 
1026     public void set(final String key, final Float value) {
1027         put(new StringKey(key), new FloatValue(value));
1028     }
1029 
1030     public void set(final String key, final Integer value) {
1031         put(new StringKey(key), new IntegerValue(value));
1032     }
1033 
1034     public void set(final String key, final Long value) {
1035         put(new StringKey(key), new LongValue(value));
1036     }
1037 
1038     public void set(final String key, final Short value) {
1039         put(new StringKey(key), new ShortValue(value));
1040     }
1041 
1042     public void set(final String key, final String value) {
1043         put(new StringKey(key), new StringValue(value));
1044     }
1045 
1046     public void set(final String key, final Time value) {
1047         put(new StringKey(key), new TimeValue(value));
1048     }
1049 
1050     public void set(final String key, final Timestamp value) {
1051         put(new StringKey(key), new TimestampValue(value));
1052     }
1053 
1054     public Integer size() {
1055         return map.size();
1056     }
1057 
1058     public String toString() {
1059     	return _toXmlString("");
1060     }
1061         
1062     /**
1063      * Converts the data map to a bracketed string representation
1064      * @return
1065      */
1066     private String _toString(){
1067         StringBuffer dataString = new StringBuffer();
1068 
1069         dataString.append("{");
1070         for (Iterator itr = this.iterator(); itr.hasNext();) {
1071             Property p = (Property) itr.next();
1072             dataString.append(p.getKey() + "=" + p.getValue());
1073             if (itr.hasNext()) {
1074                 dataString.append(", ");
1075             }
1076         }
1077         dataString.append("}");
1078 
1079         return dataString.toString();
1080     }
1081     
1082     /**
1083      * Converts the data map to an xml representation
1084      * 
1085      */
1086     private String _toXmlString(String indent){
1087         StringBuffer dataString = new StringBuffer();
1088                 
1089     	for (Iterator itr = this.iterator(); itr.hasNext();) {
1090             Property p = (Property) itr.next();
1091             Object value = p.getValue();
1092             if (value instanceof Data){            	
1093             	if (p.getKey() instanceof Integer){
1094             		dataString.append(indent + "<listitem index=\"" + p.getKey() + "\">\n");
1095             		dataString.append(((Data)value)._toXmlString(indent + "  "));
1096                 	dataString.append(indent + "</listitem>\n");            		
1097             	} else {
1098 	            	dataString.append(indent + "<" + p.getKey() + ">\n");
1099 	        		dataString.append(((Data)value)._toXmlString(indent + "  "));
1100 	            	dataString.append(indent + "</" + p.getKey() + ">\n");
1101             	}
1102             } else if (p.getKey() instanceof Integer){
1103             	dataString.append(indent + "<listitem index=\"" + p.getKey() + "\" value=\""+ value + "\"/>\n");
1104             } else {
1105             	dataString.append(indent + "<" + p.getKey() + " value=\""+ value + "\"/>\n");            	
1106             }
1107         }
1108 
1109         return dataString.toString();
1110     }
1111 
1112     public boolean containsKey(Key key) {
1113         return map.containsKey(key);
1114     }
1115 
1116     public boolean containsValue(Value value) {
1117         return map.containsValue(value);
1118     }
1119 
1120     /**
1121      * @return set of top level keys known to this data branch
1122      */
1123     public Set keySet() {
1124         return map.keySet();
1125     }
1126 
1127     public boolean isEmpty() {
1128         return map.isEmpty();
1129     }
1130 }