001: /* JFox, the OpenSource J2EE Application Server
002: *
003: * Copyright (C) 2002 huihoo.org
004: * Distributable under GNU LGPL license
005: * See the GNU Lesser General Public License for more details.
006: */
007:
008: package javax.management.openmbean;
009:
010: import java.util.Iterator;
011: import java.util.Set;
012: import java.util.Collections;
013: import java.util.TreeMap;
014: import java.io.Serializable;
015: import javax.management.openmbean.CompositeData;
016:
017: /**
018: * The <code>CompositeType</code> class is the <i>open type</i> class
019: * whose instances describe the types of {@link CompositeData <code>CompositeData</code>} values.
020: *
021: * @author <a href="mailto:young_yy@hotmail.org">Young Yang</a>
022: */
023:
024: public class CompositeType extends OpenType implements Serializable {
025:
026: /**
027: * @serial Sorted mapping of the item names to their descriptions
028: */
029: private TreeMap nameToDescription = new TreeMap();
030:
031: /**
032: * @serial Sorted mapping of the item names to their open types
033: */
034: private TreeMap nameToType = new TreeMap();
035:
036: private transient Integer myHashCode = null; // As this instance is immutable,
037: private transient String myToString = null; // these three values
038: private transient Set myNamesSet = null; // need only be calculated once.
039:
040: /* *** Constructor *** */
041:
042: /**
043: * Constructs a <code>CompositeType</code> instance, checking for the validity of the given parameters.
044: * The validity constraints are described below for each parameter.
045: * <p>
046: * Note that the contents of the three array parameters
047: * <var>itemNames</var>, <var>itemDescriptions</var> and <var>itemTypes</var>
048: * are internally copied so that any subsequent modification of these arrays by the caller of this constructor
049: * has no impact on the constructed <code>CompositeType</code> instance.
050: * <p>
051: * The Java class name of composite data values this composite type represents
052: * (ie the class name returned by the {@link OpenType#getClassName() getClassName} method)
053: * is set to the string value returned by <code>CompositeData.class.getNameSpace()</code>.
054: * <p>
055: * @param typeName The name given to the composite type this instance represents; cannot be a null or empty string.
056: * <br>
057: * @param description The human readable description of the composite type this instance represents;
058: * cannot be a null or empty string.
059: * <br>
060: * @param itemNames The names of the items contained in the
061: * composite data values described by this <code>CompositeType</code> instance;
062: * cannot be null and should contain at least one element; no element can be a null or empty string.
063: * Note that the order in which the item names are given is not important to differentiate a
064: * <code>CompositeType</code> instance from another;
065: * the item names are internally stored sorted in ascending alphanumeric order.
066: * <br>
067: * @param itemDescriptions The descriptions, in the same order as <var>itemNames</var>, of the items contained in the
068: * composite data values described by this <code>CompositeType</code> instance;
069: * should be of the same size as <var>itemNames</var>;
070: * no element can be a null or empty string.
071: * <br>
072: * @param itemTypes The open type instances, in the same order as <var>itemNames</var>, describing the items contained
073: * in the composite data values described by this <code>CompositeType</code> instance;
074: * should be of the same size as <var>itemNames</var>;
075: * no element can be null.
076: * <br>
077: * @throws IllegalArgumentException If <var>typeName</var> or <var>description</var> is a null or empty string,
078: * or <var>itemNames</var> or <var>itemDescriptions</var> or <var>itemTypes</var> is null,
079: * or any element of <var>itemNames</var> or <var>itemDescriptions</var>
080: * is a null or empty string,
081: * or any element of <var>itemTypes</var> is null,
082: * or <var>itemNames</var> or <var>itemDescriptions</var> or <var>itemTypes</var>
083: * are not of the same size.
084: * <br>
085: * @throws OpenDataException If <var>itemNames</var> contains duplicate item names
086: * (case sensitive, but leading and trailing whitespaces removed).
087: */
088: public CompositeType(String typeName, String description,
089: String[] itemNames, String[] itemDescriptions,
090: OpenType[] itemTypes) throws OpenDataException {
091:
092: // Check and construct state defined by parent
093: //
094: super (CompositeData.class.getName(), typeName, description);
095:
096: // Check the 3 arrays are not null or empty (ie length==0) and that there is no null element or empty string in them
097: //
098: checkForNullElement(itemNames, "itemNames");
099: checkForNullElement(itemDescriptions, "itemDescriptions");
100: checkForNullElement(itemTypes, "itemTypes");
101: checkForEmptyString(itemNames, "itemNames");
102: checkForEmptyString(itemDescriptions, "itemDescriptions");
103:
104: // Check the sizes of the 3 arrays are the same
105: //
106: if ((itemNames.length != itemDescriptions.length)
107: || (itemNames.length != itemTypes.length)) {
108: throw new IllegalArgumentException(
109: "Array arguments itemNames[], itemDescriptions[] and itemTypes[] "
110: + "should be of same length (got "
111: + itemNames.length + ", "
112: + itemDescriptions.length + " and "
113: + itemTypes.length + ").");
114: }
115:
116: // Initialize internal "names to descriptions" and "names to types" sorted maps,
117: // and, by doing so, check there are no duplicate item names
118: //
119: // nameToDescription = new TreeMap();
120: // nameToType = new TreeMap();
121: String key;
122: for (int i = 0; i < itemNames.length; i++) {
123: key = itemNames[i].trim();
124: if (nameToDescription.containsKey(key)) {
125: throw new OpenDataException(
126: "Argument's element itemNames["
127: + i
128: + "]=\""
129: + itemNames[i]
130: + "\" duplicates a previous item names.");
131: }
132: nameToDescription.put(key, itemDescriptions[i].trim());
133: nameToType.put(key, itemTypes[i]);
134: }
135: }
136:
137: private static void checkForNullElement(Object[] arg, String argName) {
138: if ((arg == null) || (arg.length == 0)) {
139: throw new IllegalArgumentException("Argument " + argName
140: + "[] cannot be null or empty.");
141: }
142: for (int i = 0; i < arg.length; i++) {
143: if (arg[i] == null) {
144: throw new IllegalArgumentException(
145: "Argument's element " + argName + "[" + i
146: + "] cannot be null.");
147: }
148: }
149: }
150:
151: private static void checkForEmptyString(String[] arg, String argName) {
152: for (int i = 0; i < arg.length; i++) {
153: if (arg[i].trim().equals("")) {
154: throw new IllegalArgumentException(
155: "Argument's element " + argName + "[" + i
156: + "] cannot be an empty string.");
157: }
158: }
159: }
160:
161: /* *** Composite type specific information methods *** */
162:
163: /**
164: * Returns <code>true</code> if this <code>CompositeType</code> instance defines an item
165: * whose name is <var>itemName</var>.
166: *
167: * @param itemName the name of the item.
168: *
169: * @return true if an item of this name is present.
170: */
171: public boolean containsKey(String itemName) {
172:
173: if (itemName == null) {
174: return false;
175: }
176: return nameToDescription.containsKey(itemName);
177: }
178:
179: /**
180: * Returns the description of the item whose name is <var>itemName</var>,
181: * or <code>null</code> if this <code>CompositeType</code> instance does not define any item
182: * whose name is <var>itemName</var>.
183: *
184: * @param itemName the name of the item.
185: *
186: * @return the description.
187: */
188: public String getDescription(String itemName) {
189:
190: if (itemName == null) {
191: return null;
192: }
193: return (String) nameToDescription.get(itemName);
194: }
195:
196: /**
197: * Returns the <i>open type</i> of the item whose name is <var>itemName</var>,
198: * or <code>null</code> if this <code>CompositeType</code> instance does not define any item
199: * whose name is <var>itemName</var>.
200: *
201: * @param itemName the name of the time.
202: *
203: * @return the type.
204: */
205: public OpenType getType(String itemName) {
206:
207: if (itemName == null) {
208: return null;
209: }
210: return (OpenType) nameToType.get(itemName);
211: }
212:
213: /**
214: * Returns an unmodifiable Set view of all the item names defined by this <code>CompositeType</code> instance.
215: * The set's iterator will return the item names in ascending order.
216: *
217: * @return a {@link Set} of {@link String}.
218: */
219: public Set keySet() {
220:
221: // Initializes myNamesSet on first call
222: if (myNamesSet == null) {
223: myNamesSet = Collections.unmodifiableSet(nameToDescription
224: .keySet());
225: }
226:
227: return myNamesSet; // always return the same value
228: }
229:
230: /**
231: * Tests whether <var>obj</var> is a value which could be described by this <code>CompositeType</code> instance.
232: * <p>
233: * If <var>obj</var> is null or is not an instance of <code>javax.management.openmbean.CompositeData</code>,
234: * <code>isValue</code> returns <code>false</code>.
235: * If <var>obj</var> is an instance of <code>javax.management.openmbean.CompositeData</code>,
236: * its composite type is tested for equality with this <code>CompositeType</code> instance, and <code>isValue</code>
237: * returns <code>true</code> if and only if {@link #equals(java.lang.Object) <code>equals</code>}
238: * returns <code>true</code>.
239: * <br>
240: * @param obj the value whose open type is to be tested for equality with this <code>CompositeType</code> instance.
241: *
242: * @return <code>true</code> if <var>obj</var> is a value for this composite type, <code>false</code> otherwise.
243: */
244: public boolean isValue(Object obj) {
245:
246: // if obj is null, return false
247: //
248: if (obj == null) {
249: return false;
250: }
251:
252: // if obj is not a CompositeData, return false
253: //
254: CompositeData value;
255: try {
256: value = (CompositeData) obj;
257: } catch (ClassCastException e) {
258: return false;
259: }
260:
261: // test value's CompositeType for equality with this CompositeType instance
262: //
263: return this .equals(value.getCompositeType());
264: }
265:
266: /* *** Methods overriden from class Object *** */
267:
268: /**
269: * Compares the specified <code>obj</code> parameter with this <code>CompositeType</code> instance for equality.
270: * <p>
271: * Two <code>CompositeType</code> instances are equal if and only if all of the following statements are true:
272: * <ul>
273: * <li>their type names are equal</li>
274: * <li>their items' names and types are equal</li>
275: * </ul>
276: * <br>
277: * @param obj the object to be compared for equality with this <code>CompositeType</code> instance;
278: * if <var>obj</var> is <code>null</code>, <code>equals</code> returns <code>false</code>.
279: *
280: * @return <code>true</code> if the specified object is equal to this <code>CompositeType</code> instance.
281: */
282: public boolean equals(Object obj) {
283:
284: // if obj is null, return false
285: //
286: if (obj == null) {
287: return false;
288: }
289:
290: // if obj is not a CompositeType, return false
291: //
292: CompositeType other;
293: try {
294: other = (CompositeType) obj;
295: } catch (ClassCastException e) {
296: return false;
297: }
298:
299: // Now, really test for equality between this CompositeType instance and the other
300: //
301:
302: // their names should be equal
303: if (!this .getTypeName().equals(other.getTypeName())) {
304: return false;
305: }
306:
307: // their items names and types should be equal
308: if (!this .nameToType.equals(other.nameToType)) {
309: return false;
310: }
311:
312: // All tests for equality were successfull
313: //
314: return true;
315: }
316:
317: /**
318: * Returns the hash code value for this <code>CompositeType</code> instance.
319: * <p>
320: * The hash code of a <code>CompositeType</code> instance is the sum of the hash codes
321: * of all elements of information used in <code>equals</code> comparisons
322: * (ie: name, items names, items types).
323: * This ensures that <code> t1.equals(t2) </code> implies that <code> t1.hashCode()==t2.hashCode() </code>
324: * for any two <code>CompositeType</code> instances <code>t1</code> and <code>t2</code>,
325: * as required by the general contract of the method
326: * {@link <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/Object.html#hashCode()">
327: * <code>Object.hashCode</code> </a>}.
328: * <p>
329: * As <code>CompositeType</code> instances are immutable, the hash code for this instance is calculated once,
330: * on the first call to <code>hashCode</code>, and then the same value is returned for subsequent calls.
331: *
332: * @return the hash code value for this <code>CompositeType</code> instance
333: */
334: public int hashCode() {
335:
336: // Calculate the hash code value if it has not yet been done (ie 1st call to hashCode())
337: //
338: if (myHashCode == null) {
339: int value = 0;
340: value += this .getTypeName().hashCode();
341: String key;
342: for (Iterator k = nameToDescription.keySet().iterator(); k
343: .hasNext();) {
344: key = (String) k.next();
345: value += key.hashCode();
346: value += this .nameToType.get(key).hashCode();
347: }
348: myHashCode = new Integer(value);
349: }
350:
351: // return always the same hash code for this instance (immutable)
352: //
353: return myHashCode.intValue();
354: }
355:
356: /**
357: * Returns a string representation of this <code>CompositeType</code> instance.
358: * <p>
359: * The string representation consists of
360: * the name of this class (ie <code>javax.management.openmbean.CompositeType</code>), the type name for this instance,
361: * and the list of the items names and types string representation of this instance.
362: * <p>
363: * As <code>CompositeType</code> instances are immutable, the string representation for this instance is calculated once,
364: * on the first call to <code>toString</code>, and then the same value is returned for subsequent calls.
365: *
366: * @return a string representation of this <code>CompositeType</code> instance
367: */
368: public String toString() {
369:
370: // Calculate the string representation if it has not yet been done (ie 1st call to toString())
371: //
372: if (myToString == null) {
373: StringBuffer result = new StringBuffer();
374: result.append(this .getClass().getName());
375: result.append("(name=");
376: result.append(getTypeName());
377: result.append(",items=(");
378: int i = 0;
379: Iterator k = nameToType.keySet().iterator();
380: String key;
381: while (k.hasNext()) {
382: key = (String) k.next();
383: if (i > 0)
384: result.append(",");
385: result.append("(itemName=");
386: result.append(key);
387: result.append(",itemType=");
388: result.append(nameToType.get(key).toString() + ")");
389: i++;
390: }
391: result.append("))");
392: myToString = result.toString();
393: }
394:
395: // return always the same string representation for this instance (immutable)
396: //
397: return myToString;
398: }
399: }
|