001/** 002 * Copyright 2010 The Kuali Foundation Licensed under the 003 * Educational Community License, Version 2.0 (the "License"); you may 004 * not use this file except in compliance with the License. You may 005 * obtain a copy of the License at 006 * 007 * http://www.osedu.org/licenses/ECL-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, 010 * software distributed under the License is distributed on an "AS IS" 011 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 012 * or implied. See the License for the specific language governing 013 * permissions and limitations under the License. 014 */ 015 016package org.kuali.student.r1.common.assembly.data; 017 018import java.io.Serializable; 019import java.sql.Time; 020import java.sql.Timestamp; 021import java.util.Date; 022import java.util.HashMap; 023import java.util.HashSet; 024import java.util.Iterator; 025import java.util.LinkedHashMap; 026import java.util.Map; 027import java.util.Set; 028import java.util.Map.Entry; 029 030import javax.xml.bind.annotation.XmlRootElement; 031import javax.xml.bind.annotation.XmlType; 032 033/** 034 * Kuali Student data representation. Data is essentially a map of values. 035 * 036 * @author Kuali Student Team 037 * @see Value 038 */ 039@SuppressWarnings({"serial", "unchecked"}) 040public class Data implements Serializable, Iterable<Data.Property>, HasChangeCallbacks { 041 @XmlType(name = "lookUpDataType") 042 public enum DataType { 043 STRING, INTEGER, LONG, FLOAT, DOUBLE, BOOLEAN, DATE, TRUNCATED_DATE, DATA, LIST 044 } 045 046 @XmlRootElement 047 public static class BooleanValue implements Value { 048 private Boolean value; 049 050 protected BooleanValue() { 051 052 } 053 054 public BooleanValue(final Boolean value) { 055 this.value = value; 056 } 057 058 @Override 059 public <T> T get() { 060 return (T) value; 061 } 062 063 @Override 064 public Class getType() { 065 return Boolean.class; 066 } 067 068 @Override 069 public String toString() { 070 if (value == null) { 071 return ""; 072 } else { 073 return String.valueOf(value); 074 } 075 } 076 } 077 078 @XmlRootElement 079 public static class DataValue implements Value { 080 private Data value; 081 082 protected DataValue() { 083 084 } 085 086 public DataValue(final Data value) { 087 this.value = value; 088 } 089 090 @Override 091 public <T> T get() { 092 return (T) value; 093 } 094 095 @Override 096 public Class getType() { 097 return Data.class; 098 } 099 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}