001: /**
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */package javax.management.openmbean;
008:
009: import java.io.Serializable;
010: import java.util.Arrays;
011: import java.util.Collection;
012: import java.util.Collections;
013: import java.util.Iterator;
014: import java.util.Map;
015: import java.util.Set;
016: import java.util.SortedMap;
017: import java.util.TreeMap;
018:
019: public class CompositeDataSupport implements CompositeData,
020: Serializable {
021: private static final long serialVersionUID = 8003518976613702244L;
022:
023: private SortedMap contents = new TreeMap();
024: private CompositeType compositeType;
025:
026: private transient int m_hashcode = 0;
027:
028: /**
029: * Constructs a CompositeDataSupport instance with the specified compositeType, whose item values are specified by itemValues[], in the same order as in itemNames[].
030: * As a CompositeType does not specify any order on its items, the itemNames[] parameter is used to specify the order in which the values are given in itemValues[].
031: * The items contained in this CompositeDataSupport instance are internally stored in a TreeMap, thus sorted in ascending lexicographic order of their names,
032: * for faster retrieval of individual item values.
033: * The constructor checks that all the constrainsts listed below for each parameter are satisfied, and throws the appropriate exception if they are not
034: *
035: * @param compositeType - the composite type of this composite data instance; must not be null.
036: * @param itemNames - itemNames must list, in any order, all the item names defined in compositeType;
037: * the order in which the names are listed, is used to match values in itemValues[]; must not be null or empty.
038: * @param itemValues - the values of the items, listed in the same order as their respective names in itemNames; each item value can be null,
039: * but if it is non-null it must be a valid value for the open type defined in compositeType for the corresponding item;
040: * must be of the same size as itemNames; must not be null or empty.
041: * @throws IllegalArgumentException if compositeType is null, or itemNames[] or itemValues[] is null or empty, or one of the elements in itemNames[] is a null or empty string,
042: * or itemNames[] and itemValues[] are not of the same size.
043: * @throws OpenDataException if itemNames[] or itemValues[]'s size differs from the number of items defined in compositeType, or one of the elements in itemNames[] does not exist
044: * as an item name defined in compositeType, or one of the elements in itemValues[] is not a valid value for the corresponding item as defined in compositeType
045: */
046: public CompositeDataSupport(CompositeType compositeType,
047: String[] itemNames, Object[] itemValues)
048: throws OpenDataException {
049: init(compositeType, itemNames, itemValues);
050: }
051:
052: /**
053: * Constructs a CompositeDataSupport instance with the specified compositeType, whose items names and corresponding values are given by the mappings in the map items.
054: * This constructor converts the keys to a string array and the values to an object array and calls
055: * CompositeDataSupport(javax.management.openmbean.CompositeType, java.lang.String[], java.lang.Object[]).
056: *
057: * @param compositeType - the composite type of this composite data instance; must not be null.
058: * @param items - the mappings of all the item names to their values; items must contain all the item names defined in compositeType; must not be null or empty
059: * @throws IllegalArgumentException - if compositeType is null, or items is null or empty, or one of the keys in items is a null or empty string, or one of the values in items is null
060: * @throws OpenDataException - if items' size differs from the number of items defined in compositeType, or one of the keys in items does not exist as an item name defined in compositeType,
061: * or one of the values in items is not a valid value for the corresponding item as defined in compositeType.
062: * @throws ArrayStoreException - if any of the keys in items cannot be cast to a String
063: */
064: public CompositeDataSupport(CompositeType compositeType, Map items)
065: throws OpenDataException {
066: init(compositeType, items != null ? (String[]) items.keySet()
067: .toArray(new String[items.size()]) : null,
068: items != null ? items.values().toArray() : null);
069: }
070:
071: /**
072: * do all the work of the constructor so radObject can validate all invariants by calling the method
073: */
074: private void init(CompositeType compositeType, String[] itemNames,
075: Object[] itemValues) throws OpenDataException {
076: if (compositeType == null)
077: throw new IllegalArgumentException(
078: "Null CompositeType is not an acceptable value");
079: if (itemNames == null || itemNames.length == 0)
080: throw new IllegalArgumentException(
081: "ItemNames cannot be null or empty (zero length)");
082: if (itemValues == null || itemValues.length == 0)
083: throw new IllegalArgumentException(
084: "ItemValues cannot be null or empty (zero length)");
085: if (itemNames.length != itemValues.length)
086: throw new IllegalArgumentException(
087: "Both the itemNames and itemValues arrays must be of equals length");
088:
089: // now validate the the contents itemNames must be of the same length and contain all items present in the compositeType keys
090: validateTypes(compositeType, itemNames);
091:
092: // check itemValues are valid values for openTypes
093: validateContents(compositeType, itemNames, itemValues);
094:
095: // valid now assign compositeType
096: this .compositeType = compositeType;
097:
098: // add the validated values and keys to the sortedMap
099: createMapData(itemNames, itemValues);
100: }
101:
102: private void validateContents(CompositeType compositeType,
103: String[] itemNames, Object[] itemValues)
104: throws OpenDataException {
105: for (int i = 0; i < itemValues.length; i++) {
106: if (itemValues[i] != null) {
107: OpenType openType = compositeType.getType(itemNames[i]);
108: if (!(openType.isValue(itemValues[i])))
109: throw new OpenDataException("itemValue at index "
110: + i + " is not a valid value for itemName "
111: + itemNames[i] + " and itemType "
112: + openType);
113: }
114: }
115: }
116:
117: /**
118: * validates that the itemNames are present (in full) in the keySet of the compositeType
119: */
120: private void validateTypes(CompositeType compositeType,
121: String[] itemNames) throws OpenDataException {
122: for (int i = 0; i < itemNames.length; i++) {
123: if (itemNames[i] == null || itemNames[i].trim().equals(""))
124: throw new IllegalArgumentException(
125: "Value of itemName at ["
126: + i
127: + "] is null or empty, unacceptable values");
128: }
129: Set keyTypes = compositeType.keySet();
130: if (itemNames.length != keyTypes.size())
131: throw new OpenDataException(
132: "The size of array arguments itemNames[] and itemValues[] should be equal to the number of items defined in argument compositeType");
133: if (!(Arrays.asList(itemNames).containsAll(keyTypes)))
134: throw new OpenDataException(
135: "itemNames[] does not contain all names defined in the compositeType of this instance.");
136: }
137:
138: /**
139: * fill the SortedMap with it's keys and values keys are the String[] itemNames and the values are the Object[] itemValues consisting of openTypes
140: */
141: private void createMapData(String[] itemNames, Object[] itemValues) {
142: for (int i = 0; i < itemNames.length; i++) {
143: contents.put(itemNames[i], itemValues[i]);
144: }
145: }
146:
147: /**
148: * @return the composite type of this composite data instance
149: */
150: public CompositeType getCompositeType() {
151: return compositeType;
152: }
153:
154: /**
155: * @param key - the key for which to return the value
156: * @return - the value of the item whose name is key
157: * @throws IllegalArgumentException if key is null or an empty String
158: * @throws InvalidKeyException if key is not an existing item name for this CompositeData instance
159: */
160: public Object get(String key) {
161: if (key == null || key.trim().equals(""))
162: throw new IllegalArgumentException("Null or empty key");
163: if (!(contents.containsKey(key.trim())))
164: throw new InvalidKeyException("Key with value " + key
165: + " is not a current stored key in this instance");
166: return contents.get(key.trim());
167: }
168:
169: /**
170: * Returns an array of the values of the items whose names are specified by keys, in the same order as keys this method simple calls get for each key
171: *
172: * @param the array of keys for which to return the corresponding values in the same order as specified in the keys
173: * @return the resulting array of values found
174: * @throws IllegalArgumentException if an element in keys is null or an empty String
175: * @throws InvalidKeyException if a key is not currently stored in this instances map
176: */
177: public Object[] getAll(String[] keys) {
178: /** should this throw an exception or under following conditions merely return an empty object[0] ?? */
179: if (keys == null || keys.length == 0)
180: return new Object[0];
181: Object[] dataMapValues = new Object[keys.length];
182: for (int i = 0; i < keys.length; i++) {
183: dataMapValues[i] = get(keys[i]);
184: }
185: return dataMapValues;
186: }
187:
188: /**
189: * Returns true if and only if this CompositeData instance contains an item whose name is key. If key is a null or empty String, this method simply returns false
190: *
191: * @param key the key for which to find a value
192: * @return true if value found false otherwise
193: */
194: public boolean containsKey(String key) {
195: if (key == null || key.trim().equals(""))
196: return false;
197: return contents.containsKey(key);
198: }
199:
200: /**
201: * Returns true if and only if this CompositeData instance contains an item whose value is value
202: *
203: * @param value - the value to determine if present
204: * @return true if found false otherwise
205: */
206: public boolean containsValue(Object value) {
207: return contents.containsValue(value);
208: }
209:
210: /**
211: * Returns an unmodifiable Collection view of the item values contained in this CompositeData instance. The returned collection's iterator will return the values in the
212: * ascending lexicographic order of the corresponding item names.
213: *
214: * @return unmodifiable collection of current values
215: */
216: public Collection values() {
217: return Collections.unmodifiableCollection(contents.values());
218: }
219:
220: /**
221: * tests that the Object obj is equal to this compositeData instance
222: *
223: * @param obj - the Object to test if is equals
224: * @return true if and only if
225: * <ul>
226: * <li>obj is an instanceof CompositeData as tested by instanceof CompositeData</li>
227: * <li>all of the name/value pairs in the objects map are equal to the name/value pairs of this instance</li>
228: * <li>if the compositeTypes are equal</li>
229: * </ul>
230: */
231: public boolean equals(Object obj) {
232: if (!(obj instanceof CompositeData))
233: return false;
234: CompositeData compositeData = (CompositeData) obj;
235: boolean result = getCompositeType().equals(
236: compositeData.getCompositeType());
237: if (result) {
238: Iterator i = contents.entrySet().iterator();
239: while (i.hasNext() && result) {
240: Map.Entry entry = (Map.Entry) i.next();
241: String key = (String) entry.getKey();
242: Object entryvalue = entry.getValue();
243: Object cdvalue = compositeData.get(key);
244: if (entryvalue == null) {
245: result = (cdvalue == null);
246: } else {
247: result = entryvalue.equals(cdvalue);
248: }
249: }
250: }
251: return result;
252: }
253:
254: /**
255: * Using the same information as in equals test to create the hashcode
256: * i.e) The hash code of a CompositeDataSupport instance is the sum of the hash codes of all elements of information used in equals comparisons (ie: its composite type and all the item values).
257: *
258: * @return the calculated HashCode for this Object
259: */
260: public int hashCode() {
261: if (m_hashcode == 0) {
262: int result = getCompositeType().hashCode();
263: for (Iterator i = contents.entrySet().iterator(); i
264: .hasNext();) {
265: Map.Entry entry = (Map.Entry) i.next();
266: if (entry.getValue() != null)
267: result += entry.getValue().hashCode();
268: }
269: m_hashcode = result;
270: }
271: return m_hashcode;
272: }
273:
274: /**
275: * The string representation consists of the name of this class (ie javax.management.openmbean.CompositeDataSupport),
276: * the string representation of the composite type of this instance, and the string representation of the contents (ie list the itemName=itemValue mappings).
277: *
278: * @return a string representation of this CompositeDataSupport instance.
279: */
280: public String toString() {
281: StringBuffer buffer = new StringBuffer(getClass().getName());
282: buffer.append("\tCompositeType = ");
283: buffer.append(compositeType.toString());
284: buffer.append("\tcontents are: ");
285: buffer.append(contents.toString());
286: return buffer.toString();
287: }
288: }
|