Save This Page
Home » openjdk-7 » javax » management » openmbean » [javadoc | source]
    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   }

Save This Page
Home » openjdk-7 » javax » management » openmbean » [javadoc | source]