KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > management > ImmutableDescriptor


1 /*
2  * @(#)ImmutableDescriptor.java 1.18 06/03/24
3  *
4  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.management;
9
10 import java.io.InvalidObjectException JavaDoc;
11 import java.lang.reflect.Array JavaDoc;
12 import java.util.Arrays JavaDoc;
13 import java.util.Comparator JavaDoc;
14 import java.util.Map JavaDoc;
15 import java.util.SortedMap JavaDoc;
16 import java.util.TreeMap JavaDoc;
17
18 /**
19  * An immutable descriptor.
20  * @since 1.6
21  */

22 public class ImmutableDescriptor implements Descriptor JavaDoc {
23     private static final long serialVersionUID = 8853308591080540165L;
24
25     /**
26      * The names of the fields in this ImmutableDescriptor with their
27      * original case. The names must be in alphabetical order as determined
28      * by {@link String#CASE_INSENSITIVE_ORDER}.
29      */

30     private final String JavaDoc[] names;
31     /**
32      * The values of the fields in this ImmutableDescriptor. The
33      * elements in this array match the corresponding elements in the
34      * {@code names} array.
35      */

36     private final Object JavaDoc[] values;
37     
38     private transient int hashCode = -1;
39
40     /**
41      * An empty descriptor.
42      */

43     public static final ImmutableDescriptor JavaDoc EMPTY_DESCRIPTOR =
44             new ImmutableDescriptor JavaDoc();
45     
46     /**
47      * Construct a descriptor containing the given fields and values.
48      *
49      * @throws IllegalArgumentException if either array is null, or
50      * if the arrays have different sizes, or
51      * if a field name is null or empty, or if the same field name
52      * appears more than once.
53      */

54     public ImmutableDescriptor(String JavaDoc[] fieldNames, Object JavaDoc[] fieldValues) {
55         this(makeMap(fieldNames, fieldValues));
56     }
57     
58     /**
59      * Construct a descriptor containing the given fields. Each String
60      * must be of the form {@code fieldName=fieldValue}. The field name
61      * ends at the first {@code =} character; for example if the String
62      * is {@code a=b=c} then the field name is {@code a} and its value
63      * is {@code b=c}.
64      *
65      * @throws IllegalArgumentException if the parameter is null, or
66      * if a field name is empty, or if the same field name appears
67      * more than once, or if one of the strings does not contain
68      * an {@code =} character.
69      */

70     public ImmutableDescriptor(String JavaDoc... fields) {
71         this(makeMap(fields));
72     }
73
74     /**
75      * <p>Construct a descriptor where the names and values of the fields
76      * are the keys and values of the given Map.</p>
77      *
78      * @throws IllegalArgumentException if the parameter is null, or
79      * if a field name is null or empty, or if the same field name appears
80      * more than once (which can happen because field names are not case
81      * sensitive).
82      */

83     public ImmutableDescriptor(Map JavaDoc<String JavaDoc, ?> fields) {
84         if (fields == null)
85             throw new IllegalArgumentException JavaDoc("Null Map");
86         SortedMap JavaDoc<String JavaDoc, Object JavaDoc> map =
87                 new TreeMap JavaDoc<String JavaDoc, Object JavaDoc>(String.CASE_INSENSITIVE_ORDER);
88         for (Map.Entry JavaDoc<String JavaDoc, ?> entry : fields.entrySet()) {
89             String JavaDoc name = entry.getKey();
90             if (name == null || name.equals(""))
91                 throw new IllegalArgumentException JavaDoc("Empty or null field name");
92             if (map.containsKey(name))
93                 throw new IllegalArgumentException JavaDoc("Duplicate name: " + name);
94             map.put(name, entry.getValue());
95         }
96         int size = map.size();
97         this.names = map.keySet().toArray(new String JavaDoc[size]);
98         this.values = map.values().toArray(new Object JavaDoc[size]);
99     }
100     
101     /**
102      * This method can replace a deserialized instance of this
103      * class with another instance. For example, it might replace
104      * a deserialized empty ImmutableDescriptor with
105      * {@link #EMPTY_DESCRIPTOR}.
106      *
107      * @return the replacement object, which may be {@code this}.
108      *
109      * @throws InvalidObjectException if the read object has invalid fields.
110      */

111     private Object JavaDoc readResolve() throws InvalidObjectException JavaDoc {
112         if (names.length == 0 && getClass() == ImmutableDescriptor JavaDoc.class)
113             return EMPTY_DESCRIPTOR;
114
115         boolean bad = false;
116         if (names == null || values == null || names.length != values.length)
117             bad = true;
118         if (!bad) {
119             final Comparator JavaDoc<String JavaDoc> compare = String.CASE_INSENSITIVE_ORDER;
120             String JavaDoc lastName = ""; // also catches illegal null name
121
for (int i = 0; i < names.length; i++) {
122                 if (names[i] == null ||
123                         compare.compare(lastName, names[i]) >= 0) {
124                     bad = true;
125                     break;
126                 }
127                 lastName = names[i];
128             }
129         }
130         if (bad)
131             throw new InvalidObjectException JavaDoc("Bad names or values");
132
133         return this;
134     }
135     
136     private static SortedMap JavaDoc<String JavaDoc, ?> makeMap(String JavaDoc[] fieldNames,
137                                                 Object JavaDoc[] fieldValues) {
138         if (fieldNames == null || fieldValues == null)
139             throw new IllegalArgumentException JavaDoc("Null array parameter");
140         if (fieldNames.length != fieldValues.length)
141             throw new IllegalArgumentException JavaDoc("Different size arrays");
142         SortedMap JavaDoc<String JavaDoc, Object JavaDoc> map =
143                 new TreeMap JavaDoc<String JavaDoc, Object JavaDoc>(String.CASE_INSENSITIVE_ORDER);
144         for (int i = 0; i < fieldNames.length; i++) {
145             String JavaDoc name = fieldNames[i];
146             if (name == null || name.equals(""))
147                 throw new IllegalArgumentException JavaDoc("Empty or null field name");
148             Object JavaDoc old = map.put(name, fieldValues[i]);
149             if (old != null) {
150                 throw new IllegalArgumentException JavaDoc("Duplicate field name: " +
151                                                    name);
152             }
153         }
154         return map;
155     }
156
157     private static SortedMap JavaDoc<String JavaDoc, ?> makeMap(String JavaDoc[] fields) {
158         if (fields == null)
159             throw new IllegalArgumentException JavaDoc("Null fields parameter");
160         String JavaDoc[] fieldNames = new String JavaDoc[fields.length];
161         String JavaDoc[] fieldValues = new String JavaDoc[fields.length];
162         for (int i = 0; i < fields.length; i++) {
163             String JavaDoc field = fields[i];
164             int eq = field.indexOf('=');
165             if (eq < 0) {
166                 throw new IllegalArgumentException JavaDoc("Missing = character: " +
167                                                    field);
168             }
169             fieldNames[i] = field.substring(0, eq);
170             // makeMap will catch the case where the name is empty
171
fieldValues[i] = field.substring(eq + 1);
172         }
173         return makeMap(fieldNames, fieldValues);
174     }
175     
176     /**
177      * <p>Return an {@code ImmutableDescriptor} whose contents are the union of
178      * the given descriptors. Every field name that appears in any of
179      * the descriptors will appear in the result with the
180      * value that it has when the method is called. Subsequent changes
181      * to any of the descriptors do not affect the ImmutableDescriptor
182      * returned here.</p>
183      *
184      * <p>In the simplest case, there is only one descriptor and the
185      * returned {@code ImmutableDescriptor} is a copy of its fields at the
186      * time this method is called:</p>
187      *
188      * <pre>
189      * Descriptor d = something();
190      * ImmutableDescriptor copy = ImmutableDescriptor.union(d);
191      * </pre>
192      *
193      * @param descriptors the descriptors to be combined. Any of the
194      * descriptors can be null, in which case it is skipped.
195      *
196      * @return an {@code ImmutableDescriptor} that is the union of the given
197      * descriptors. The returned object may be identical to one of the
198      * input descriptors if it is an ImmutableDescriptor that contains all of
199      * the required fields.
200      *
201      * @throws IllegalArgumentException if two Descriptors contain the
202      * same field name with different associated values. Primitive array
203      * values are considered the same if they are of the same type with
204      * the same elements. Object array values are considered the same if
205      * {@link Arrays#deepEquals(Object[],Object[])} returns true.
206      */

207     public static ImmutableDescriptor JavaDoc union(Descriptor JavaDoc... descriptors) {
208         // Optimize the case where exactly one Descriptor is non-Empty
209
// and it is immutable - we can just return it.
210
int index = findNonEmpty(descriptors, 0);
211         if (index < 0)
212             return EMPTY_DESCRIPTOR;
213         if (descriptors[index] instanceof ImmutableDescriptor JavaDoc
214                 && findNonEmpty(descriptors, index + 1) < 0)
215             return (ImmutableDescriptor JavaDoc) descriptors[index];
216
217         Map JavaDoc<String JavaDoc, Object JavaDoc> map =
218             new TreeMap JavaDoc<String JavaDoc, Object JavaDoc>(String.CASE_INSENSITIVE_ORDER);
219         ImmutableDescriptor JavaDoc biggestImmutable = EMPTY_DESCRIPTOR;
220         for (Descriptor JavaDoc d : descriptors) {
221             if (d != null) {
222                 String JavaDoc[] names;
223                 if (d instanceof ImmutableDescriptor JavaDoc) {
224                     ImmutableDescriptor JavaDoc id = (ImmutableDescriptor JavaDoc) d;
225                     names = id.names;
226                     if (id.getClass() == ImmutableDescriptor JavaDoc.class
227                             && names.length > biggestImmutable.names.length)
228                         biggestImmutable = id;
229                 } else
230                     names = d.getFieldNames();
231                 for (String JavaDoc n : names) {
232                     Object JavaDoc v = d.getFieldValue(n);
233                     Object JavaDoc old = map.put(n, v);
234                     if (old != null) {
235                         boolean equal;
236                         if (old.getClass().isArray()) {
237                             equal = Arrays.deepEquals(new Object JavaDoc[] {old},
238                                                       new Object JavaDoc[] {v});
239                         } else
240                             equal = old.equals(v);
241                         if (!equal) {
242                             final String JavaDoc msg =
243                                 "Inconsistent values for descriptor field " +
244                                 n + ": " + old + " :: " + v;
245                             throw new IllegalArgumentException JavaDoc(msg);
246                         }
247                     }
248                 }
249             }
250         }
251         if (biggestImmutable.names.length == map.size())
252             return biggestImmutable;
253         return new ImmutableDescriptor JavaDoc(map);
254     }
255     
256     private static boolean isEmpty(Descriptor JavaDoc d) {
257         if (d == null)
258             return true;
259         else if (d instanceof ImmutableDescriptor JavaDoc)
260             return ((ImmutableDescriptor JavaDoc) d).names.length == 0;
261         else
262             return (d.getFieldNames().length == 0);
263     }
264     
265     private static int findNonEmpty(Descriptor JavaDoc[] ds, int start) {
266         for (int i = start; i < ds.length; i++) {
267             if (!isEmpty(ds[i]))
268                 return i;
269         }
270         return -1;
271     }
272
273     private int fieldIndex(String JavaDoc name) {
274         return Arrays.binarySearch(names, name, String.CASE_INSENSITIVE_ORDER);
275     }
276     
277     public final Object JavaDoc getFieldValue(String JavaDoc fieldName) {
278         checkIllegalFieldName(fieldName);
279         int i = fieldIndex(fieldName);
280         if (i < 0)
281             return null;
282         Object JavaDoc v = values[i];
283         if (v == null || !v.getClass().isArray())
284             return v;
285         if (v instanceof Object JavaDoc[])
286             return ((Object JavaDoc[]) v).clone();
287         // clone the primitive array, could use an 8-way if/else here
288
int len = Array.getLength(v);
289         Object JavaDoc a = Array.newInstance(v.getClass().getComponentType(), len);
290         System.arraycopy(v, 0, a, 0, len);
291         return a;
292     }
293
294     public final String JavaDoc[] getFields() {
295         String JavaDoc[] result = new String JavaDoc[names.length];
296         for (int i = 0; i < result.length; i++) {
297             Object JavaDoc value = values[i];
298             if (value == null)
299                 value = "";
300             else if (!(value instanceof String JavaDoc))
301                 value = "(" + value + ")";
302             result[i] = names[i] + "=" + value;
303         }
304         return result;
305     }
306
307     public final Object JavaDoc[] getFieldValues(String JavaDoc... fieldNames) {
308         if (fieldNames == null)
309             return values.clone();
310         Object JavaDoc[] result = new Object JavaDoc[fieldNames.length];
311         for (int i = 0; i < fieldNames.length; i++) {
312             String JavaDoc name = fieldNames[i];
313             if (name != null && !name.equals(""))
314                 result[i] = getFieldValue(name);
315         }
316         return result;
317     }
318
319     public final String JavaDoc[] getFieldNames() {
320         return names.clone();
321     }
322
323     /**
324      * Compares this descriptor to the given object. The objects are equal if
325      * the given object is also a Descriptor, and if the two Descriptors have
326      * the same field names (possibly differing in case) and the same
327      * associated values. The respective values for a field in the two
328      * Descriptors are equal if the following conditions hold:</p>
329      *
330      * <ul>
331      * <li>If one value is null then the other must be too.</li>
332      * <li>If one value is a primitive array then the other must be a primitive
333      * array of the same type with the same elements.</li>
334      * <li>If one value is an object array then the other must be too and
335      * {@link Arrays#deepEquals(Object[],Object[])} must return true.</li>
336      * <li>Otherwise {@link Object#equals(Object)} must return true.</li>
337      * </ul>
338      *
339      * @param o the object to compare with.
340      *
341      * @return {@code true} if the objects are the same; {@code false}
342      * otherwise.
343      *
344      */

345     // Note: this Javadoc is copied from javax.management.Descriptor
346
// due to 6369229.
347
public boolean equals(Object JavaDoc o) {
348         if (o == this)
349             return true;
350         if (!(o instanceof Descriptor JavaDoc))
351             return false;
352         String JavaDoc[] onames;
353         if (o instanceof ImmutableDescriptor JavaDoc) {
354             onames = ((ImmutableDescriptor JavaDoc) o).names;
355         } else {
356             onames = ((Descriptor JavaDoc) o).getFieldNames();
357             Arrays.sort(onames, String.CASE_INSENSITIVE_ORDER);
358         }
359         if (names.length != onames.length)
360             return false;
361         for (int i = 0; i < names.length; i++) {
362             if (!names[i].equalsIgnoreCase(onames[i]))
363                 return false;
364         }
365         Object JavaDoc[] ovalues;
366         if (o instanceof ImmutableDescriptor JavaDoc)
367             ovalues = ((ImmutableDescriptor JavaDoc) o).values;
368         else
369             ovalues = ((Descriptor JavaDoc) o).getFieldValues(onames);
370         return Arrays.deepEquals(values, ovalues);
371     }
372     
373     /**
374      * <p>Returns the hash code value for this descriptor. The hash
375      * code is computed as the sum of the hash codes for each field in
376      * the descriptor. The hash code of a field with name {@code n}
377      * and value {@code v} is {@code n.toLowerCase().hashCode() ^ h}.
378      * Here {@code h} is the hash code of {@code v}, computed as
379      * follows:</p>
380      *
381      * <ul>
382      * <li>If {@code v} is null then {@code h} is 0.</li>
383      * <li>If {@code v} is a primitive array then {@code h} is computed using
384      * the appropriate overloading of {@code java.util.Arrays.hashCode}.</li>
385      * <li>If {@code v} is an object array then {@code h} is computed using
386      * {@link Arrays#deepHashCode(Object[])}.</li>
387      * <li>Otherwise {@code h} is {@code v.hashCode()}.</li>
388      * </ul>
389      *
390      * @return A hash code value for this object.
391      *
392      */

393     // Note: this Javadoc is copied from javax.management.Descriptor
394
// due to 6369229.
395
public int hashCode() {
396         if (hashCode == -1) {
397             int hash = 0;
398             for (int i = 0; i < names.length; i++) {
399                 Object JavaDoc v = values[i];
400                 int h;
401                 if (v == null)
402                     h = 0;
403                 else if (v instanceof Object JavaDoc[])
404                     h = Arrays.deepHashCode((Object JavaDoc[]) v);
405                 else if (v.getClass().isArray()) {
406                     h = Arrays.deepHashCode(new Object JavaDoc[] {v}) - 31;
407                     // hashcode of a list containing just v is
408
// v.hashCode() + 31, see List.hashCode()
409
} else
410                     h = v.hashCode();
411                 hash += names[i].toLowerCase().hashCode() ^ h;
412             }
413             hashCode = hash;
414         }
415         return hashCode;
416     }
417     
418     public String JavaDoc toString() {
419         StringBuilder JavaDoc sb = new StringBuilder JavaDoc("{");
420         for (int i = 0; i < names.length; i++) {
421             if (i > 0)
422                 sb.append(", ");
423             sb.append(names[i]).append("=");
424             Object JavaDoc v = values[i];
425             if (v != null && v.getClass().isArray()) {
426                 String JavaDoc s = Arrays.deepToString(new Object JavaDoc[] {v});
427                 s = s.substring(1, s.length() - 1); // remove [...]
428
v = s;
429             }
430             sb.append(String.valueOf(v));
431         }
432         return sb.append("}").toString();
433     }
434
435     /**
436      * Returns true if all of the fields have legal values given their
437      * names. This method always returns true, but a subclass can
438      * override it to return false when appropriate.
439      *
440      * @return true if the values are legal.
441      *
442      * @exception RuntimeOperationsException if the validity checking fails.
443      * The method returns false if the descriptor is not valid, but throws
444      * this exception if the attempt to determine validity fails.
445      */

446     public boolean isValid() {
447         return true;
448     }
449     
450     /**
451      * <p>Returns a descriptor which is equal to this descriptor.
452      * Changes to the returned descriptor will have no effect on this
453      * descriptor, and vice versa.</p>
454      *
455      * <p>This method returns the object on which it is called.
456      * A subclass can override it
457      * to return another object provided the contract is respected.
458      *
459      * @exception RuntimeOperationsException for illegal value for field Names
460      * or field Values.
461      * If the descriptor construction fails for any reason, this exception will
462      * be thrown.
463      */

464     public Descriptor JavaDoc clone() {
465         return this;
466     }
467
468     /**
469      * This operation is unsupported since this class is immutable. If
470      * this call would change a mutable descriptor with the same contents,
471      * then a {@link RuntimeOperationsException} wrapping an
472      * {@link UnsupportedOperationException} is thrown. Otherwise,
473      * the behavior is the same as it would be for a mutable descriptor:
474      * either an exception is thrown because of illegal parameters, or
475      * there is no effect.
476      */

477     public final void setFields(String JavaDoc[] fieldNames, Object JavaDoc[] fieldValues)
478         throws RuntimeOperationsException JavaDoc {
479         if (fieldNames == null || fieldValues == null)
480             illegal("Null argument");
481         if (fieldNames.length != fieldValues.length)
482             illegal("Different array sizes");
483         for (int i = 0; i < fieldNames.length; i++)
484             checkIllegalFieldName(fieldNames[i]);
485         for (int i = 0; i < fieldNames.length; i++)
486             setField(fieldNames[i], fieldValues[i]);
487     }
488
489     /**
490      * This operation is unsupported since this class is immutable. If
491      * this call would change a mutable descriptor with the same contents,
492      * then a {@link RuntimeOperationsException} wrapping an
493      * {@link UnsupportedOperationException} is thrown. Otherwise,
494      * the behavior is the same as it would be for a mutable descriptor:
495      * either an exception is thrown because of illegal parameters, or
496      * there is no effect.
497      */

498     public final void setField(String JavaDoc fieldName, Object JavaDoc fieldValue)
499         throws RuntimeOperationsException JavaDoc {
500         checkIllegalFieldName(fieldName);
501         int i = fieldIndex(fieldName);
502         if (i < 0)
503             unsupported();
504         Object JavaDoc value = values[i];
505         if ((value == null) ?
506                 (fieldValue != null) :
507                 !value.equals(fieldValue))
508             unsupported();
509     }
510
511     /**
512      * Removes a field from the descriptor.
513      *
514      * @param fieldName String name of the field to be removed.
515      * If the field name is illegal or the field is not found,
516      * no exception is thrown.
517      *
518      * @exception RuntimeOperationsException if a field of the given name
519      * exists and the descriptor is immutable. The wrapped exception will
520      * be an {@link UnsupportedOperationException}.
521      */

522     public final void removeField(String JavaDoc fieldName) {
523         if (fieldName != null && fieldIndex(fieldName) >= 0)
524             unsupported();
525     }
526     
527     static Descriptor JavaDoc nonNullDescriptor(Descriptor JavaDoc d) {
528         if (d == null)
529             return EMPTY_DESCRIPTOR;
530         else
531             return d;
532     }
533
534     private static void checkIllegalFieldName(String JavaDoc name) {
535         if (name == null || name.equals(""))
536             illegal("Null or empty field name");
537     }
538     
539     private static void unsupported() {
540         UnsupportedOperationException JavaDoc uoe =
541             new UnsupportedOperationException JavaDoc("Descriptor is read-only");
542         throw new RuntimeOperationsException JavaDoc(uoe);
543     }
544
545     private static void illegal(String JavaDoc message) {
546         IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc(message);
547         throw new RuntimeOperationsException JavaDoc(iae);
548     }
549 }
550
Popular Tags