< prev index next >

src/java.management/share/classes/javax/management/openmbean/TabularDataSupport.java

Print this page




  34 import java.io.IOException;
  35 import java.io.ObjectInputStream;
  36 import java.io.Serializable;
  37 import java.security.AccessController;
  38 import java.util.ArrayList;
  39 import java.util.Arrays;
  40 import java.util.Collection;
  41 import java.util.Collections;
  42 import java.util.HashMap;
  43 import java.util.Iterator;
  44 import java.util.LinkedHashMap;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Set;
  48 
  49 // jmx import
  50 //
  51 
  52 
  53 /**
  54  * The <tt>TabularDataSupport</tt> class is the <i>open data</i> class which implements the <tt>TabularData</tt>
  55  * and the <tt>Map</tt> interfaces, and which is internally based on a hash map data structure.

  56  *
  57  * @since 1.5
  58  */
  59 /* It would make much more sense to implement
  60    Map<List<?>,CompositeData> here, but unfortunately we cannot for
  61    compatibility reasons.  If we did that, then we would have to
  62    define e.g.
  63    CompositeData remove(Object)
  64    instead of
  65    Object remove(Object).
  66 
  67    That would mean that if any existing code subclassed
  68    TabularDataSupport and overrode
  69    Object remove(Object),
  70    it would (a) no longer compile and (b) not actually override
  71    CompositeData remove(Object)
  72    in binaries compiled before the change.
  73 */
  74 public class TabularDataSupport
  75     implements TabularData, Map<Object,Object>,


  85      */
  86     // field cannot be final because of clone method
  87     private Map<Object,CompositeData> dataMap;
  88 
  89     /**
  90      * @serial This tabular data instance's tabular type
  91      */
  92     private final TabularType tabularType;
  93 
  94     /**
  95      * The array of item names that define the index used for rows (convenience field)
  96      */
  97     private transient String[] indexNamesArray;
  98 
  99 
 100 
 101     /* *** Constructors *** */
 102 
 103 
 104     /**
 105      * Creates an empty <tt>TabularDataSupport</tt> instance whose open-type is <var>tabularType</var>,
 106      * and whose underlying <tt>HashMap</tt> has a default initial capacity (101) and default load factor (0.75).


 107      * <p>
 108      * This constructor simply calls <tt>this(tabularType, 101, 0.75f);</tt>
 109      *
 110      * @param  tabularType               the <i>tabular type</i> describing this <tt>TabularData</tt> instance;
 111      *                                   cannot be null.
 112      *
 113      * @throws IllegalArgumentException  if the tabular type is null.
 114      */
 115     public TabularDataSupport(TabularType tabularType) {
 116 
 117         this(tabularType, 16, 0.75f);
 118     }
 119 
 120     /**
 121      * Creates an empty <tt>TabularDataSupport</tt> instance whose open-type is <var>tabularType</var>,
 122      * and whose underlying <tt>HashMap</tt> has the specified initial capacity and load factor.
 123      *
 124      * @param  tabularType               the <i>tabular type</i> describing this <tt>TabularData</tt> instance;
 125      *                           cannot be null.
 126      *
 127      * @param  initialCapacity   the initial capacity of the HashMap.
 128      *
 129      * @param  loadFactor        the load factor of the HashMap
 130      *
 131      * @throws IllegalArgumentException  if the initial capacity is less than zero,
 132      *                                   or the load factor is nonpositive,
 133      *                                   or the tabular type is null.
 134      */
 135     public TabularDataSupport(TabularType tabularType, int initialCapacity, float loadFactor) {
 136 
 137         // Check tabularType is not null
 138         //
 139         if (tabularType == null) {
 140             throw new IllegalArgumentException("Argument tabularType cannot be null.");
 141         }
 142 
 143         // Initialize this.tabularType (and indexNamesArray for convenience)
 144         //


 150         // if very unlikely that we might be the server of a 1.3 client.  In
 151         // that case you'll need to set this property.  See CR 6334663.
 152         String useHashMapProp = AccessController.doPrivileged(
 153                 new GetPropertyAction("jmx.tabular.data.hash.map"));
 154         boolean useHashMap = "true".equalsIgnoreCase(useHashMapProp);
 155 
 156         // Construct the empty contents HashMap
 157         //
 158         this.dataMap = useHashMap ?
 159             new HashMap<Object,CompositeData>(initialCapacity, loadFactor) :
 160             new LinkedHashMap<Object, CompositeData>(initialCapacity, loadFactor);
 161     }
 162 
 163 
 164 
 165 
 166     /* *** TabularData specific information methods *** */
 167 
 168 
 169     /**
 170      * Returns the <i>tabular type</i> describing this <tt>TabularData</tt> instance.
 171      */
 172     public TabularType getTabularType() {
 173 
 174         return tabularType;
 175     }
 176 
 177     /**
 178      * Calculates the index that would be used in this <tt>TabularData</tt> instance to refer to the specified
 179      * composite data <var>value</var> parameter if it were added to this instance.
 180      * This method checks for the type validity of the specified <var>value</var>,
 181      * but does not check if the calculated index is already used to refer to a value in this <tt>TabularData</tt> instance.

 182      *
 183      * @param  value                      the composite data value whose index in this
 184      *                                    <tt>TabularData</tt> instance is to be calculated;
 185      *                                    must be of the same composite type as this instance's row type;
 186      *                                    must not be null.
 187      *
 188      * @return the index that the specified <var>value</var> would have in this <tt>TabularData</tt> instance.
 189      *
 190      * @throws NullPointerException       if <var>value</var> is <tt>null</tt>.
 191      *
 192      * @throws InvalidOpenTypeException   if <var>value</var> does not conform to this <tt>TabularData</tt> instance's
 193      *                                    row type definition.
 194      */
 195     public Object[] calculateIndex(CompositeData value) {
 196 
 197         // Check value is valid
 198         //
 199         checkValueType(value);
 200 
 201         // Return its calculated index
 202         //
 203         return internalCalculateIndex(value).toArray();
 204     }
 205 
 206 
 207 
 208 
 209     /* *** Content information query methods *** */
 210 
 211 
 212     /**
 213      * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains a <tt>CompositeData</tt> value
 214      * (ie a row) whose index is the specified <var>key</var>. If <var>key</var> cannot be cast to a one dimension array
 215      * of Object instances, this method simply returns <tt>false</tt>; otherwise it returns the result of the call to
 216      * <tt>this.containsKey((Object[]) key)</tt>.
 217      *
 218      * @param  key  the index value whose presence in this <tt>TabularData</tt> instance is to be tested.
 219      *
 220      * @return  <tt>true</tt> if this <tt>TabularData</tt> indexes a row value with the specified key.
 221      */
 222     public boolean containsKey(Object key) {
 223 
 224         // if key is not an array of Object instances, return false
 225         //
 226         Object[] k;
 227         try {
 228             k = (Object[]) key;
 229         } catch (ClassCastException e) {
 230             return false;
 231         }
 232 
 233         return  this.containsKey(k);
 234     }
 235 
 236     /**
 237      * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains a <tt>CompositeData</tt> value
 238      * (ie a row) whose index is the specified <var>key</var>. If <var>key</var> is <tt>null</tt> or does not conform to
 239      * this <tt>TabularData</tt> instance's <tt>TabularType</tt> definition, this method simply returns <tt>false</tt>.
 240      *
 241      * @param  key  the index value whose presence in this <tt>TabularData</tt> instance is to be tested.
 242      *
 243      * @return  <tt>true</tt> if this <tt>TabularData</tt> indexes a row value with the specified key.
 244      */
 245     public boolean containsKey(Object[] key) {
 246 
 247         return  ( key == null ? false : dataMap.containsKey(Arrays.asList(key)));
 248     }
 249 
 250     /**
 251      * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains the specified
 252      * <tt>CompositeData</tt> value. If <var>value</var> is <tt>null</tt> or does not conform to
 253      * this <tt>TabularData</tt> instance's row type definition, this method simply returns <tt>false</tt>.
 254      *
 255      * @param  value  the row value whose presence in this <tt>TabularData</tt> instance is to be tested.
 256      *
 257      * @return  <tt>true</tt> if this <tt>TabularData</tt> instance contains the specified row value.
 258      */
 259     public boolean containsValue(CompositeData value) {
 260 
 261         return dataMap.containsValue(value);
 262     }
 263 
 264     /**
 265      * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains the specified
 266      * value.
 267      *
 268      * @param  value  the row value whose presence in this <tt>TabularData</tt> instance is to be tested.
 269      *
 270      * @return  <tt>true</tt> if this <tt>TabularData</tt> instance contains the specified row value.
 271      */
 272     public boolean containsValue(Object value) {
 273 
 274         return dataMap.containsValue(value);
 275     }
 276 
 277     /**
 278      * This method simply calls <tt>get((Object[]) key)</tt>.
 279      *
 280      * @throws NullPointerException  if the <var>key</var> is <tt>null</tt>
 281      * @throws ClassCastException    if the <var>key</var> is not of the type <tt>Object[]</tt>
 282      * @throws InvalidKeyException   if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
 283      *                               <tt>TabularType</tt> definition

 284      */
 285     public Object get(Object key) {
 286 
 287         return get((Object[]) key);
 288     }
 289 
 290     /**
 291      * Returns the <tt>CompositeData</tt> value whose index is
 292      * <var>key</var>, or <tt>null</tt> if there is no value mapping
 293      * to <var>key</var>, in this <tt>TabularData</tt> instance.
 294      *
 295      * @param key the index of the value to get in this
 296      * <tt>TabularData</tt> instance; * must be valid with this
 297      * <tt>TabularData</tt> instance's row type definition; * must not
 298      * be null.
 299      *
 300      * @return the value corresponding to <var>key</var>.
 301      *
 302      * @throws NullPointerException  if the <var>key</var> is <tt>null</tt>
 303      * @throws InvalidKeyException   if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
 304      *                               <tt>TabularType</tt> type definition.

 305      */
 306     public CompositeData get(Object[] key) {
 307 
 308         // Check key is not null and valid with tabularType
 309         // (throws NullPointerException, InvalidKeyException)
 310         //
 311         checkKeyType(key);
 312 
 313         // Return the mapping stored in the parent HashMap
 314         //
 315         return dataMap.get(Arrays.asList(key));
 316     }
 317 
 318 
 319 
 320 
 321     /* *** Content modification operations (one element at a time) *** */
 322 
 323 
 324     /**
 325      * This method simply calls <tt>put((CompositeData) value)</tt> and
 326      * therefore ignores its <var>key</var> parameter which can be <tt>null</tt>.
 327      *
 328      * @param key an ignored parameter.
 329      * @param value the {@link CompositeData} to put.
 330      *
 331      * @return the value which is put
 332      *
 333      * @throws NullPointerException  if the <var>value</var> is <tt>null</tt>
 334      * @throws ClassCastException if the <var>value</var> is not of
 335      * the type <tt>CompositeData</tt>
 336      * @throws InvalidOpenTypeException if the <var>value</var> does
 337      * not conform to this <tt>TabularData</tt> instance's
 338      * <tt>TabularType</tt> definition
 339      * @throws KeyAlreadyExistsException if the key for the
 340      * <var>value</var> parameter, calculated according to this
 341      * <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
 342      * already maps to an existing value
 343      */
 344     public Object put(Object key, Object value) {
 345         internalPut((CompositeData) value);
 346         return value; // should be return internalPut(...); (5090566)
 347     }
 348 
 349     public void put(CompositeData value) {
 350         internalPut(value);
 351     }
 352 
 353     private CompositeData internalPut(CompositeData value) {
 354         // Check value is not null, value's type is the same as this instance's row type,
 355         // and calculate the value's index according to this instance's tabularType and
 356         // check it is not already used for a mapping in the parent HashMap
 357         //
 358         List<?> index = checkValueAndIndex(value);
 359 
 360         // store the (key, value) mapping in the dataMap HashMap
 361         //
 362         return dataMap.put(index, value);
 363     }
 364 
 365     /**
 366      * This method simply calls <tt>remove((Object[]) key)</tt>.
 367      *
 368      * @param key an <tt>Object[]</tt> representing the key to remove.
 369      *
 370      * @return previous value associated with specified key, or <tt>null</tt>
 371      *         if there was no mapping for key.
 372      *
 373      * @throws NullPointerException  if the <var>key</var> is <tt>null</tt>
 374      * @throws ClassCastException    if the <var>key</var> is not of the type <tt>Object[]</tt>
 375      * @throws InvalidKeyException   if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
 376      *                               <tt>TabularType</tt> definition
 377      */
 378     public Object remove(Object key) {
 379 
 380         return remove((Object[]) key);
 381     }
 382 
 383     /**
 384      * Removes the <tt>CompositeData</tt> value whose index is <var>key</var> from this <tt>TabularData</tt> instance,
 385      * and returns the removed value, or returns <tt>null</tt> if there is no value whose index is <var>key</var>.
 386      *
 387      * @param  key  the index of the value to get in this <tt>TabularData</tt> instance;
 388      *              must be valid with this <tt>TabularData</tt> instance's row type definition;
 389      *              must not be null.
 390      *
 391      * @return previous value associated with specified key, or <tt>null</tt>
 392      *         if there was no mapping for key.
 393      *
 394      * @throws NullPointerException  if the <var>key</var> is <tt>null</tt>
 395      * @throws InvalidKeyException   if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
 396      *                               <tt>TabularType</tt> definition
 397      */
 398     public CompositeData remove(Object[] key) {
 399 
 400         // Check key is not null and valid with tabularType
 401         // (throws NullPointerException, InvalidKeyException)
 402         //
 403         checkKeyType(key);
 404 
 405         // Removes the (key, value) mapping in the parent HashMap
 406         //
 407         return dataMap.remove(Arrays.asList(key));
 408     }
 409 
 410 
 411 
 412     /* ***   Content modification bulk operations   *** */
 413 
 414 
 415     /**
 416      * Add all the values contained in the specified map <var>t</var>
 417      * to this <tt>TabularData</tt> instance.  This method converts
 418      * the collection of values contained in this map into an array of
 419      * <tt>CompositeData</tt> values, if possible, and then call the
 420      * method <tt>putAll(CompositeData[])</tt>. Note that the keys
 421      * used in the specified map <var>t</var> are ignored. This method
 422      * allows, for example to add the content of another
 423      * <tt>TabularData</tt> instance with the same row type (but
 424      * possibly different index names) into this instance.
 425      *
 426      * @param t the map whose values are to be added as new rows to
 427      * this <tt>TabularData</tt> instance; if <var>t</var> is
 428      * <tt>null</tt> or empty, this method returns without doing
 429      * anything.
 430      *
 431      * @throws NullPointerException if a value in <var>t</var> is
 432      * <tt>null</tt>.
 433      * @throws ClassCastException if a value in <var>t</var> is not an
 434      * instance of <tt>CompositeData</tt>.
 435      * @throws InvalidOpenTypeException if a value in <var>t</var>
 436      * does not conform to this <tt>TabularData</tt> instance's row
 437      * type definition.
 438      * @throws KeyAlreadyExistsException if the index for a value in
 439      * <var>t</var>, calculated according to this
 440      * <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
 441      * already maps to an existing value in this instance, or two
 442      * values in <var>t</var> have the same index.
 443      */
 444     public void putAll(Map<?,?> t) {
 445 
 446         // if t is null or empty, just return
 447         //
 448         if ( (t == null) || (t.size() == 0) ) {
 449             return;
 450         }
 451 
 452         // Convert the values in t into an array of <tt>CompositeData</tt>
 453         //
 454         CompositeData[] values;
 455         try {
 456             values =
 457                 t.values().toArray(new CompositeData[t.size()]);
 458         } catch (java.lang.ArrayStoreException e) {
 459             throw new ClassCastException("Map argument t contains values which are not instances of <tt>CompositeData</tt>");
 460         }
 461 
 462         // Add the array of values
 463         //
 464         putAll(values);
 465     }
 466 
 467     /**
 468      * Add all the elements in <var>values</var> to this
 469      * <tt>TabularData</tt> instance.  If any element in
 470      * <var>values</var> does not satisfy the constraints defined in
 471      * {@link #put(CompositeData) <tt>put</tt>}, or if any two
 472      * elements in <var>values</var> have the same index calculated
 473      * according to this <tt>TabularData</tt> instance's
 474      * <tt>TabularType</tt> definition, then an exception describing
 475      * the failure is thrown and no element of <var>values</var> is
 476      * added, thus leaving this <tt>TabularData</tt> instance
 477      * unchanged.
 478      *
 479      * @param values the array of composite data values to be added as
 480      * new rows to this <tt>TabularData</tt> instance; if
 481      * <var>values</var> is <tt>null</tt> or empty, this method
 482      * returns without doing anything.
 483      *
 484      * @throws NullPointerException if an element of <var>values</var>
 485      * is <tt>null</tt>
 486      * @throws InvalidOpenTypeException if an element of
 487      * <var>values</var> does not conform to this
 488      * <tt>TabularData</tt> instance's row type definition (ie its
 489      * <tt>TabularType</tt> definition)
 490      * @throws KeyAlreadyExistsException if the index for an element
 491      * of <var>values</var>, calculated according to this
 492      * <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
 493      * already maps to an existing value in this instance, or two
 494      * elements of <var>values</var> have the same index
 495      */
 496     public void putAll(CompositeData[] values) {
 497 
 498         // if values is null or empty, just return
 499         //
 500         if ( (values == null) || (values.length == 0) ) {
 501             return;
 502         }
 503 
 504         // create the list of indexes corresponding to each value
 505         List<List<?>> indexes =
 506             new ArrayList<List<?>>(values.length + 1);
 507 
 508         // Check all elements in values and build index list
 509         //
 510         List<?> index;
 511         for (int i=0; i<values.length; i++) {
 512             // check value and calculate index
 513             index = checkValueAndIndex(values[i]);
 514             // check index is different of those previously calculated
 515             if (indexes.contains(index)) {
 516                 throw new KeyAlreadyExistsException("Argument elements values["+ i +"] and values["+ indexes.indexOf(index) +
 517                                                     "] have the same indexes, "+
 518                                                     "calculated according to this TabularData instance's tabularType.");
 519             }
 520             // add to index list
 521             indexes.add(index);
 522         }
 523 
 524         // store all (index, value) mappings in the dataMap HashMap
 525         //
 526         for (int i=0; i<values.length; i++) {
 527             dataMap.put(indexes.get(i), values[i]);
 528         }
 529     }
 530 
 531     /**
 532      * Removes all rows from this <code>TabularDataSupport</code> instance.
 533      */
 534     public void clear() {
 535 
 536         dataMap.clear();
 537     }
 538 
 539 
 540 
 541     /* ***  Informational methods from java.util.Map  *** */
 542 
 543     /**
 544      * Returns the number of rows in this <code>TabularDataSupport</code> instance.
 545      *
 546      * @return the number of rows in this <code>TabularDataSupport</code> instance.
 547      */
 548     public int size() {
 549 
 550         return dataMap.size();
 551     }
 552 
 553     /**
 554      * Returns <tt>true</tt> if this <code>TabularDataSupport</code> instance contains no rows.
 555      *
 556      * @return <tt>true</tt> if this <code>TabularDataSupport</code> instance contains no rows.
 557      */
 558     public boolean isEmpty() {
 559 
 560         return (this.size() == 0);
 561     }
 562 
 563 
 564 
 565     /* ***  Collection views from java.util.Map  *** */
 566 
 567     /**
 568      * Returns a set view of the keys contained in the underlying map of this
 569      * {@code TabularDataSupport} instance used to index the rows.
 570      * Each key contained in this {@code Set} is an unmodifiable {@code List<?>}
 571      * so the returned set view is a {@code Set<List<?>>} but is declared as a
 572      * {@code Set<Object>} for compatibility reasons.
 573      * The set is backed by the underlying map of this
 574      * {@code TabularDataSupport} instance, so changes to the
 575      * {@code TabularDataSupport} instance are reflected in the
 576      * set, and vice-versa.


 639      * <b>IMPORTANT NOTICE</b>: Do not use the {@code setValue} method of the
 640      * {@code Map.Entry} elements contained in the returned collection view.
 641      * Doing so would corrupt the index to row mappings contained in this
 642      * {@code TabularDataSupport} instance.
 643      *
 644      * @return a collection view ({@code Set<Map.Entry<List<?>,CompositeData>>})
 645      * of the mappings contained in this map.
 646      * @see java.util.Map.Entry
 647      */
 648     @SuppressWarnings("unchecked")  // historical confusion about the return type
 649     public Set<Map.Entry<Object,Object>> entrySet() {
 650 
 651         return Util.cast(dataMap.entrySet());
 652     }
 653 
 654 
 655     /* ***  Commodity methods from java.lang.Object  *** */
 656 
 657 
 658     /**
 659      * Returns a clone of this <code>TabularDataSupport</code> instance:
 660      * the clone is obtained by calling <tt>super.clone()</tt>, and then cloning the underlying map.
 661      * Only a shallow clone of the underlying map is made, i.e. no cloning of the indexes and row values is made as they are immutable.

 662      */
 663     /* We cannot use covariance here and return TabularDataSupport
 664        because this would fail with existing code that subclassed
 665        TabularDataSupport and overrode Object clone().  It would not
 666        override the new clone().  */
 667     public Object clone() {
 668         try {
 669             TabularDataSupport c = (TabularDataSupport) super.clone();
 670             c.dataMap = new HashMap<Object,CompositeData>(c.dataMap);
 671             return c;
 672         }
 673         catch (CloneNotSupportedException e) {
 674             throw new InternalError(e.toString(), e);
 675         }
 676     }
 677 
 678 
 679     /**
 680      * Compares the specified <var>obj</var> parameter with this <code>TabularDataSupport</code> instance for equality.
 681      * <p>
 682      * Returns <tt>true</tt> if and only if all of the following statements are true:
 683      * <ul>
 684      * <li><var>obj</var> is non null,</li>
 685      * <li><var>obj</var> also implements the <code>TabularData</code> interface,</li>
 686      * <li>their tabular types are equal</li>
 687      * <li>their contents (ie all CompositeData values) are equal.</li>
 688      * </ul>
 689      * This ensures that this <tt>equals</tt> method works properly for <var>obj</var> parameters which are
 690      * different implementations of the <code>TabularData</code> interface.
 691      * <br>&nbsp;
 692      * @param  obj  the object to be compared for equality with this <code>TabularDataSupport</code> instance;
 693      *
 694      * @return  <code>true</code> if the specified object is equal to this <code>TabularDataSupport</code> instance.
 695      */
 696     public boolean equals(Object obj) {
 697 
 698         // if obj is null, return false
 699         //
 700         if (obj == null) {
 701             return false;
 702         }
 703 
 704         // if obj is not a TabularData, return false
 705         //
 706         TabularData other;
 707         try {
 708             other = (TabularData) obj;
 709         } catch (ClassCastException e) {
 710             return false;
 711         }
 712 
 713         // Now, really test for equality between this TabularData implementation and the other:
 714         //


 721         // their contents should be equal:
 722         // . same size
 723         // . values in this instance are in the other (we know there are no duplicate elements possible)
 724         // (row values comparison is enough, because keys are calculated according to tabularType)
 725 
 726         if (this.size() != other.size()) {
 727             return false;
 728         }
 729         for (CompositeData value : dataMap.values()) {
 730             if ( ! other.containsValue(value) ) {
 731                 return false;
 732             }
 733         }
 734 
 735         // All tests for equality were successfull
 736         //
 737         return true;
 738     }
 739 
 740     /**
 741      * Returns the hash code value for this <code>TabularDataSupport</code> instance.
 742      * <p>
 743      * The hash code of a <code>TabularDataSupport</code> instance is the sum of the hash codes
 744      * of all elements of information used in <code>equals</code> comparisons
 745      * (ie: its <i>tabular type</i> and its content, where the content is defined as all the CompositeData values).
 746      * <p>
 747      * This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
 748      * for any two <code>TabularDataSupport</code> instances <code>t1</code> and <code>t2</code>,
 749      * as required by the general contract of the method
 750      * {@link Object#hashCode() Object.hashCode()}.
 751      * <p>
 752      * However, note that another instance of a class implementing the <code>TabularData</code> interface
 753      * may be equal to this <code>TabularDataSupport</code> instance as defined by {@link #equals},
 754      * but may have a different hash code if it is calculated differently.
 755      *
 756      * @return  the hash code value for this <code>TabularDataSupport</code> instance
 757      */
 758    public int hashCode() {
 759 
 760         int result = 0;
 761 
 762         result += this.tabularType.hashCode();
 763         for (Object value : values())
 764             result += value.hashCode();
 765 
 766         return result;
 767 
 768     }
 769 
 770     /**
 771      * Returns a string representation of this <code>TabularDataSupport</code> instance.
 772      * <p>
 773      * The string representation consists of the name of this class (ie <code>javax.management.openmbean.TabularDataSupport</code>),

 774      * the string representation of the tabular type of this instance, and the string representation of the contents
 775      * (ie list the key=value mappings as returned by a call to
 776      * <tt>dataMap.</tt>{@link java.util.HashMap#toString() toString()}).
 777      *
 778      * @return  a string representation of this <code>TabularDataSupport</code> instance
 779      */
 780     public String toString() {
 781 
 782         return new StringBuilder()
 783             .append(this.getClass().getName())
 784             .append("(tabularType=")
 785             .append(tabularType.toString())
 786             .append(",contents=")
 787             .append(dataMap.toString())
 788             .append(")")
 789             .toString();
 790     }
 791 
 792 
 793 
 794 
 795     /* *** TabularDataSupport internal utility methods *** */
 796 
 797 
 798     /**
 799      * Returns the index for value, assuming value is valid for this <tt>TabularData</tt> instance
 800      * (ie value is not null, and its composite type is equal to row type).
 801      *
 802      * The index is a List, and not an array, so that an index.equals(otherIndex) call will actually compare contents,

 803      * not just the objects references as is done for an array object.
 804      *
 805      * The returned List is unmodifiable so that once a row has been put into the dataMap, its index cannot be modified,
 806      * for example by a user that would attempt to modify an index contained in the Set returned by keySet().


 807      */
 808     private List<?> internalCalculateIndex(CompositeData value) {
 809 
 810         return Collections.unmodifiableList(Arrays.asList(value.getAll(this.indexNamesArray)));
 811     }
 812 
 813     /**
 814      * Checks if the specified key is valid for this <tt>TabularData</tt> instance.
 815      *
 816      * @throws  NullPointerException
 817      * @throws  InvalidOpenTypeException
 818      */
 819     private void checkKeyType(Object[] key) {
 820 
 821         // Check key is neither null nor empty
 822         //
 823         if ( (key == null) || (key.length == 0) ) {
 824             throw new NullPointerException("Argument key cannot be null or empty.");
 825         }
 826 
 827         /* Now check key is valid with tabularType index and row type definitions: */
 828 
 829         // key[] should have the size expected for an index
 830         //
 831         if (key.length != this.indexNamesArray.length) {
 832             throw new InvalidKeyException("Argument key's length="+ key.length +
 833                                           " is different from the number of item values, which is "+ indexNamesArray.length +
 834                                           ", specified for the indexing rows in this TabularData instance.");
 835         }
 836 
 837         // each element in key[] should be a value for its corresponding open type specified in rowType
 838         //
 839         OpenType<?> keyElementType;
 840         for (int i=0; i<key.length; i++) {
 841             keyElementType = tabularType.getRowType().getType(this.indexNamesArray[i]);
 842             if ( (key[i] != null) && (! keyElementType.isValue(key[i])) ) {
 843                 throw new InvalidKeyException("Argument element key["+ i +"] is not a value for the open type expected for "+
 844                                               "this element of the index, whose name is \""+ indexNamesArray[i] +
 845                                               "\" and whose open type is "+ keyElementType);
 846             }
 847         }
 848     }
 849 
 850     /**
 851      * Checks the specified value's type is valid for this <tt>TabularData</tt> instance
 852      * (ie value is not null, and its composite type is equal to row type).
 853      *
 854      * @throws  NullPointerException
 855      * @throws  InvalidOpenTypeException
 856      */
 857     private void checkValueType(CompositeData value) {
 858 
 859         // Check value is not null
 860         //
 861         if (value == null) {
 862             throw new NullPointerException("Argument value cannot be null.");
 863         }
 864 
 865         // if value's type is not the same as this instance's row type, throw InvalidOpenTypeException
 866         //
 867         if (!tabularType.getRowType().isValue(value)) {
 868             throw new InvalidOpenTypeException("Argument value's composite type ["+ value.getCompositeType() +
 869                                                "] is not assignable to "+
 870                                                "this TabularData instance's row type ["+ tabularType.getRowType() +"].");
 871         }
 872     }
 873 
 874     /**
 875      * Checks if the specified value can be put (ie added) in this <tt>TabularData</tt> instance
 876      * (ie value is not null, its composite type is equal to row type, and its index is not already used),
 877      * and returns the index calculated for this value.
 878      *
 879      * The index is a List, and not an array, so that an index.equals(otherIndex) call will actually compare contents,
 880      * not just the objects references as is done for an array object.
 881      *
 882      * @throws  NullPointerException
 883      * @throws  InvalidOpenTypeException
 884      * @throws  KeyAlreadyExistsException
 885      */
 886     private List<?> checkValueAndIndex(CompositeData value) {
 887 
 888         // Check value is valid
 889         //
 890         checkValueType(value);
 891 
 892         // Calculate value's index according to this instance's tabularType
 893         // and check it is not already used for a mapping in the parent HashMap
 894         //
 895         List<?> index = internalCalculateIndex(value);




  34 import java.io.IOException;
  35 import java.io.ObjectInputStream;
  36 import java.io.Serializable;
  37 import java.security.AccessController;
  38 import java.util.ArrayList;
  39 import java.util.Arrays;
  40 import java.util.Collection;
  41 import java.util.Collections;
  42 import java.util.HashMap;
  43 import java.util.Iterator;
  44 import java.util.LinkedHashMap;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Set;
  48 
  49 // jmx import
  50 //
  51 
  52 
  53 /**
  54  * The {@code TabularDataSupport} class is the <i>open data</i>
  55  * class which implements the {@code TabularData}
  56  * and the {@code Map} interfaces, and which is internally based on a hash map data structure.
  57  *
  58  * @since 1.5
  59  */
  60 /* It would make much more sense to implement
  61    Map<List<?>,CompositeData> here, but unfortunately we cannot for
  62    compatibility reasons.  If we did that, then we would have to
  63    define e.g.
  64    CompositeData remove(Object)
  65    instead of
  66    Object remove(Object).
  67 
  68    That would mean that if any existing code subclassed
  69    TabularDataSupport and overrode
  70    Object remove(Object),
  71    it would (a) no longer compile and (b) not actually override
  72    CompositeData remove(Object)
  73    in binaries compiled before the change.
  74 */
  75 public class TabularDataSupport
  76     implements TabularData, Map<Object,Object>,


  86      */
  87     // field cannot be final because of clone method
  88     private Map<Object,CompositeData> dataMap;
  89 
  90     /**
  91      * @serial This tabular data instance's tabular type
  92      */
  93     private final TabularType tabularType;
  94 
  95     /**
  96      * The array of item names that define the index used for rows (convenience field)
  97      */
  98     private transient String[] indexNamesArray;
  99 
 100 
 101 
 102     /* *** Constructors *** */
 103 
 104 
 105     /**
 106      * Creates an empty {@code TabularDataSupport} instance
 107      * whose open-type is <var>tabularType</var>,
 108      * and whose underlying {@code HashMap} has a default
 109      * initial capacity (101) and default load factor (0.75).
 110      * <p>
 111      * This constructor simply calls {@code this(tabularType, 101, 0.75f);}
 112      *
 113      * @param  tabularType the <i>tabular type</i> describing this
 114      *         {@code TabularData} instance; cannot be null.
 115      *
 116      * @throws IllegalArgumentException  if the tabular type is null.
 117      */
 118     public TabularDataSupport(TabularType tabularType) {
 119 
 120         this(tabularType, 16, 0.75f);
 121     }
 122 
 123     /**
 124      * Creates an empty {@code TabularDataSupport} instance whose open-type is <var>tabularType</var>,
 125      * and whose underlying {@code HashMap} has the specified initial capacity and load factor.
 126      *
 127      * @param  tabularType               the <i>tabular type</i> describing this {@code TabularData} instance;
 128      *                           cannot be null.
 129      *
 130      * @param  initialCapacity   the initial capacity of the HashMap.
 131      *
 132      * @param  loadFactor        the load factor of the HashMap
 133      *
 134      * @throws IllegalArgumentException  if the initial capacity is less than zero,
 135      *                                   or the load factor is nonpositive,
 136      *                                   or the tabular type is null.
 137      */
 138     public TabularDataSupport(TabularType tabularType, int initialCapacity, float loadFactor) {
 139 
 140         // Check tabularType is not null
 141         //
 142         if (tabularType == null) {
 143             throw new IllegalArgumentException("Argument tabularType cannot be null.");
 144         }
 145 
 146         // Initialize this.tabularType (and indexNamesArray for convenience)
 147         //


 153         // if very unlikely that we might be the server of a 1.3 client.  In
 154         // that case you'll need to set this property.  See CR 6334663.
 155         String useHashMapProp = AccessController.doPrivileged(
 156                 new GetPropertyAction("jmx.tabular.data.hash.map"));
 157         boolean useHashMap = "true".equalsIgnoreCase(useHashMapProp);
 158 
 159         // Construct the empty contents HashMap
 160         //
 161         this.dataMap = useHashMap ?
 162             new HashMap<Object,CompositeData>(initialCapacity, loadFactor) :
 163             new LinkedHashMap<Object, CompositeData>(initialCapacity, loadFactor);
 164     }
 165 
 166 
 167 
 168 
 169     /* *** TabularData specific information methods *** */
 170 
 171 
 172     /**
 173      * Returns the <i>tabular type</i> describing this {@code TabularData} instance.
 174      */
 175     public TabularType getTabularType() {
 176 
 177         return tabularType;
 178     }
 179 
 180     /**
 181      * Calculates the index that would be used in this {@code TabularData} instance to refer
 182      * to the specified composite data <var>value</var> parameter if it were added to this instance.
 183      * This method checks for the type validity of the specified <var>value</var>,
 184      * but does not check if the calculated index is already used
 185      * to refer to a value in this {@code TabularData} instance.
 186      *
 187      * @param  value                      the composite data value whose index in this
 188      *                                    {@code TabularData} instance is to be calculated;
 189      *                                    must be of the same composite type as this instance's row type;
 190      *                                    must not be null.
 191      *
 192      * @return the index that the specified <var>value</var> would have in this {@code TabularData} instance.
 193      *
 194      * @throws NullPointerException       if <var>value</var> is {@code null}.
 195      *
 196      * @throws InvalidOpenTypeException   if <var>value</var> does not conform to this {@code TabularData} instance's
 197      *                                    row type definition.
 198      */
 199     public Object[] calculateIndex(CompositeData value) {
 200 
 201         // Check value is valid
 202         //
 203         checkValueType(value);
 204 
 205         // Return its calculated index
 206         //
 207         return internalCalculateIndex(value).toArray();
 208     }
 209 
 210 
 211 
 212 
 213     /* *** Content information query methods *** */
 214 
 215 
 216     /**
 217      * Returns {@code true} if and only if this {@code TabularData} instance contains a {@code CompositeData} value
 218      * (ie a row) whose index is the specified <var>key</var>. If <var>key</var> cannot be cast to a one dimension array
 219      * of Object instances, this method simply returns {@code false}; otherwise it returns the result of the call to
 220      * {@code this.containsKey((Object[]) key)}.
 221      *
 222      * @param  key  the index value whose presence in this {@code TabularData} instance is to be tested.
 223      *
 224      * @return  {@code true} if this {@code TabularData} indexes a row value with the specified key.
 225      */
 226     public boolean containsKey(Object key) {
 227 
 228         // if key is not an array of Object instances, return false
 229         //
 230         Object[] k;
 231         try {
 232             k = (Object[]) key;
 233         } catch (ClassCastException e) {
 234             return false;
 235         }
 236 
 237         return  this.containsKey(k);
 238     }
 239 
 240     /**
 241      * Returns {@code true} if and only if this {@code TabularData} instance contains a {@code CompositeData} value
 242      * (ie a row) whose index is the specified <var>key</var>. If <var>key</var> is {@code null} or does not conform to
 243      * this {@code TabularData} instance's {@code TabularType} definition, this method simply returns {@code false}.
 244      *
 245      * @param  key  the index value whose presence in this {@code TabularData} instance is to be tested.
 246      *
 247      * @return  {@code true} if this {@code TabularData} indexes a row value with the specified key.
 248      */
 249     public boolean containsKey(Object[] key) {
 250 
 251         return  ( key == null ? false : dataMap.containsKey(Arrays.asList(key)));
 252     }
 253 
 254     /**
 255      * Returns {@code true} if and only if this {@code TabularData} instance contains the specified
 256      * {@code CompositeData} value. If <var>value</var> is {@code null} or does not conform to
 257      * this {@code TabularData} instance's row type definition, this method simply returns {@code false}.
 258      *
 259      * @param  value  the row value whose presence in this {@code TabularData} instance is to be tested.
 260      *
 261      * @return  {@code true} if this {@code TabularData} instance contains the specified row value.
 262      */
 263     public boolean containsValue(CompositeData value) {
 264 
 265         return dataMap.containsValue(value);
 266     }
 267 
 268     /**
 269      * Returns {@code true} if and only if this {@code TabularData} instance contains the specified
 270      * value.
 271      *
 272      * @param  value  the row value whose presence in this {@code TabularData} instance is to be tested.
 273      *
 274      * @return  {@code true} if this {@code TabularData} instance contains the specified row value.
 275      */
 276     public boolean containsValue(Object value) {
 277 
 278         return dataMap.containsValue(value);
 279     }
 280 
 281     /**
 282      * This method simply calls {@code get((Object[]) key)}.
 283      *
 284      * @throws NullPointerException  if the <var>key</var> is {@code null}
 285      * @throws ClassCastException    if the <var>key</var> is not of the type {@code Object[]}
 286      * @throws InvalidKeyException   if the <var>key</var> does not conform
 287      *                               to this {@code TabularData} instance's
 288      *                               {@code TabularType} definition
 289      */
 290     public Object get(Object key) {
 291 
 292         return get((Object[]) key);
 293     }
 294 
 295     /**
 296      * Returns the {@code CompositeData} value whose index is
 297      * <var>key</var>, or {@code null} if there is no value mapping
 298      * to <var>key</var>, in this {@code TabularData} instance.
 299      *
 300      * @param key the index of the value to get in this
 301      * {@code TabularData} instance; must be valid with this
 302      * {@code TabularData} instance's row type definition; must not
 303      * be null.
 304      *
 305      * @return the value corresponding to <var>key</var>.
 306      *
 307      * @throws NullPointerException  if the <var>key</var> is {@code null}
 308      * @throws InvalidKeyException   if the <var>key</var> does not conform
 309      *                               to this {@code TabularData} instance's
 310      *                               {@code TabularType} type definition.
 311      */
 312     public CompositeData get(Object[] key) {
 313 
 314         // Check key is not null and valid with tabularType
 315         // (throws NullPointerException, InvalidKeyException)
 316         //
 317         checkKeyType(key);
 318 
 319         // Return the mapping stored in the parent HashMap
 320         //
 321         return dataMap.get(Arrays.asList(key));
 322     }
 323 
 324 
 325 
 326 
 327     /* *** Content modification operations (one element at a time) *** */
 328 
 329 
 330     /**
 331      * This method simply calls {@code put((CompositeData) value)} and
 332      * therefore ignores its <var>key</var> parameter which can be {@code null}.
 333      *
 334      * @param key an ignored parameter.
 335      * @param value the {@link CompositeData} to put.
 336      *
 337      * @return the value which is put
 338      *
 339      * @throws NullPointerException  if the <var>value</var> is {@code null}
 340      * @throws ClassCastException if the <var>value</var> is not of
 341      * the type {@code CompositeData}
 342      * @throws InvalidOpenTypeException if the <var>value</var> does
 343      * not conform to this {@code TabularData} instance's
 344      * {@code TabularType} definition
 345      * @throws KeyAlreadyExistsException if the key for the
 346      * <var>value</var> parameter, calculated according to this
 347      * {@code TabularData} instance's {@code TabularType} definition
 348      * already maps to an existing value
 349      */
 350     public Object put(Object key, Object value) {
 351         internalPut((CompositeData) value);
 352         return value; // should be return internalPut(...); (5090566)
 353     }
 354 
 355     public void put(CompositeData value) {
 356         internalPut(value);
 357     }
 358 
 359     private CompositeData internalPut(CompositeData value) {
 360         // Check value is not null, value's type is the same as this instance's row type,
 361         // and calculate the value's index according to this instance's tabularType and
 362         // check it is not already used for a mapping in the parent HashMap
 363         //
 364         List<?> index = checkValueAndIndex(value);
 365 
 366         // store the (key, value) mapping in the dataMap HashMap
 367         //
 368         return dataMap.put(index, value);
 369     }
 370 
 371     /**
 372      * This method simply calls {@code remove((Object[]) key)}.
 373      *
 374      * @param key an {@code Object[]} representing the key to remove.
 375      *
 376      * @return previous value associated with specified key, or {@code null}
 377      *         if there was no mapping for key.
 378      *
 379      * @throws NullPointerException  if the <var>key</var> is {@code null}
 380      * @throws ClassCastException    if the <var>key</var> is not of the type {@code Object[]}
 381      * @throws InvalidKeyException   if the <var>key</var> does not conform to this {@code TabularData} instance's
 382      *                               {@code TabularType} definition
 383      */
 384     public Object remove(Object key) {
 385 
 386         return remove((Object[]) key);
 387     }
 388 
 389     /**
 390      * Removes the {@code CompositeData} value whose index is <var>key</var> from this {@code TabularData} instance,
 391      * and returns the removed value, or returns {@code null} if there is no value whose index is <var>key</var>.
 392      *
 393      * @param  key  the index of the value to get in this {@code TabularData} instance;
 394      *              must be valid with this {@code TabularData} instance's row type definition;
 395      *              must not be null.
 396      *
 397      * @return previous value associated with specified key, or {@code null}
 398      *         if there was no mapping for key.
 399      *
 400      * @throws NullPointerException  if the <var>key</var> is {@code null}
 401      * @throws InvalidKeyException   if the <var>key</var> does not conform to this {@code TabularData} instance's
 402      *                               {@code TabularType} definition
 403      */
 404     public CompositeData remove(Object[] key) {
 405 
 406         // Check key is not null and valid with tabularType
 407         // (throws NullPointerException, InvalidKeyException)
 408         //
 409         checkKeyType(key);
 410 
 411         // Removes the (key, value) mapping in the parent HashMap
 412         //
 413         return dataMap.remove(Arrays.asList(key));
 414     }
 415 
 416 
 417 
 418     /* ***   Content modification bulk operations   *** */
 419 
 420 
 421     /**
 422      * Add all the values contained in the specified map <var>t</var>
 423      * to this {@code TabularData} instance.  This method converts
 424      * the collection of values contained in this map into an array of
 425      * {@code CompositeData} values, if possible, and then call the
 426      * method {@code putAll(CompositeData[])}. Note that the keys
 427      * used in the specified map <var>t</var> are ignored. This method
 428      * allows, for example to add the content of another
 429      * {@code TabularData} instance with the same row type (but
 430      * possibly different index names) into this instance.
 431      *
 432      * @param t the map whose values are to be added as new rows to
 433      * this {@code TabularData} instance; if <var>t</var> is
 434      * {@code null} or empty, this method returns without doing
 435      * anything.
 436      *
 437      * @throws NullPointerException if a value in <var>t</var> is
 438      * {@code null}.
 439      * @throws ClassCastException if a value in <var>t</var> is not an
 440      * instance of {@code CompositeData}.
 441      * @throws InvalidOpenTypeException if a value in <var>t</var>
 442      * does not conform to this {@code TabularData} instance's row
 443      * type definition.
 444      * @throws KeyAlreadyExistsException if the index for a value in
 445      * <var>t</var>, calculated according to this
 446      * {@code TabularData} instance's {@code TabularType} definition
 447      * already maps to an existing value in this instance, or two
 448      * values in <var>t</var> have the same index.
 449      */
 450     public void putAll(Map<?,?> t) {
 451 
 452         // if t is null or empty, just return
 453         //
 454         if ( (t == null) || (t.size() == 0) ) {
 455             return;
 456         }
 457 
 458         // Convert the values in t into an array of {@code CompositeData}
 459         //
 460         CompositeData[] values;
 461         try {
 462             values =
 463                 t.values().toArray(new CompositeData[t.size()]);
 464         } catch (java.lang.ArrayStoreException e) {
 465             throw new ClassCastException("Map argument t contains values which are not instances of {@code CompositeData}");
 466         }
 467 
 468         // Add the array of values
 469         //
 470         putAll(values);
 471     }
 472 
 473     /**
 474      * Add all the elements in <var>values</var> to this
 475      * {@code TabularData} instance.  If any element in
 476      * <var>values</var> does not satisfy the constraints defined in
 477      * {@link #put(CompositeData) put}, or if any two
 478      * elements in <var>values</var> have the same index calculated
 479      * according to this {@code TabularData} instance's
 480      * {@code TabularType} definition, then an exception describing
 481      * the failure is thrown and no element of <var>values</var> is
 482      * added, thus leaving this {@code TabularData} instance
 483      * unchanged.
 484      *
 485      * @param values the array of composite data values to be added as
 486      * new rows to this {@code TabularData} instance; if
 487      * <var>values</var> is {@code null} or empty, this method
 488      * returns without doing anything.
 489      *
 490      * @throws NullPointerException if an element of <var>values</var>
 491      * is {@code null}
 492      * @throws InvalidOpenTypeException if an element of
 493      * <var>values</var> does not conform to this
 494      * {@code TabularData} instance's row type definition (ie its
 495      * {@code TabularType} definition)
 496      * @throws KeyAlreadyExistsException if the index for an element
 497      * of <var>values</var>, calculated according to this
 498      * {@code TabularData} instance's {@code TabularType} definition
 499      * already maps to an existing value in this instance, or two
 500      * elements of <var>values</var> have the same index
 501      */
 502     public void putAll(CompositeData[] values) {
 503 
 504         // if values is null or empty, just return
 505         //
 506         if ( (values == null) || (values.length == 0) ) {
 507             return;
 508         }
 509 
 510         // create the list of indexes corresponding to each value
 511         List<List<?>> indexes =
 512             new ArrayList<List<?>>(values.length + 1);
 513 
 514         // Check all elements in values and build index list
 515         //
 516         List<?> index;
 517         for (int i=0; i<values.length; i++) {
 518             // check value and calculate index
 519             index = checkValueAndIndex(values[i]);
 520             // check index is different of those previously calculated
 521             if (indexes.contains(index)) {
 522                 throw new KeyAlreadyExistsException("Argument elements values["+ i +"] and values["+ indexes.indexOf(index) +
 523                                                     "] have the same indexes, "+
 524                                                     "calculated according to this TabularData instance's tabularType.");
 525             }
 526             // add to index list
 527             indexes.add(index);
 528         }
 529 
 530         // store all (index, value) mappings in the dataMap HashMap
 531         //
 532         for (int i=0; i<values.length; i++) {
 533             dataMap.put(indexes.get(i), values[i]);
 534         }
 535     }
 536 
 537     /**
 538      * Removes all rows from this {@code TabularDataSupport} instance.
 539      */
 540     public void clear() {
 541 
 542         dataMap.clear();
 543     }
 544 
 545 
 546 
 547     /* ***  Informational methods from java.util.Map  *** */
 548 
 549     /**
 550      * Returns the number of rows in this {@code TabularDataSupport} instance.
 551      *
 552      * @return the number of rows in this {@code TabularDataSupport} instance.
 553      */
 554     public int size() {
 555 
 556         return dataMap.size();
 557     }
 558 
 559     /**
 560      * Returns {@code true} if this {@code TabularDataSupport} instance contains no rows.
 561      *
 562      * @return {@code true} if this {@code TabularDataSupport} instance contains no rows.
 563      */
 564     public boolean isEmpty() {
 565 
 566         return (this.size() == 0);
 567     }
 568 
 569 
 570 
 571     /* ***  Collection views from java.util.Map  *** */
 572 
 573     /**
 574      * Returns a set view of the keys contained in the underlying map of this
 575      * {@code TabularDataSupport} instance used to index the rows.
 576      * Each key contained in this {@code Set} is an unmodifiable {@code List<?>}
 577      * so the returned set view is a {@code Set<List<?>>} but is declared as a
 578      * {@code Set<Object>} for compatibility reasons.
 579      * The set is backed by the underlying map of this
 580      * {@code TabularDataSupport} instance, so changes to the
 581      * {@code TabularDataSupport} instance are reflected in the
 582      * set, and vice-versa.


 645      * <b>IMPORTANT NOTICE</b>: Do not use the {@code setValue} method of the
 646      * {@code Map.Entry} elements contained in the returned collection view.
 647      * Doing so would corrupt the index to row mappings contained in this
 648      * {@code TabularDataSupport} instance.
 649      *
 650      * @return a collection view ({@code Set<Map.Entry<List<?>,CompositeData>>})
 651      * of the mappings contained in this map.
 652      * @see java.util.Map.Entry
 653      */
 654     @SuppressWarnings("unchecked")  // historical confusion about the return type
 655     public Set<Map.Entry<Object,Object>> entrySet() {
 656 
 657         return Util.cast(dataMap.entrySet());
 658     }
 659 
 660 
 661     /* ***  Commodity methods from java.lang.Object  *** */
 662 
 663 
 664     /**
 665      * Returns a clone of this {@code TabularDataSupport} instance:
 666      * the clone is obtained by calling {@code super.clone()}, and then cloning the underlying map.
 667      * Only a shallow clone of the underlying map is made, i.e.
 668      * no cloning of the indexes and row values is made as they are immutable.
 669      */
 670     /* We cannot use covariance here and return TabularDataSupport
 671        because this would fail with existing code that subclassed
 672        TabularDataSupport and overrode Object clone().  It would not
 673        override the new clone().  */
 674     public Object clone() {
 675         try {
 676             TabularDataSupport c = (TabularDataSupport) super.clone();
 677             c.dataMap = new HashMap<Object,CompositeData>(c.dataMap);
 678             return c;
 679         }
 680         catch (CloneNotSupportedException e) {
 681             throw new InternalError(e.toString(), e);
 682         }
 683     }
 684 
 685 
 686     /**
 687      * Compares the specified <var>obj</var> parameter with this {@code TabularDataSupport} instance for equality.
 688      * <p>
 689      * Returns {@code true} if and only if all of the following statements are true:
 690      * <ul>
 691      * <li><var>obj</var> is non null,</li>
 692      * <li><var>obj</var> also implements the {@code TabularData} interface,</li>
 693      * <li>their tabular types are equal</li>
 694      * <li>their contents (ie all CompositeData values) are equal.</li>
 695      * </ul>
 696      * This ensures that this {@code equals} method works properly for <var>obj</var> parameters which are
 697      * different implementations of the {@code TabularData} interface.
 698      * <br>&nbsp;
 699      * @param  obj  the object to be compared for equality with this {@code TabularDataSupport} instance;
 700      *
 701      * @return  {@code true} if the specified object is equal to this {@code TabularDataSupport} instance.
 702      */
 703     public boolean equals(Object obj) {
 704 
 705         // if obj is null, return false
 706         //
 707         if (obj == null) {
 708             return false;
 709         }
 710 
 711         // if obj is not a TabularData, return false
 712         //
 713         TabularData other;
 714         try {
 715             other = (TabularData) obj;
 716         } catch (ClassCastException e) {
 717             return false;
 718         }
 719 
 720         // Now, really test for equality between this TabularData implementation and the other:
 721         //


 728         // their contents should be equal:
 729         // . same size
 730         // . values in this instance are in the other (we know there are no duplicate elements possible)
 731         // (row values comparison is enough, because keys are calculated according to tabularType)
 732 
 733         if (this.size() != other.size()) {
 734             return false;
 735         }
 736         for (CompositeData value : dataMap.values()) {
 737             if ( ! other.containsValue(value) ) {
 738                 return false;
 739             }
 740         }
 741 
 742         // All tests for equality were successfull
 743         //
 744         return true;
 745     }
 746 
 747     /**
 748      * Returns the hash code value for this {@code TabularDataSupport} instance.
 749      * <p>
 750      * The hash code of a {@code TabularDataSupport} instance is the sum of the hash codes
 751      * of all elements of information used in {@code equals} comparisons
 752      * (ie: its <i>tabular type</i> and its content, where the content is defined as all the CompositeData values).
 753      * <p>
 754      * This ensures that {@code t1.equals(t2)} implies that {@code t1.hashCode()==t2.hashCode()}
 755      * for any two {@code TabularDataSupport} instances {@code t1} and {@code t2},
 756      * as required by the general contract of the method
 757      * {@link Object#hashCode() Object.hashCode()}.
 758      * <p>
 759      * However, note that another instance of a class implementing the {@code TabularData} interface
 760      * may be equal to this {@code TabularDataSupport} instance as defined by {@link #equals},
 761      * but may have a different hash code if it is calculated differently.
 762      *
 763      * @return  the hash code value for this {@code TabularDataSupport} instance
 764      */
 765    public int hashCode() {
 766 
 767         int result = 0;
 768 
 769         result += this.tabularType.hashCode();
 770         for (Object value : values())
 771             result += value.hashCode();
 772 
 773         return result;
 774 
 775     }
 776 
 777     /**
 778      * Returns a string representation of this {@code TabularDataSupport} instance.
 779      * <p>
 780      * The string representation consists of the name of this class
 781      * (ie {@code javax.management.openmbean.TabularDataSupport}),
 782      * the string representation of the tabular type of this instance, and the string representation of the contents
 783      * (ie list the key=value mappings as returned by a call to
 784      * {@code dataMap.}{@link java.util.HashMap#toString() toString()}).
 785      *
 786      * @return  a string representation of this {@code TabularDataSupport} instance
 787      */
 788     public String toString() {
 789 
 790         return new StringBuilder()
 791             .append(this.getClass().getName())
 792             .append("(tabularType=")
 793             .append(tabularType.toString())
 794             .append(",contents=")
 795             .append(dataMap.toString())
 796             .append(")")
 797             .toString();
 798     }
 799 
 800 
 801 
 802 
 803     /* *** TabularDataSupport internal utility methods *** */
 804 
 805 
 806     /**
 807      * Returns the index for value, assuming value is valid for this {@code TabularData} instance
 808      * (ie value is not null, and its composite type is equal to row type).
 809      *
 810      * The index is a List, and not an array, so that an 
 811      * index.equals(otherIndex) call will actually compare contents,
 812      * not just the objects references as is done for an array object.
 813      *
 814      * The returned List is unmodifiable so that once a row has been put
 815      * into the dataMap, its index cannot be modified,
 816      * for example by a user that would attempt to modify an
 817      * index contained in the Set returned by keySet().
 818      */
 819     private List<?> internalCalculateIndex(CompositeData value) {
 820 
 821         return Collections.unmodifiableList(Arrays.asList(value.getAll(this.indexNamesArray)));
 822     }
 823 
 824     /**
 825      * Checks if the specified key is valid for this {@code TabularData} instance.
 826      *
 827      * @throws  NullPointerException
 828      * @throws  InvalidOpenTypeException
 829      */
 830     private void checkKeyType(Object[] key) {
 831 
 832         // Check key is neither null nor empty
 833         //
 834         if ( (key == null) || (key.length == 0) ) {
 835             throw new NullPointerException("Argument key cannot be null or empty.");
 836         }
 837 
 838         /* Now check key is valid with tabularType index and row type definitions: */
 839 
 840         // key[] should have the size expected for an index
 841         //
 842         if (key.length != this.indexNamesArray.length) {
 843             throw new InvalidKeyException("Argument key's length="+ key.length +
 844                                           " is different from the number of item values, which is "+ indexNamesArray.length +
 845                                           ", specified for the indexing rows in this TabularData instance.");
 846         }
 847 
 848         // each element in key[] should be a value for its corresponding open type specified in rowType
 849         //
 850         OpenType<?> keyElementType;
 851         for (int i=0; i<key.length; i++) {
 852             keyElementType = tabularType.getRowType().getType(this.indexNamesArray[i]);
 853             if ( (key[i] != null) && (! keyElementType.isValue(key[i])) ) {
 854                 throw new InvalidKeyException("Argument element key["+ i +"] is not a value for the open type expected for "+
 855                                               "this element of the index, whose name is \""+ indexNamesArray[i] +
 856                                               "\" and whose open type is "+ keyElementType);
 857             }
 858         }
 859     }
 860 
 861     /**
 862      * Checks the specified value's type is valid for this {@code TabularData} instance
 863      * (ie value is not null, and its composite type is equal to row type).
 864      *
 865      * @throws  NullPointerException
 866      * @throws  InvalidOpenTypeException
 867      */
 868     private void checkValueType(CompositeData value) {
 869 
 870         // Check value is not null
 871         //
 872         if (value == null) {
 873             throw new NullPointerException("Argument value cannot be null.");
 874         }
 875 
 876         // if value's type is not the same as this instance's row type, throw InvalidOpenTypeException
 877         //
 878         if (!tabularType.getRowType().isValue(value)) {
 879             throw new InvalidOpenTypeException("Argument value's composite type ["+ value.getCompositeType() +
 880                                                "] is not assignable to "+
 881                                                "this TabularData instance's row type ["+ tabularType.getRowType() +"].");
 882         }
 883     }
 884 
 885     /**
 886      * Checks if the specified value can be put (ie added) in this {@code TabularData} instance
 887      * (ie value is not null, its composite type is equal to row type, and its index is not already used),
 888      * and returns the index calculated for this value.
 889      *
 890      * The index is a List, and not an array, so that an index.equals(otherIndex) call will actually compare contents,
 891      * not just the objects references as is done for an array object.
 892      *
 893      * @throws  NullPointerException
 894      * @throws  InvalidOpenTypeException
 895      * @throws  KeyAlreadyExistsException
 896      */
 897     private List<?> checkValueAndIndex(CompositeData value) {
 898 
 899         // Check value is valid
 900         //
 901         checkValueType(value);
 902 
 903         // Calculate value's index according to this instance's tabularType
 904         // and check it is not already used for a mapping in the parent HashMap
 905         //
 906         List<?> index = internalCalculateIndex(value);


< prev index next >