jdk/src/share/classes/javax/management/openmbean/TabularDataSupport.java
changeset 2 90ce3da70b43
child 1018 9f07e65e9653
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 2000-2007 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 
       
    27 package javax.management.openmbean;
       
    28 
       
    29 
       
    30 // java import
       
    31 //
       
    32 import java.io.IOException;
       
    33 import java.io.ObjectInputStream;
       
    34 import java.io.Serializable;
       
    35 import java.util.ArrayList;
       
    36 import java.util.Arrays;
       
    37 import java.util.Collection;
       
    38 import java.util.Collections;
       
    39 import java.util.HashMap;
       
    40 import java.util.Iterator;
       
    41 import java.util.List;
       
    42 import java.util.Map;
       
    43 import java.util.Set;
       
    44 
       
    45 // jmx import
       
    46 //
       
    47 
       
    48 
       
    49 /**
       
    50  * The <tt>TabularDataSupport</tt> class is the <i>open data</i> class which implements the <tt>TabularData</tt>
       
    51  * and the <tt>Map</tt> interfaces, and which is internally based on a hash map data structure.
       
    52  *
       
    53  * @since 1.5
       
    54  */
       
    55 /* It would make much more sense to implement
       
    56    Map<List<?>,CompositeData> here, but unfortunately we cannot for
       
    57    compatibility reasons.  If we did that, then we would have to
       
    58    define e.g.
       
    59    CompositeData remove(Object)
       
    60    instead of
       
    61    Object remove(Object).
       
    62 
       
    63    That would mean that if any existing code subclassed
       
    64    TabularDataSupport and overrode
       
    65    Object remove(Object),
       
    66    it would (a) no longer compile and (b) not actually override
       
    67    CompositeData remove(Object)
       
    68    in binaries compiled before the change.
       
    69 */
       
    70 public class TabularDataSupport
       
    71     implements TabularData, Map<Object,Object>,
       
    72                Cloneable, Serializable {
       
    73 
       
    74 
       
    75     /* Serial version */
       
    76     static final long serialVersionUID = 5720150593236309827L;
       
    77 
       
    78 
       
    79     /**
       
    80      * @serial This tabular data instance's contents: a {@link HashMap}
       
    81      */
       
    82     private Map<Object,CompositeData> dataMap;
       
    83 
       
    84     /**
       
    85      * @serial This tabular data instance's tabular type
       
    86      */
       
    87     private TabularType tabularType;
       
    88 
       
    89     /**
       
    90      * The array of item names that define the index used for rows (convenience field)
       
    91      */
       
    92     private transient String[] indexNamesArray;
       
    93 
       
    94 
       
    95 
       
    96     /* *** Constructors *** */
       
    97 
       
    98 
       
    99     /**
       
   100      * Creates an empty <tt>TabularDataSupport</tt> instance whose open-type is <var>tabularType</var>,
       
   101      * and whose underlying <tt>HashMap</tt> has a default initial capacity (101) and default load factor (0.75).
       
   102      * <p>
       
   103      * This constructor simply calls <tt>this(tabularType, 101, 0.75f);</tt>
       
   104      *
       
   105      * @param  tabularType               the <i>tabular type</i> describing this <tt>TabularData</tt> instance;
       
   106      *                                   cannot be null.
       
   107      *
       
   108      * @throws IllegalArgumentException  if the tabular type is null.
       
   109      */
       
   110     public TabularDataSupport(TabularType tabularType) {
       
   111 
       
   112         this(tabularType, 101, 0.75f);
       
   113     }
       
   114 
       
   115     /**
       
   116      * Creates an empty <tt>TabularDataSupport</tt> instance whose open-type is <var>tabularType</var>,
       
   117      * and whose underlying <tt>HashMap</tt> has the specified initial capacity and load factor.
       
   118      *
       
   119      * @param  tabularType               the <i>tabular type</i> describing this <tt>TabularData</tt> instance;
       
   120      *                           cannot be null.
       
   121      *
       
   122      * @param  initialCapacity   the initial capacity of the HashMap.
       
   123      *
       
   124      * @param  loadFactor        the load factor of the HashMap
       
   125      *
       
   126      * @throws IllegalArgumentException  if the initial capacity is less than zero,
       
   127      *                                   or the load factor is nonpositive,
       
   128      *                                   or the tabular type is null.
       
   129      */
       
   130     public TabularDataSupport(TabularType tabularType, int initialCapacity, float loadFactor) {
       
   131 
       
   132         // Check tabularType is not null
       
   133         //
       
   134         if (tabularType == null) {
       
   135             throw new IllegalArgumentException("Argument tabularType cannot be null.");
       
   136         }
       
   137 
       
   138         // Initialize this.tabularType (and indexNamesArray for convenience)
       
   139         //
       
   140         this.tabularType = tabularType;
       
   141         List<String> tmpNames = tabularType.getIndexNames();
       
   142         this.indexNamesArray = tmpNames.toArray(new String[tmpNames.size()]);
       
   143 
       
   144         // Construct the empty contents HashMap
       
   145         //
       
   146         this.dataMap =
       
   147             new HashMap<Object,CompositeData>(initialCapacity, loadFactor);
       
   148     }
       
   149 
       
   150 
       
   151 
       
   152 
       
   153     /* *** TabularData specific information methods *** */
       
   154 
       
   155 
       
   156     /**
       
   157      * Returns the <i>tabular type</i> describing this <tt>TabularData</tt> instance.
       
   158      */
       
   159     public TabularType getTabularType() {
       
   160 
       
   161         return tabularType;
       
   162     }
       
   163 
       
   164     /**
       
   165      * Calculates the index that would be used in this <tt>TabularData</tt> instance to refer to the specified
       
   166      * composite data <var>value</var> parameter if it were added to this instance.
       
   167      * This method checks for the type validity of the specified <var>value</var>,
       
   168      * but does not check if the calculated index is already used to refer to a value in this <tt>TabularData</tt> instance.
       
   169      *
       
   170      * @param  value                      the composite data value whose index in this
       
   171      *                                    <tt>TabularData</tt> instance is to be calculated;
       
   172      *                                    must be of the same composite type as this instance's row type;
       
   173      *                                    must not be null.
       
   174      *
       
   175      * @return the index that the specified <var>value</var> would have in this <tt>TabularData</tt> instance.
       
   176      *
       
   177      * @throws NullPointerException       if <var>value</var> is <tt>null</tt>.
       
   178      *
       
   179      * @throws InvalidOpenTypeException   if <var>value</var> does not conform to this <tt>TabularData</tt> instance's
       
   180      *                                    row type definition.
       
   181      */
       
   182     public Object[] calculateIndex(CompositeData value) {
       
   183 
       
   184         // Check value is valid
       
   185         //
       
   186         checkValueType(value);
       
   187 
       
   188         // Return its calculated index
       
   189         //
       
   190         return internalCalculateIndex(value).toArray();
       
   191     }
       
   192 
       
   193 
       
   194 
       
   195 
       
   196     /* *** Content information query methods *** */
       
   197 
       
   198 
       
   199     /**
       
   200      * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains a <tt>CompositeData</tt> value
       
   201      * (ie a row) whose index is the specified <var>key</var>. If <var>key</var> cannot be cast to a one dimension array
       
   202      * of Object instances, this method simply returns <tt>false</tt>; otherwise it returns the the result of the call to
       
   203      * <tt>this.containsKey((Object[]) key)</tt>.
       
   204      *
       
   205      * @param  key  the index value whose presence in this <tt>TabularData</tt> instance is to be tested.
       
   206      *
       
   207      * @return  <tt>true</tt> if this <tt>TabularData</tt> indexes a row value with the specified key.
       
   208      */
       
   209     public boolean containsKey(Object key) {
       
   210 
       
   211         // if key is not an array of Object instances, return false
       
   212         //
       
   213         Object[] k;
       
   214         try {
       
   215             k = (Object[]) key;
       
   216         } catch (ClassCastException e) {
       
   217             return false;
       
   218         }
       
   219 
       
   220         return  this.containsKey(k);
       
   221     }
       
   222 
       
   223     /**
       
   224      * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains a <tt>CompositeData</tt> value
       
   225      * (ie a row) whose index is the specified <var>key</var>. If <var>key</var> is <tt>null</tt> or does not conform to
       
   226      * this <tt>TabularData</tt> instance's <tt>TabularType</tt> definition, this method simply returns <tt>false</tt>.
       
   227      *
       
   228      * @param  key  the index value whose presence in this <tt>TabularData</tt> instance is to be tested.
       
   229      *
       
   230      * @return  <tt>true</tt> if this <tt>TabularData</tt> indexes a row value with the specified key.
       
   231      */
       
   232     public boolean containsKey(Object[] key) {
       
   233 
       
   234         return  ( key == null ? false : dataMap.containsKey(Arrays.asList(key)));
       
   235     }
       
   236 
       
   237     /**
       
   238      * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains the specified
       
   239      * <tt>CompositeData</tt> value. If <var>value</var> is <tt>null</tt> or does not conform to
       
   240      * this <tt>TabularData</tt> instance's row type definition, this method simply returns <tt>false</tt>.
       
   241      *
       
   242      * @param  value  the row value whose presence in this <tt>TabularData</tt> instance is to be tested.
       
   243      *
       
   244      * @return  <tt>true</tt> if this <tt>TabularData</tt> instance contains the specified row value.
       
   245      */
       
   246     public boolean containsValue(CompositeData value) {
       
   247 
       
   248         return dataMap.containsValue(value);
       
   249     }
       
   250 
       
   251     /**
       
   252      * Returns <tt>true</tt> if and only if this <tt>TabularData</tt> instance contains the specified
       
   253      * value.
       
   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(Object value) {
       
   260 
       
   261         return dataMap.containsValue(value);
       
   262     }
       
   263 
       
   264     /**
       
   265      * This method simply calls <tt>get((Object[]) key)</tt>.
       
   266      *
       
   267      * @throws NullPointerException  if the <var>key</var> is <tt>null</tt>
       
   268      * @throws ClassCastException    if the <var>key</var> is not of the type <tt>Object[]</tt>
       
   269      * @throws InvalidKeyException   if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
       
   270      *                               <tt>TabularType</tt> definition
       
   271      */
       
   272     public Object get(Object key) {
       
   273 
       
   274         return get((Object[]) key);
       
   275     }
       
   276 
       
   277     /**
       
   278      * Returns the <tt>CompositeData</tt> value whose index is
       
   279      * <var>key</var>, or <tt>null</tt> if there is no value mapping
       
   280      * to <var>key</var>, in this <tt>TabularData</tt> instance.
       
   281      *
       
   282      * @param key the index of the value to get in this
       
   283      * <tt>TabularData</tt> instance; * must be valid with this
       
   284      * <tt>TabularData</tt> instance's row type definition; * must not
       
   285      * be null.
       
   286      *
       
   287      * @return the value corresponding to <var>key</var>.
       
   288      *
       
   289      * @throws NullPointerException  if the <var>key</var> is <tt>null</tt>
       
   290      * @throws InvalidKeyException   if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
       
   291      *                               <tt>TabularType</tt> type definition.
       
   292      */
       
   293     public CompositeData get(Object[] key) {
       
   294 
       
   295         // Check key is not null and valid with tabularType
       
   296         // (throws NullPointerException, InvalidKeyException)
       
   297         //
       
   298         checkKeyType(key);
       
   299 
       
   300         // Return the mapping stored in the parent HashMap
       
   301         //
       
   302         return dataMap.get(Arrays.asList(key));
       
   303     }
       
   304 
       
   305 
       
   306 
       
   307 
       
   308     /* *** Content modification operations (one element at a time) *** */
       
   309 
       
   310 
       
   311     /**
       
   312      * This method simply calls <tt>put((CompositeData) value)</tt> and
       
   313      * therefore ignores its <var>key</var> parameter which can be <tt>null</tt>.
       
   314      *
       
   315      * @param key an ignored parameter.
       
   316      * @param value the {@link CompositeData} to put.
       
   317      *
       
   318      * @return the value which is put
       
   319      *
       
   320      * @throws NullPointerException  if the <var>value</var> is <tt>null</tt>
       
   321      * @throws ClassCastException if the <var>value</var> is not of
       
   322      * the type <tt>CompositeData</tt>
       
   323      * @throws InvalidOpenTypeException if the <var>value</var> does
       
   324      * not conform to this <tt>TabularData</tt> instance's
       
   325      * <tt>TabularType</tt> definition
       
   326      * @throws KeyAlreadyExistsException if the key for the
       
   327      * <var>value</var> parameter, calculated according to this
       
   328      * <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
       
   329      * already maps to an existing value
       
   330      */
       
   331     public Object put(Object key, Object value) {
       
   332         internalPut((CompositeData) value);
       
   333         return value; // should be return internalPut(...); (5090566)
       
   334     }
       
   335 
       
   336     public void put(CompositeData value) {
       
   337         internalPut(value);
       
   338     }
       
   339 
       
   340     private CompositeData internalPut(CompositeData value) {
       
   341         // Check value is not null, value's type is the same as this instance's row type,
       
   342         // and calculate the value's index according to this instance's tabularType and
       
   343         // check it is not already used for a mapping in the parent HashMap
       
   344         //
       
   345         List<?> index = checkValueAndIndex(value);
       
   346 
       
   347         // store the (key, value) mapping in the dataMap HashMap
       
   348         //
       
   349         return dataMap.put(index, value);
       
   350     }
       
   351 
       
   352     /**
       
   353      * This method simply calls <tt>remove((Object[]) key)</tt>.
       
   354      *
       
   355      * @param key an <tt>Object[]</tt> representing the key to remove.
       
   356      *
       
   357      * @return previous value associated with specified key, or <tt>null</tt>
       
   358      *         if there was no mapping for key.
       
   359      *
       
   360      * @throws NullPointerException  if the <var>key</var> is <tt>null</tt>
       
   361      * @throws ClassCastException    if the <var>key</var> is not of the type <tt>Object[]</tt>
       
   362      * @throws InvalidKeyException   if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
       
   363      *                               <tt>TabularType</tt> definition
       
   364      */
       
   365     public Object remove(Object key) {
       
   366 
       
   367         return remove((Object[]) key);
       
   368     }
       
   369 
       
   370     /**
       
   371      * Removes the <tt>CompositeData</tt> value whose index is <var>key</var> from this <tt>TabularData</tt> instance,
       
   372      * and returns the removed value, or returns <tt>null</tt> if there is no value whose index is <var>key</var>.
       
   373      *
       
   374      * @param  key  the index of the value to get in this <tt>TabularData</tt> instance;
       
   375      *              must be valid with this <tt>TabularData</tt> instance's row type definition;
       
   376      *              must not be null.
       
   377      *
       
   378      * @return previous value associated with specified key, or <tt>null</tt>
       
   379      *         if there was no mapping for key.
       
   380      *
       
   381      * @throws NullPointerException  if the <var>key</var> is <tt>null</tt>
       
   382      * @throws InvalidKeyException   if the <var>key</var> does not conform to this <tt>TabularData</tt> instance's
       
   383      *                               <tt>TabularType</tt> definition
       
   384      */
       
   385     public CompositeData remove(Object[] key) {
       
   386 
       
   387         // Check key is not null and valid with tabularType
       
   388         // (throws NullPointerException, InvalidKeyException)
       
   389         //
       
   390         checkKeyType(key);
       
   391 
       
   392         // Removes the (key, value) mapping in the parent HashMap
       
   393         //
       
   394         return dataMap.remove(Arrays.asList(key));
       
   395     }
       
   396 
       
   397 
       
   398 
       
   399     /* ***   Content modification bulk operations   *** */
       
   400 
       
   401 
       
   402     /**
       
   403      * Add all the values contained in the specified map <var>t</var>
       
   404      * to this <tt>TabularData</tt> instance.  This method converts
       
   405      * the collection of values contained in this map into an array of
       
   406      * <tt>CompositeData</tt> values, if possible, and then call the
       
   407      * method <tt>putAll(CompositeData[])</tt>. Note that the keys
       
   408      * used in the specified map <var>t</var> are ignored. This method
       
   409      * allows, for example to add the content of another
       
   410      * <tt>TabularData</tt> instance with the same row type (but
       
   411      * possibly different index names) into this instance.
       
   412      *
       
   413      * @param t the map whose values are to be added as new rows to
       
   414      * this <tt>TabularData</tt> instance; if <var>t</var> is
       
   415      * <tt>null</tt> or empty, this method returns without doing
       
   416      * anything.
       
   417      *
       
   418      * @throws NullPointerException if a value in <var>t</var> is
       
   419      * <tt>null</tt>.
       
   420      * @throws ClassCastException if a value in <var>t</var> is not an
       
   421      * instance of <tt>CompositeData</tt>.
       
   422      * @throws InvalidOpenTypeException if a value in <var>t</var>
       
   423      * does not conform to this <tt>TabularData</tt> instance's row
       
   424      * type definition.
       
   425      * @throws KeyAlreadyExistsException if the index for a value in
       
   426      * <var>t</var>, calculated according to this
       
   427      * <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
       
   428      * already maps to an existing value in this instance, or two
       
   429      * values in <var>t</var> have the same index.
       
   430      */
       
   431     public void putAll(Map<?,?> t) {
       
   432 
       
   433         // if t is null or empty, just return
       
   434         //
       
   435         if ( (t == null) || (t.size() == 0) ) {
       
   436             return;
       
   437         }
       
   438 
       
   439         // Convert the values in t into an array of <tt>CompositeData</tt>
       
   440         //
       
   441         CompositeData[] values;
       
   442         try {
       
   443             values =
       
   444                 t.values().toArray(new CompositeData[t.size()]);
       
   445         } catch (java.lang.ArrayStoreException e) {
       
   446             throw new ClassCastException("Map argument t contains values which are not instances of <tt>CompositeData</tt>");
       
   447         }
       
   448 
       
   449         // Add the array of values
       
   450         //
       
   451         putAll(values);
       
   452     }
       
   453 
       
   454     /**
       
   455      * Add all the elements in <var>values</var> to this
       
   456      * <tt>TabularData</tt> instance.  If any element in
       
   457      * <var>values</var> does not satisfy the constraints defined in
       
   458      * {@link #put(CompositeData) <tt>put</tt>}, or if any two
       
   459      * elements in <var>values</var> have the same index calculated
       
   460      * according to this <tt>TabularData</tt> instance's
       
   461      * <tt>TabularType</tt> definition, then an exception describing
       
   462      * the failure is thrown and no element of <var>values</var> is
       
   463      * added, thus leaving this <tt>TabularData</tt> instance
       
   464      * unchanged.
       
   465      *
       
   466      * @param values the array of composite data values to be added as
       
   467      * new rows to this <tt>TabularData</tt> instance; if
       
   468      * <var>values</var> is <tt>null</tt> or empty, this method
       
   469      * returns without doing anything.
       
   470      *
       
   471      * @throws NullPointerException if an element of <var>values</var>
       
   472      * is <tt>null</tt>
       
   473      * @throws InvalidOpenTypeException if an element of
       
   474      * <var>values</var> does not conform to this
       
   475      * <tt>TabularData</tt> instance's row type definition (ie its
       
   476      * <tt>TabularType</tt> definition)
       
   477      * @throws KeyAlreadyExistsException if the index for an element
       
   478      * of <var>values</var>, calculated according to this
       
   479      * <tt>TabularData</tt> instance's <tt>TabularType</tt> definition
       
   480      * already maps to an existing value in this instance, or two
       
   481      * elements of <var>values</var> have the same index
       
   482      */
       
   483     public void putAll(CompositeData[] values) {
       
   484 
       
   485         // if values is null or empty, just return
       
   486         //
       
   487         if ( (values == null) || (values.length == 0) ) {
       
   488             return;
       
   489         }
       
   490 
       
   491         // create the list of indexes corresponding to each value
       
   492         List<List<?>> indexes =
       
   493             new ArrayList<List<?>>(values.length + 1);
       
   494 
       
   495         // Check all elements in values and build index list
       
   496         //
       
   497         List<?> index;
       
   498         for (int i=0; i<values.length; i++) {
       
   499             // check value and calculate index
       
   500             index = checkValueAndIndex(values[i]);
       
   501             // check index is different of those previously calculated
       
   502             if (indexes.contains(index)) {
       
   503                 throw new KeyAlreadyExistsException("Argument elements values["+ i +"] and values["+ indexes.indexOf(index) +
       
   504                                                     "] have the same indexes, "+
       
   505                                                     "calculated according to this TabularData instance's tabularType.");
       
   506             }
       
   507             // add to index list
       
   508             indexes.add(index);
       
   509         }
       
   510 
       
   511         // store all (index, value) mappings in the dataMap HashMap
       
   512         //
       
   513         for (int i=0; i<values.length; i++) {
       
   514             dataMap.put(indexes.get(i), values[i]);
       
   515         }
       
   516     }
       
   517 
       
   518     /**
       
   519      * Removes all rows from this <code>TabularDataSupport</code> instance.
       
   520      */
       
   521     public void clear() {
       
   522 
       
   523         dataMap.clear();
       
   524     }
       
   525 
       
   526 
       
   527 
       
   528     /* ***  Informational methods from java.util.Map  *** */
       
   529 
       
   530     /**
       
   531      * Returns the number of rows in this <code>TabularDataSupport</code> instance.
       
   532      *
       
   533      * @return the number of rows in this <code>TabularDataSupport</code> instance.
       
   534      */
       
   535     public int size() {
       
   536 
       
   537         return dataMap.size();
       
   538     }
       
   539 
       
   540     /**
       
   541      * Returns <tt>true</tt> if this <code>TabularDataSupport</code> instance contains no rows.
       
   542      *
       
   543      * @return <tt>true</tt> if this <code>TabularDataSupport</code> instance contains no rows.
       
   544      */
       
   545     public boolean isEmpty() {
       
   546 
       
   547         return (this.size() == 0);
       
   548     }
       
   549 
       
   550 
       
   551 
       
   552     /* ***  Collection views from java.util.Map  *** */
       
   553 
       
   554     /**
       
   555      * Returns a set view of the keys contained in the underlying map of this
       
   556      * {@code TabularDataSupport} instance used to index the rows.
       
   557      * Each key contained in this {@code Set} is an unmodifiable {@code List<?>}
       
   558      * so the returned set view is a {@code Set<List<?>>} but is declared as a
       
   559      * {@code Set<Object>} for compatibility reasons.
       
   560      * The set is backed by the underlying map of this
       
   561      * {@code TabularDataSupport} instance, so changes to the
       
   562      * {@code TabularDataSupport} instance are reflected in the
       
   563      * set, and vice-versa.
       
   564      *
       
   565      * The set supports element removal, which removes the corresponding
       
   566      * row from this {@code TabularDataSupport} instance, via the
       
   567      * {@link Iterator#remove}, {@link Set#remove}, {@link Set#removeAll},
       
   568      * {@link Set#retainAll}, and {@link Set#clear} operations. It does
       
   569      *  not support the {@link Set#add} or {@link Set#addAll} operations.
       
   570      *
       
   571      * @return a set view ({@code Set<List<?>>}) of the keys used to index
       
   572      * the rows of this {@code TabularDataSupport} instance.
       
   573      */
       
   574     public Set<Object> keySet() {
       
   575 
       
   576         return dataMap.keySet() ;
       
   577     }
       
   578 
       
   579     /**
       
   580      * Returns a collection view of the rows contained in this
       
   581      * {@code TabularDataSupport} instance. The returned {@code Collection}
       
   582      * is a {@code Collection<CompositeData>} but is declared as a
       
   583      * {@code Collection<Object>} for compatibility reasons.
       
   584      * The returned collection can be used to iterate over the values.
       
   585      * The collection is backed by the underlying map, so changes to the
       
   586      * {@code TabularDataSupport} instance are reflected in the collection,
       
   587      * and vice-versa.
       
   588      *
       
   589      * The collection supports element removal, which removes the corresponding
       
   590      * index to row mapping from this {@code TabularDataSupport} instance, via
       
   591      * the {@link Iterator#remove}, {@link Collection#remove},
       
   592      * {@link Collection#removeAll}, {@link Collection#retainAll},
       
   593      * and {@link Collection#clear} operations. It does not support
       
   594      * the {@link Collection#add} or {@link Collection#addAll} operations.
       
   595      *
       
   596      * @return a collection view ({@code Collection<CompositeData>}) of
       
   597      * the values contained in this {@code TabularDataSupport} instance.
       
   598      */
       
   599     @SuppressWarnings("unchecked")  // historical confusion about the return type
       
   600     public Collection<Object> values() {
       
   601 
       
   602         return (Collection) dataMap.values() ;
       
   603     }
       
   604 
       
   605 
       
   606     /**
       
   607      * Returns a collection view of the index to row mappings
       
   608      * contained in this {@code TabularDataSupport} instance.
       
   609      * Each element in the returned collection is
       
   610      * a {@code Map.Entry<List<?>,CompositeData>} but
       
   611      * is declared as a {@code Map.Entry<Object,Object>}
       
   612      * for compatibility reasons. Each of the map entry
       
   613      * keys is an unmodifiable {@code List<?>}.
       
   614      * The collection is backed by the underlying map of this
       
   615      * {@code TabularDataSupport} instance, so changes to the
       
   616      * {@code TabularDataSupport} instance are reflected in
       
   617      * the collection, and vice-versa.
       
   618      * The collection supports element removal, which removes
       
   619      * the corresponding mapping from the map, via the
       
   620      * {@link Iterator#remove}, {@link Collection#remove},
       
   621      * {@link Collection#removeAll}, {@link Collection#retainAll},
       
   622      * and {@link Collection#clear} operations. It does not support
       
   623      * the {@link Collection#add} or {@link Collection#addAll}
       
   624      * operations.
       
   625      * <p>
       
   626      * <b>IMPORTANT NOTICE</b>: Do not use the {@code setValue} method of the
       
   627      * {@code Map.Entry} elements contained in the returned collection view.
       
   628      * Doing so would corrupt the index to row mappings contained in this
       
   629      * {@code TabularDataSupport} instance.
       
   630      *
       
   631      * @return a collection view ({@code Set<Map.Entry<List<?>,CompositeData>>})
       
   632      * of the mappings contained in this map.
       
   633      * @see java.util.Map.Entry
       
   634      */
       
   635     @SuppressWarnings("unchecked")  // historical confusion about the return type
       
   636     public Set<Map.Entry<Object,Object>> entrySet() {
       
   637 
       
   638         return (Set) dataMap.entrySet();
       
   639     }
       
   640 
       
   641 
       
   642     /* ***  Commodity methods from java.lang.Object  *** */
       
   643 
       
   644 
       
   645     /**
       
   646      * Returns a clone of this <code>TabularDataSupport</code> instance:
       
   647      * the clone is obtained by calling <tt>super.clone()</tt>, and then cloning the underlying map.
       
   648      * 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.
       
   649      */
       
   650     /* We cannot use covariance here and return TabularDataSupport
       
   651        because this would fail with existing code that subclassed
       
   652        TabularDataSupport and overrode Object clone().  It would not
       
   653        override the new clone().  */
       
   654     public Object clone() {
       
   655         try {
       
   656             TabularDataSupport c = (TabularDataSupport) super.clone();
       
   657             c.dataMap = new HashMap<Object,CompositeData>(c.dataMap);
       
   658             return c;
       
   659         }
       
   660         catch (CloneNotSupportedException e) {
       
   661             throw new InternalError(e.toString());
       
   662         }
       
   663     }
       
   664 
       
   665 
       
   666     /**
       
   667      * Compares the specified <var>obj</var> parameter with this <code>TabularDataSupport</code> instance for equality.
       
   668      * <p>
       
   669      * Returns <tt>true</tt> if and only if all of the following statements are true:
       
   670      * <ul>
       
   671      * <li><var>obj</var> is non null,</li>
       
   672      * <li><var>obj</var> also implements the <code>TabularData</code> interface,</li>
       
   673      * <li>their tabular types are equal</li>
       
   674      * <li>their contents (ie all CompositeData values) are equal.</li>
       
   675      * </ul>
       
   676      * This ensures that this <tt>equals</tt> method works properly for <var>obj</var> parameters which are
       
   677      * different implementations of the <code>TabularData</code> interface.
       
   678      * <br>&nbsp;
       
   679      * @param  obj  the object to be compared for equality with this <code>TabularDataSupport</code> instance;
       
   680      *
       
   681      * @return  <code>true</code> if the specified object is equal to this <code>TabularDataSupport</code> instance.
       
   682      */
       
   683     public boolean equals(Object obj) {
       
   684 
       
   685         // if obj is null, return false
       
   686         //
       
   687         if (obj == null) {
       
   688             return false;
       
   689         }
       
   690 
       
   691         // if obj is not a TabularData, return false
       
   692         //
       
   693         TabularData other;
       
   694         try {
       
   695             other = (TabularData) obj;
       
   696         } catch (ClassCastException e) {
       
   697             return false;
       
   698         }
       
   699 
       
   700         // Now, really test for equality between this TabularData implementation and the other:
       
   701         //
       
   702 
       
   703         // their tabularType should be equal
       
   704         if ( ! this.getTabularType().equals(other.getTabularType()) ) {
       
   705             return false;
       
   706         }
       
   707 
       
   708         // their contents should be equal:
       
   709         // . same size
       
   710         // . values in this instance are in the other (we know there are no duplicate elements possible)
       
   711         // (row values comparison is enough, because keys are calculated according to tabularType)
       
   712 
       
   713         if (this.size() != other.size()) {
       
   714             return false;
       
   715         }
       
   716         for (Iterator iter = this.values().iterator(); iter.hasNext();  ) {
       
   717             CompositeData value = (CompositeData) iter.next();
       
   718             if ( ! other.containsValue(value) ) {
       
   719                 return false;
       
   720             }
       
   721         }
       
   722 
       
   723         // All tests for equality were successfull
       
   724         //
       
   725         return true;
       
   726     }
       
   727 
       
   728     /**
       
   729      * Returns the hash code value for this <code>TabularDataSupport</code> instance.
       
   730      * <p>
       
   731      * The hash code of a <code>TabularDataSupport</code> instance is the sum of the hash codes
       
   732      * of all elements of information used in <code>equals</code> comparisons
       
   733      * (ie: its <i>tabular type</i> and its content, where the content is defined as all the CompositeData values).
       
   734      * <p>
       
   735      * This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
       
   736      * for any two <code>TabularDataSupport</code> instances <code>t1</code> and <code>t2</code>,
       
   737      * as required by the general contract of the method
       
   738      * {@link Object#hashCode() Object.hashCode()}.
       
   739      * <p>
       
   740      * However, note that another instance of a class implementing the <code>TabularData</code> interface
       
   741      * may be equal to this <code>TabularDataSupport</code> instance as defined by {@link #equals},
       
   742      * but may have a different hash code if it is calculated differently.
       
   743      *
       
   744      * @return  the hash code value for this <code>TabularDataSupport</code> instance
       
   745      */
       
   746    public int hashCode() {
       
   747 
       
   748         int result = 0;
       
   749 
       
   750         result += this.tabularType.hashCode();
       
   751         for (Iterator iter = this.values().iterator(); iter.hasNext();  ) {
       
   752             result += ((CompositeData)iter.next()).hashCode();
       
   753         }
       
   754 
       
   755         return result;
       
   756 
       
   757     }
       
   758 
       
   759     /**
       
   760      * Returns a string representation of this <code>TabularDataSupport</code> instance.
       
   761      * <p>
       
   762      * The string representation consists of the name of this class (ie <code>javax.management.openmbean.TabularDataSupport</code>),
       
   763      * the string representation of the tabular type of this instance, and the string representation of the contents
       
   764      * (ie list the key=value mappings as returned by a call to
       
   765      * <tt>dataMap.</tt>{@link java.util.HashMap#toString() toString()}).
       
   766      *
       
   767      * @return  a string representation of this <code>TabularDataSupport</code> instance
       
   768      */
       
   769     public String toString() {
       
   770 
       
   771         return new StringBuilder()
       
   772             .append(this.getClass().getName())
       
   773             .append("(tabularType=")
       
   774             .append(tabularType.toString())
       
   775             .append(",contents=")
       
   776             .append(dataMap.toString())
       
   777             .append(")")
       
   778             .toString();
       
   779     }
       
   780 
       
   781 
       
   782 
       
   783 
       
   784     /* *** TabularDataSupport internal utility methods *** */
       
   785 
       
   786 
       
   787     /**
       
   788      * Returns the index for value, assuming value is valid for this <tt>TabularData</tt> instance
       
   789      * (ie value is not null, and its composite type is equal to row type).
       
   790      *
       
   791      * The index is a List, and not an array, so that an index.equals(otherIndex) call will actually compare contents,
       
   792      * not just the objects references as is done for an array object.
       
   793      *
       
   794      * The returned List is unmodifiable so that once a row has been put into the dataMap, its index cannot be modified,
       
   795      * for example by a user that would attempt to modify an index contained in the Set returned by keySet().
       
   796      */
       
   797     private List<?> internalCalculateIndex(CompositeData value) {
       
   798 
       
   799         return Collections.unmodifiableList(Arrays.asList(value.getAll(this.indexNamesArray)));
       
   800     }
       
   801 
       
   802     /**
       
   803      * Checks if the specified key is valid for this <tt>TabularData</tt> instance.
       
   804      *
       
   805      * @throws  NullPointerException
       
   806      * @throws  InvalidOpenTypeException
       
   807      */
       
   808     private void checkKeyType(Object[] key) {
       
   809 
       
   810         // Check key is neither null nor empty
       
   811         //
       
   812         if ( (key == null) || (key.length == 0) ) {
       
   813             throw new NullPointerException("Argument key cannot be null or empty.");
       
   814         }
       
   815 
       
   816         /* Now check key is valid with tabularType index and row type definitions: */
       
   817 
       
   818         // key[] should have the size expected for an index
       
   819         //
       
   820         if (key.length != this.indexNamesArray.length) {
       
   821             throw new InvalidKeyException("Argument key's length="+ key.length +
       
   822                                           " is different from the number of item values, which is "+ indexNamesArray.length +
       
   823                                           ", specified for the indexing rows in this TabularData instance.");
       
   824         }
       
   825 
       
   826         // each element in key[] should be a value for its corresponding open type specified in rowType
       
   827         //
       
   828         OpenType<?> keyElementType;
       
   829         for (int i=0; i<key.length; i++) {
       
   830             keyElementType = tabularType.getRowType().getType(this.indexNamesArray[i]);
       
   831             if ( (key[i] != null) && (! keyElementType.isValue(key[i])) ) {
       
   832                 throw new InvalidKeyException("Argument element key["+ i +"] is not a value for the open type expected for "+
       
   833                                               "this element of the index, whose name is \""+ indexNamesArray[i] +
       
   834                                               "\" and whose open type is "+ keyElementType);
       
   835             }
       
   836         }
       
   837     }
       
   838 
       
   839     /**
       
   840      * Checks the specified value's type is valid for this <tt>TabularData</tt> instance
       
   841      * (ie value is not null, and its composite type is equal to row type).
       
   842      *
       
   843      * @throws  NullPointerException
       
   844      * @throws  InvalidOpenTypeException
       
   845      */
       
   846     private void checkValueType(CompositeData value) {
       
   847 
       
   848         // Check value is not null
       
   849         //
       
   850         if (value == null) {
       
   851             throw new NullPointerException("Argument value cannot be null.");
       
   852         }
       
   853 
       
   854         // if value's type is not the same as this instance's row type, throw InvalidOpenTypeException
       
   855         //
       
   856         if (!tabularType.getRowType().isValue(value)) {
       
   857             throw new InvalidOpenTypeException("Argument value's composite type ["+ value.getCompositeType() +
       
   858                                                "] is not assignable to "+
       
   859                                                "this TabularData instance's row type ["+ tabularType.getRowType() +"].");
       
   860         }
       
   861     }
       
   862 
       
   863     /**
       
   864      * Checks if the specified value can be put (ie added) in this <tt>TabularData</tt> instance
       
   865      * (ie value is not null, its composite type is equal to row type, and its index is not already used),
       
   866      * and returns the index calculated for this value.
       
   867      *
       
   868      * The index is a List, and not an array, so that an index.equals(otherIndex) call will actually compare contents,
       
   869      * not just the objects references as is done for an array object.
       
   870      *
       
   871      * @throws  NullPointerException
       
   872      * @throws  InvalidOpenTypeException
       
   873      * @throws  KeyAlreadyExistsException
       
   874      */
       
   875     private List<?> checkValueAndIndex(CompositeData value) {
       
   876 
       
   877         // Check value is valid
       
   878         //
       
   879         checkValueType(value);
       
   880 
       
   881         // Calculate value's index according to this instance's tabularType
       
   882         // and check it is not already used for a mapping in the parent HashMap
       
   883         //
       
   884         List<?> index = internalCalculateIndex(value);
       
   885 
       
   886         if (dataMap.containsKey(index)) {
       
   887             throw new KeyAlreadyExistsException("Argument value's index, calculated according to this TabularData "+
       
   888                                                 "instance's tabularType, already refers to a value in this table.");
       
   889         }
       
   890 
       
   891         // The check is OK, so return the index
       
   892         //
       
   893         return index;
       
   894     }
       
   895 
       
   896     /**
       
   897      * Deserializes a {@link TabularDataSupport} from an {@link ObjectInputStream}.
       
   898      */
       
   899     private void readObject(ObjectInputStream in)
       
   900             throws IOException, ClassNotFoundException {
       
   901       in.defaultReadObject();
       
   902       List<String> tmpNames = tabularType.getIndexNames();
       
   903       indexNamesArray = tmpNames.toArray(new String[tmpNames.size()]);
       
   904     }
       
   905 }