javax.management.openmbean.TabularDataSupport Maven / Gradle / Ivy
The newest version!
/**
* Copyright (C) The MX4J Contributors.
* All rights reserved.
*
* This software is distributed under the terms of the MX4J License version 1.0.
* See the terms of the MX4J License in the documentation provided with this software.
*/
package javax.management.openmbean;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @version $Revision: 1.10 $
*/
/**
* The TabularDataSupport class is the open data class which implements the TabularData and the
* Map interfaces, and which is internally based on a hash map data structure
*/
public class TabularDataSupport implements TabularData, Map, Cloneable, Serializable
{
private static final long serialVersionUID = 5720150593236309827L;
private Map dataMap;
private TabularType tabularType;
private transient String[] m_indexNames;
/**
* Creates an empty TabularDataSupport instance whose open-type is tabularType, and whose underlying HashMap has a default initial capacity (101) and default load factor (0.75).
*
* @param tabularType the TabularTypeBackUP describing this TabularData instance, cannot be null
*/
public TabularDataSupport(TabularType tabularType)
{
this(tabularType, 101, 0.75f);
}
/**
* Creates an empty TabularDataSupport instance whose open-type is tabularType, and whose underlying HashMap has the specified initial capacity and load factor
*
* @param tabularType - the tabular type describing this instance, cannot be null;
* @param initialCapacity - the initial capacity of the Map
* @param loadFactor - the load factor of the Map
* @throws IllegalArgumentException if the initialCapacity is less than zero, the load factor is negative or the tabularType is null
*/
public TabularDataSupport(TabularType tabularType, int initialCapacity, float loadFactor)
{
if (tabularType == null) throw new IllegalArgumentException("TabularTypeBackUP instance cannot be null.");
if (initialCapacity < 0) throw new IllegalArgumentException("The initialCapacity cannot be a negative number.");
if (loadFactor < 0) throw new IllegalArgumentException("The load factor cannot be a negative number.");
this.tabularType = tabularType;
this.dataMap = new HashMap(initialCapacity, loadFactor);
initialize();
}
/**
* can be called by the readObject method to initialize the deserialized instance
*/
private void initialize()
{
// get the index names list from the tabular type as these will act as the unique index into each row of the TabularData instance
List tabularIndexList = tabularType.getIndexNames();
m_indexNames = (String[])tabularIndexList.toArray(new String[tabularIndexList.size()]);
}
/**
* get the TabularType that this instance contains
*
* @return TabularType describing this TabularData instance
*/
public TabularType getTabularType()
{
return tabularType;
}
/**
* Calculates the index that would be used in this TabularData instance to refer to the specified composite data value parameter is it were added
* to this instance.This method checks for the type validity of the specified value, but does not check if the calculated index is already used to refer to a
* value, in this tabular data instance
*
* @param value - the CompositeData value whose index in this TabularData instance is to be calculated, must be of the same composite type as this instance's row type;
* must not be null.
* @return - the index that the specified value would have in this tabularData instance
* @throws NullPointerException if value is null
* @throws InvalidOpenTypeException if value does not conform to this tabular data instances' row type definition
*/
public Object[] calculateIndex(CompositeData value)
{
if (value == null) throw new NullPointerException("CompositeData object cannot be null");
if (!(value.getCompositeType().equals(tabularType.getRowType()))) throw new InvalidOpenTypeException("Invalid CompositeData object, its' tabularType is not equal to the row type of this TabularType instance");
return Collections.unmodifiableList(Arrays.asList(value.getAll(m_indexNames))).toArray();
}
/**
* returns true if and only if this tabularData instance contains a compositeData value (i.e. a row) whose index is the specified key. If key cannot be cast to a one dimension
* array of Object instances, this method returns false, otherwise it returns the result of the call this.containsKey((Object[]) key)
*
* @param key - the index value
* @return true if a CompositeData value is found false otherwise, false if key cannot be cast to an Object[]
*/
public boolean containsKey(Object key)
{
if (!(key instanceof Object[])) return false;
return containsKey((Object[])key);
}
/**
* Returns true if and only if this TabularData instance contains a CompositeData value (ie a row) whose index is the specified key. If key is null or does not conform to this
* TabularData instance's TabularType definition, this method simply returns false.
*
* @param key the index value whose presence in this TabularData instance is to be tested
* @return true if it is found false otherwise
*/
public boolean containsKey(Object[] key)
{
if (key == null) return false;
return dataMap.containsKey(Arrays.asList(key));
}
/**
* Returns true if and only if this TabularData instance contains the specified CompositeData value. If value is null or does not conform to
* this TabularData instance's row type definition, this method simply returns false.
*
* @param value - the row value whose presence in this TabularData instance is to be tested
* @return true if this instance contains the specified value
*/
public boolean containsValue(CompositeData value)
{
return dataMap.containsValue(value);
}
/**
* Returns true if and only if this TabularData instance contains the specified value.
*
* @param value - the row value whose presence in this TabularData instance is to be tested
* @return true if this TabularData instance contains the specified row value
*/
public boolean containsValue(Object value)
{
return dataMap.containsValue(value);
}
/**
* This method simply calls get((Object[]) key).
*
* @param key - the key for which to lookup the value
* @return the Object found for key "key"
*/
public Object get(Object key)
{
return get((Object[])key);
}
/**
* This method validates the key Object[] parameter. It cannot be null, or empty. It must be the same length as the indexNames of the tabularType passed into this instances
* constructor, and must be a valid value i.e the value returned by the method call OpenType isValue(Object obj) must be true
*
* @param key - the Object[] key stored as a key in this instance
* @return - the CompositeData value corresponding to the key
* @throws NullPointerException if key is null or empty
* @throws InvalidKeyException if an item in the array returns false for call OpenType isValue(Object o)
*/
public CompositeData get(Object[] key)
{
validateKeys(key);
return (CompositeData)dataMap.get(Arrays.asList(key));
}
/**
* This method simply calls put((CompositeData) value) and therefore ignores its key parameter which can be null
*
* @return the value that is put
* @throws ClassCastException if value is not an instanceof CompositeData
*/
public Object put(Object key, Object value)
{
put((CompositeData)value);
return value;
}
/**
* Adds "value" to this TabularData instance, if value's composite type is the same as this instance's row type
* (ie the composite type returned by this.getTabularType().getRowType()), and if there is not already an existing value in this TabularData
* instance whose index is the same as the one calculated for the value to be added. The index for value is calculated according to this TabularData instance's
* TabularType definition @see javax.management.openmbean.TabularType#getIndexNames()
*
* This method calls calculateIndex(CompositeData value) which validates value the returned Object[] is then converted into an unmodifiableList and stored
*
* @param value - the composite data value to be added as a new row to this TabularData instance; must be of the same composite type as this instance's row type; must not be null.
* @throws NullPointerException if value is null
* @throws InvalidOpenTypeException if value does not conform to this TabularData instance's row type definition
* @throws KeyAlreadyExistsException if the underlying HashMap already contains the calculated key
*/
public void put(CompositeData value)
{
// calculateIndex method tests for null and invalid rowType
List list = Collections.unmodifiableList(Arrays.asList(calculateIndex(value)));
if (dataMap.containsKey(list)) throw new KeyAlreadyExistsException("The list of index names already exists in this instance");
dataMap.put(list, value);
}
/**
* This method simply calls remove((Object[]) key).
*
* @param key - the Object to remove note no checks are done if key can be cast to Object[] hence if not a ClassCastException will be thrown
* @throws ClassCastException if key cannot be cast to an Object[]
*/
public Object remove(Object key)
{
return remove((Object[])key);
}
/**
* Method validates the key checking for null, zero length and if items in the key are invalid as returned by @see OpenType isValue(Object obj)
*
* @param key - the index of the value to get in this TabularData instance; must be valid with this TabularData instance's row type definition; must not be null.
* @return previous value associated with specified key, or null if there was no mapping for key
* @throws NullPointerException if key is null
* @throws InvalidKeyException if the key does not conform to this TabularData instance's TabularType definition
*/
public CompositeData remove(Object[] key)
{
validateKeys(key);
return (CompositeData)dataMap.remove(Arrays.asList(key));
}
/**
* Add all the values contained in the specified map t to this TabularData instance. This method converts the collection of values contained in this map into an
* array of CompositeData values, if possible, and then calls the method putAll(CompositeData[]). Note that the keys used in the specified map t are ignored.
* This method allows, for example to add the content of another TabularData instance with the same row type (but possibly different index names) into this instance.
*
* @param t - the map whose values are to be added as new rows to this TabularData instance; if t is null or empty, this method returns without doing anything
* @throws NullPointerException - if a "value" in t is null
* @throws ClassCastException - if a value in t is not an instanceof CompositeData this is a wrapper around the RuntimeException ArrayStoreException
* which is generated when an attempt has been made to store the wrong type of object into an array of objects.
* @throws InvalidOpenTypeException - if a value in t does not conform to this TabularData instance's row type definition
* @throws KeyAlreadyExistsException - if the index for a value in t, calculated according to this TabularData instance's TabularType definition
* already maps to an existing value in this instance, or two values in t have the same index.
*/
public void putAll(Map t)
{
if (t == null || t.size() == 0) return;
CompositeData[] compositeData;
try
{
compositeData = (CompositeData[])t.values().toArray(new CompositeData[t.size()]);
}
catch (ArrayStoreException e)
{
throw new ClassCastException("The values contained in t must all be of type CompositeData");
}
putAll(compositeData);
}
/**
* Adds all the elements in values to this TabularData instance. If any element in values does not satisfy the constraints defined in put, or if any two elements in values
* have the same index calculated according to this TabularData instance's TabularType definition, then an exception describing the failure is thrown
* and no element of values is added, thus leaving this TabularData instance unchanged.
*
* @param values - the array of composite data values to be added as new rows to this TabularData instance; if values is null or empty, this method returns without doing anything
* @throws NullPointerException - if an element of values is null
* @throws InvalidOpenTypeException - if an element of values does not conform to this TabularData instance's row type definition (ie its TabularType definition)
* @throws KeyAlreadyExistsException - if the index for an element of values, calculated according to this TabularData instance's TabularType definition already
* maps to an existing value in this instance, or two elements of values have the same index
*/
public void putAll(CompositeData[] values)
{
// validate values
if (values == null || values.length == 0) return;
/** creating a list to store all the keys obtained from method calculateIndex this is to check that no keys are duplicated */
List storeList = validateNoDuplicates(values);
/** Once we have validated that there are no duplicated keys we can add them to the global map if a duplicate is found an exception is thrown leaving instance unchanged */
for (int i = 0; i < values.length; i++)
{
dataMap.put(storeList.get(i), values[i]);
}
}
/**
* test that the list returned from calculateIndex for each CompositeData object has no duplicates
*/
private List validateNoDuplicates(CompositeData[] values)
{
List storeList = new ArrayList();
for (int i = 0; i < values.length; i++)
{
List list = Collections.unmodifiableList(Arrays.asList(calculateIndex(values[i])));
/** check no duplicate keys if there is we have an invalid key */
if (storeList.contains(list)) throw new KeyAlreadyExistsException("value at [" + i + "] has the same index values as: " + storeList.indexOf(list));
/** store it to check the next values are not duplicated */
storeList.add(list);
}
return storeList;
}
/**
* clears this instances internal map
*/
public void clear()
{
dataMap.clear();
}
/**
* @return the size of the map
*/
public int size()
{
return dataMap.size();
}
/**
* @return true if the map is empty false if not empty
*/
public boolean isEmpty()
{
return dataMap.isEmpty();
}
/**
* Returns a set view of the keys contained in the underlying map of this TabularDataSupport instance, and used to index the rows.
* Each key contained in this set is an unmodifiable List. The set is backed by the underlying map of this TabularDataSupport instance,
* so changes to the TabularDataSupport instance are reflected in the set, and vice-versa. The set supports element removal, which removes the
* corresponding row from this TabularDataSupport instance, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
* It does not support the add or addAll operations
*
* @return a set view of the keys used to index the rows of this TabularDataSupport instance
*/
public Set keySet()
{
return dataMap.keySet();
}
/**
* Returns a collection view of the rows contained in this TabularDataSupport instance. The collection is backed by the underlying map,
* so changes to the TabularDataSupport instance are reflected in the collection, and vice-versa. The collection supports element removal,
* which removes the corresponding index to row mapping from this TabularDataSupport instance, via the Iterator.remove, Collection.remove, removeAll,
* retainAll, and clear operations. It does not support the add or addAll operations
*
* @return a collection view of the values contained in this TabularDataSupport instance.
*/
public Collection values()
{
return dataMap.values();
}
/**
* Returns a collection view of the index to row mappings contained in this TabularDataSupport instance. Each element in the returned collection is a Map.Entry.
* The collection is backed by the underlying map of this TabularDataSupport instance, in so changes to the TabularDataSupport instance are reflected the collection, and vice-versa.
* The collection supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Collection.remove, removeAll, retainAll, and clear operations.
* It does not support the add or addAll operations.
* NOTE Do not use the SetValue method of Map.Entry elements contained in the returned collection view.
* Doing so would corrupt the index to row mappings contained in this TabularDataSupport instance
*
* @return a collection view of the mappings contained in this map
*/
public Set entrySet()
{
return dataMap.entrySet();
}
/**
* Returns a clone of this TabularDataSupport instance: the clone is obtained by calling super.clone(),
* and then cloning the underlying map. 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!!
*
* @return a copy of the TabularDataSupport
*/
public Object clone()
{
TabularDataSupport dataSupportClone = null;
try
{
dataSupportClone = (TabularDataSupport)super.clone();
dataSupportClone.dataMap = (HashMap)((HashMap)dataMap).clone();
}
catch (CloneNotSupportedException e)
{
// shouldn't happen
return null;
}
return dataSupportClone;
}
/**
* Compares the specified obj parameter with this TabularDataSupport instance for equality
*
* @return true if and only if all of the following statements are true:
*
*
* - their tabular types are equal
*
*
*/
public boolean equals(Object obj)
{
if (!(obj instanceof TabularData)) return false;
TabularData tabularData = (TabularData)obj;
for (Iterator i = values().iterator(); i.hasNext();)
{
CompositeData compositeData = (CompositeData)i.next();
if (!(tabularData.containsValue(compositeData))) return false;
}
return ((getTabularType().equals(tabularData.getTabularType()) && (size() == tabularData.size())));
}
/**
* The hash code of a TabularDataSupport instance is the sum of the hash codes of all elements of information used in equals comparisons
* (ie: its tabular type and its content, where the content is defined as all the CompositeData values).
*
* @return the calculated hashCode for this object
*/
public int hashCode()
{
int result = tabularType.hashCode();
for (Iterator i = values().iterator(); i.hasNext();)
{
result += i.next().hashCode();
}
return result;
}
/**
* The string representation consists of the name of this class (ie com.sun.jdmk.TabularDataSupport), the string representation of the tabular type of this instance,
* and the string representation of the contents (ie list the key=value mappings as returned by a call to dataMap.toString())
*
* @return a string representation of this TabularDataSupport instance
*/
public String toString()
{
return getClass().getName() + "(tabularType = " + tabularType.toString() + ",contains = " + dataMap.toString() + ")";
}
/**
* validate the Object[] of keys they cannot be null or empty. The length must be the same length as the indexNames used for the indexing rows
*/
private void validateKeys(Object[] key)
{
if (key == null || key.length == 0) throw new NullPointerException("Object[] key cannot be null or of zero length");
if (key.length != m_indexNames.length) throw new InvalidKeyException("Length of Object[] passed in as a parameter is not equal to the number of items: " + m_indexNames.length + " as specified for the indexing rows in this instance.");
for (int i = 0; i < key.length; i++)
{
OpenType openType = tabularType.getRowType().getType(m_indexNames[i]);
if ((key[i] != null) && (!(openType.isValue(key[i])))) throw new InvalidKeyException("expected value is: " + openType + " at index: " + i + " but got: " + key[i]);
}
}
/**
* acts as a second constructor so validate all invariants and recreate the object in the correct state
*/
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException
{
inputStream.defaultReadObject();
initialize();
}
}