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.IOException;
010: import java.io.ObjectInputStream;
011: import java.io.Serializable;
012: import java.io.StreamCorruptedException;
013: import java.util.Collections;
014: import java.util.Iterator;
015: import java.util.Map;
016: import java.util.Set;
017: import java.util.TreeMap;
018:
019: /**
020: * @version $Revision: 1.11 $
021: */
022: public class CompositeType extends OpenType implements Serializable {
023: private static final long serialVersionUID = -5366242454346948798L;
024:
025: private TreeMap nameToDescription;
026: private TreeMap nameToType;
027:
028: private transient String m_classStringValue = null;
029: private transient int m_hashcode = 0;
030:
031: /**
032: * The CompositeType class is the open type class whose instances describe the types of CompositeData values
033: * The Java class name of composite data values this composite type represents (ie the class name returned by
034: * the getClassName method) is set to the string value returned by CompositeData.class.getName().
035: * <p><b>Parameters:<</p>
036: * typeName - The name given to the composite type this instance represents; cannot be a null or empty string
037: * <p/>
038: * description - The human readable description of the composite type this instance represents; cannot be a null or empty string
039: * <p/>
040: * itemNames - The n/b>ames of the items contained in the composite data values described by this composite type instance, cannot be null, and should contain at
041: * least one element, no element can be null, or an empty string.
042: * NOTE: the order in which the item names are given is not important to differentiate one CompositeType from another. The item names are stored internally sorted in
043: * ascending alphanumeric order
044: * <p/>
045: * itemDescriptions - the descriptions in the same order as the itemNames, same size as itemNames, with no item null or an empty String.
046: * <p/>
047: * itemTypes - The openType instances, in the same order as itemNames, describing the items contained in the compositeData values described by this instance.
048: * Should be the same size as itemNames and no element can be null.
049: * <p/>
050: * <p><b>Throws:</b></p>
051: * IllegalArgumentException - If typeName or description is a null or empty string, or itemNames or itemDescriptions or itemTypes is null, or any element of
052: * itemNames or itemDescriptions is a null or empty string, or any element of itemTypes is null, or itemNames or itemDescriptions or itemTypes are not of the same size.
053: * <p/>
054: * OpenDataException - If itemNames contains duplicate item names (case sensitive, but leading and trailing whitespaces removed).
055: */
056: public CompositeType(String typeName, String description,
057: String[] itemNames, String[] itemDescriptions,
058: OpenType[] itemTypes) throws OpenDataException {
059: super (CompositeData.class.getName(), typeName, description);
060: validate(typeName, description, itemNames, itemDescriptions,
061: itemTypes);
062: initialize(itemNames, itemDescriptions, itemTypes);
063: }
064:
065: /**
066: * Initialize treeMaps and store the data required: itemName (key) -> itemDescription (value) into nameDescription map, itemName (key) -> itemType (value) into nameType map
067: */
068: private void initialize(String[] itemNames,
069: String[] itemDescriptions, OpenType[] itemTypes)
070: throws OpenDataException {
071: m_hashcode = computeHashCode(getTypeName(), itemNames,
072: itemTypes);
073: nameToDescription = new TreeMap();
074: nameToType = new TreeMap();
075: for (int i = 0; i < itemNames.length; i++) {
076: String item = itemNames[i].trim();
077: if (nameToDescription.containsKey(item))
078: throw new OpenDataException("The key: " + item
079: + " is already mapped to a previous entry");
080: nameToDescription.put(item, itemDescriptions[i]);
081: nameToType.put(item, itemTypes[i]);
082: }
083: }
084:
085: /**
086: * validate strings for zero length and null and itemTypes for null, we know before we get here that all the arrays are of equal length tested in constructor
087: * so just get the respective objects at the index
088: */
089: private void validate(String typeName, String description,
090: String[] itemNames, String[] itemDescriptions,
091: OpenType[] itemTypes) throws IllegalArgumentException {
092: if (typeName == null || typeName.length() == 0)
093: throw new IllegalArgumentException(
094: "typeName can't be null or empty");
095: if (description == null || description.length() == 0)
096: throw new IllegalArgumentException(
097: "description can't be null or empty");
098: if (itemNames == null || itemNames.length == 0)
099: throw new IllegalArgumentException(
100: "The String[] of itemNames cannot be null or empty");
101: if (itemDescriptions == null || itemDescriptions.length == 0)
102: throw new IllegalArgumentException(
103: "The String[] of itemDescriptions cannot be null or empty");
104: if (itemTypes == null || itemTypes.length == 0)
105: throw new IllegalArgumentException(
106: "The OpenType[] of itemTypes cannot be null or empty");
107: if (itemDescriptions.length != itemNames.length
108: || itemTypes.length != itemNames.length)
109: throw new IllegalArgumentException(
110: "itemNames, itemDescriptions and itemTypes must all be the same length");
111: for (int i = 0; i < itemNames.length; i++) {
112: String value = itemNames[i];
113: String d = itemDescriptions[i];
114: if (value == null)
115: throw new IllegalArgumentException(
116: "The itemName at index: " + i
117: + " cannot be a null value");
118: if (d == null)
119: throw new IllegalArgumentException(
120: "The itemDescription at index: " + i
121: + " cannot be a null value");
122: if (value.trim().equals(""))
123: throw new IllegalArgumentException(
124: "The itemName at index: " + i
125: + " cannot be an empty string");
126: if (d.trim().equals(""))
127: throw new IllegalArgumentException(
128: "The itemDescription at index: " + i
129: + " cannot be an empty string");
130: if (itemTypes[i] == null)
131: throw new IllegalArgumentException(
132: "The OpenType at index: " + i
133: + " cannot be a null value");
134: }
135: }
136:
137: /**
138: * check if the key itemName is present
139: *
140: * @param itemName the name of the key to look for
141: * @return true if the key is present, false otherwise
142: */
143: public boolean containsKey(String itemName) {
144: if (itemName == null || itemName.length() == 0)
145: return false;
146: /** pick any treeMap as both Maps map the same values as keys */
147: return nameToDescription.containsKey(itemName);
148: }
149:
150: /**
151: * Retrieve the description value for the given key
152: *
153: * @param itemName the key
154: * @return the corresponding value
155: */
156: public String getDescription(String itemName) {
157: if (itemName == null || itemName.length() == 0)
158: return null;
159: return (String) nameToDescription.get(itemName);
160: }
161:
162: /**
163: * Retrieve the OpenType for the given key
164: *
165: * @param itemName the key for which to fetch the openType value
166: * @return OpenType or null if there is no value for the given key, or no matching key
167: */
168: public OpenType getType(String itemName) {
169: if (itemName == null || itemName.length() == 0)
170: return null;
171: return (OpenType) nameToType.get(itemName);
172: }
173:
174: /**
175: * Retrieve an unmodifiable set of keys
176: *
177: * @return a Set of the keys
178: */
179: public Set keySet() {
180: return Collections.unmodifiableSet(nameToDescription.keySet());
181: }
182:
183: /**
184: * Test whether object is a value which could be described by this CompositeType instance.
185: *
186: * @param object the Object to test if is a value which can be described by this CompositeType instance
187: * @return true if object is a value which can be described by this CompositeType instance, false otherwise.
188: */
189: public boolean isValue(Object object) {
190: if (!(object instanceof CompositeData))
191: return false;
192: CompositeData compositeData = (CompositeData) object;
193: return equals(compositeData.getCompositeType());
194: }
195:
196: /**
197: * tests object passed in is equal to the CompositeType instance
198: *
199: * @param object the Object to test if it is equal to this CompositeType instance
200: * @return true if the objects are equal as tested by taking the most significant fields and testing they are equal
201: */
202: public boolean equals(Object object) {
203: /** see if we can prevent a fairly expensive equals operation later where we compare the treeMaps */
204: if (object == this )
205: return true;
206:
207: /** the above test failed lets see if we have an instanceof at least */
208: if (!(object instanceof CompositeType))
209: return false;
210: CompositeType type = (CompositeType) object;
211: if (!(getTypeName().equals(type.getTypeName())))
212: return false;
213: return (nameToType.equals(type.nameToType));
214: }
215:
216: /**
217: * @return the calculated hashcode
218: */
219: public int hashCode() {
220: return m_hashcode;
221: }
222:
223: /**
224: * @return human readable representation of this class
225: */
226: public String toString() {
227: if (m_classStringValue == null) {
228: StringBuffer value = new StringBuffer(100);
229: value.append(getClassName()).append(" TypeName: ").append(
230: getTypeName()).append(" contains data:\n");
231: for (Iterator i = nameToType.entrySet().iterator(); i
232: .hasNext();) {
233: Map.Entry entry = (Map.Entry) i.next();
234: value.append("ItemName: ").append(
235: (String) entry.getKey()).append(
236: " OpenType value: ").append(
237: ((OpenType) entry.getValue()).toString())
238: .append("\n");
239: }
240: m_classStringValue = value.toString();
241: }
242: return m_classStringValue;
243: }
244:
245: /**
246: * obtain the values from the TreeMaps and validate
247: */
248: private void readObject(ObjectInputStream inputStream)
249: throws IOException, ClassNotFoundException {
250: inputStream.defaultReadObject();
251: String[] itemNames = (String[]) nameToDescription.keySet()
252: .toArray(new String[nameToDescription.size()]);
253: String[] itemDescriptions = (String[]) nameToDescription
254: .values().toArray(new String[nameToDescription.size()]);
255: OpenType[] itemTypes = (OpenType[]) nameToType.values()
256: .toArray(new OpenType[nameToType.size()]);
257: try {
258: validate(getTypeName(), getDescription(), itemNames,
259: itemDescriptions, itemTypes);
260: initialize(itemNames, itemDescriptions, itemTypes);
261: } catch (OpenDataException e) {
262: throw new StreamCorruptedException(
263: "validation failed for deserialized object, unable to create object in the correct state");
264: }
265: }
266:
267: private int computeHashCode(String name, String[] itemnames,
268: OpenType[] itemtypes) {
269: int result = name.hashCode();
270: for (int i = 0; i < itemnames.length; i++) {
271: result += itemnames[i].hashCode();
272: result += itemtypes[i].hashCode();
273: }
274: return result;
275: }
276:
277: }
|