History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/management/ImmutableDescriptor.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/management/ImmutableDescriptor.java+556−0
@@ -0,0 +1,556 @@
1/*
2 * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.management;
27
28import com.sun.jmx.mbeanserver.Util;
29import java.io.InvalidObjectException;
30import java.lang.reflect.Array;
31import java.util.Arrays;
32import java.util.Comparator;
33import java.util.Map;
34import java.util.SortedMap;
35import java.util.TreeMap;
36
37/**
38 * An immutable descriptor.
39 * @since 1.6
40 */
41public class ImmutableDescriptor implements Descriptor {
42 private static final long serialVersionUID = 8853308591080540165L;
43
44 /**
45 * The names of the fields in this ImmutableDescriptor with their
46 * original case. The names must be in alphabetical order as determined
47 * by {@link String#CASE_INSENSITIVE_ORDER}.
48 */
49 private final String[] names;
50 /**
51 * The values of the fields in this ImmutableDescriptor. The
52 * elements in this array match the corresponding elements in the
53 * {@code names} array.
54 */
55 private final Object[] values;
56
57 private transient int hashCode = -1;
58
59 /**
60 * An empty descriptor.
61 */
62 public static final ImmutableDescriptor EMPTY_DESCRIPTOR =
63 new ImmutableDescriptor();
64
65 /**
66 * Construct a descriptor containing the given fields and values.
67 *
68 * @throws IllegalArgumentException if either array is null, or
69 * if the arrays have different sizes, or
70 * if a field name is null or empty, or if the same field name
71 * appears more than once.
72 */
73 public ImmutableDescriptor(String[] fieldNames, Object[] fieldValues) {
74 this(makeMap(fieldNames, fieldValues));
75 }
76
77 /**
78 * Construct a descriptor containing the given fields. Each String
79 * must be of the form {@code fieldName=fieldValue}. The field name
80 * ends at the first {@code =} character; for example if the String
81 * is {@code a=b=c} then the field name is {@code a} and its value
82 * is {@code b=c}.
83 *
84 * @throws IllegalArgumentException if the parameter is null, or
85 * if a field name is empty, or if the same field name appears
86 * more than once, or if one of the strings does not contain
87 * an {@code =} character.
88 */
89 public ImmutableDescriptor(String... fields) {
90 this(makeMap(fields));
91 }
92
93 /**
94 * <p>Construct a descriptor where the names and values of the fields
95 * are the keys and values of the given Map.</p>
96 *
97 * @throws IllegalArgumentException if the parameter is null, or
98 * if a field name is null or empty, or if the same field name appears
99 * more than once (which can happen because field names are not case
100 * sensitive).
101 */
102 public ImmutableDescriptor(Map<String, ?> fields) {
103 if (fields == null)
104 throw new IllegalArgumentException("Null Map");
105 SortedMap<String, Object> map =
106 new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
107 for (Map.Entry<String, ?> entry : fields.entrySet()) {
108 String name = entry.getKey();
109 if (name == null || name.equals(""))
110 throw new IllegalArgumentException("Empty or null field name");
111 if (map.containsKey(name))
112 throw new IllegalArgumentException("Duplicate name: " + name);
113 map.put(name, entry.getValue());
114 }
115 int size = map.size();
116 this.names = map.keySet().toArray(new String[size]);
117 this.values = map.values().toArray(new Object[size]);
118 }
119
120 /**
121 * This method can replace a deserialized instance of this
122 * class with another instance. For example, it might replace
123 * a deserialized empty ImmutableDescriptor with
124 * {@link #EMPTY_DESCRIPTOR}.
125 *
126 * @return the replacement object, which may be {@code this}.
127 *
128 * @throws InvalidObjectException if the read object has invalid fields.
129 */
130 private Object readResolve() throws InvalidObjectException {
131
132 boolean bad = false;
133 if (names == null || values == null || names.length != values.length)
134 bad = true;
135 if (!bad) {
136 if (names.length == 0 && getClass() == ImmutableDescriptor.class)
137 return EMPTY_DESCRIPTOR;
138 final Comparator<String> compare = String.CASE_INSENSITIVE_ORDER;
139 String lastName = ""; // also catches illegal null name
140 for (int i = 0; i < names.length; i++) {
141 if (names[i] == null ||
142 compare.compare(lastName, names[i]) >= 0) {
143 bad = true;
144 break;
145 }
146 lastName = names[i];
147 }
148 }
149 if (bad)
150 throw new InvalidObjectException("Bad names or values");
151
152 return this;
153 }
154
155 private static SortedMap<String, ?> makeMap(String[] fieldNames,
156 Object[] fieldValues) {
157 if (fieldNames == null || fieldValues == null)
158 throw new IllegalArgumentException("Null array parameter");
159 if (fieldNames.length != fieldValues.length)
160 throw new IllegalArgumentException("Different size arrays");
161 SortedMap<String, Object> map =
162 new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
163 for (int i = 0; i < fieldNames.length; i++) {
164 String name = fieldNames[i];
165 if (name == null || name.equals(""))
166 throw new IllegalArgumentException("Empty or null field name");
167 Object old = map.put(name, fieldValues[i]);
168 if (old != null) {
169 throw new IllegalArgumentException("Duplicate field name: " +
170 name);
171 }
172 }
173 return map;
174 }
175
176 private static SortedMap<String, ?> makeMap(String[] fields) {
177 if (fields == null)
178 throw new IllegalArgumentException("Null fields parameter");
179 String[] fieldNames = new String[fields.length];
180 String[] fieldValues = new String[fields.length];
181 for (int i = 0; i < fields.length; i++) {
182 String field = fields[i];
183 int eq = field.indexOf('=');
184 if (eq < 0) {
185 throw new IllegalArgumentException("Missing = character: " +
186 field);
187 }
188 fieldNames[i] = field.substring(0, eq);
189 // makeMap will catch the case where the name is empty
190 fieldValues[i] = field.substring(eq + 1);
191 }
192 return makeMap(fieldNames, fieldValues);
193 }
194
195 /**
196 * <p>Return an {@code ImmutableDescriptor} whose contents are the union of
197 * the given descriptors. Every field name that appears in any of
198 * the descriptors will appear in the result with the
199 * value that it has when the method is called. Subsequent changes
200 * to any of the descriptors do not affect the ImmutableDescriptor
201 * returned here.</p>
202 *
203 * <p>In the simplest case, there is only one descriptor and the
204 * returned {@code ImmutableDescriptor} is a copy of its fields at the
205 * time this method is called:</p>
206 *
207 * <pre>
208 * Descriptor d = something();
209 * ImmutableDescriptor copy = ImmutableDescriptor.union(d);
210 * </pre>
211 *
212 * @param descriptors the descriptors to be combined. Any of the
213 * descriptors can be null, in which case it is skipped.
214 *
215 * @return an {@code ImmutableDescriptor} that is the union of the given
216 * descriptors. The returned object may be identical to one of the
217 * input descriptors if it is an ImmutableDescriptor that contains all of
218 * the required fields.
219 *
220 * @throws IllegalArgumentException if two Descriptors contain the
221 * same field name with different associated values. Primitive array
222 * values are considered the same if they are of the same type with
223 * the same elements. Object array values are considered the same if
224 * {@link Arrays#deepEquals(Object[],Object[])} returns true.
225 */
226 public static ImmutableDescriptor union(Descriptor... descriptors) {
227 // Optimize the case where exactly one Descriptor is non-Empty
228 // and it is immutable - we can just return it.
229 int index = findNonEmpty(descriptors, 0);
230 if (index < 0)
231 return EMPTY_DESCRIPTOR;
232 if (descriptors[index] instanceof ImmutableDescriptor
233 && findNonEmpty(descriptors, index + 1) < 0)
234 return (ImmutableDescriptor) descriptors[index];
235
236 Map<String, Object> map =
237 new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
238 ImmutableDescriptor biggestImmutable = EMPTY_DESCRIPTOR;
239 for (Descriptor d : descriptors) {
240 if (d != null) {
241 String[] names;
242 if (d instanceof ImmutableDescriptor) {
243 ImmutableDescriptor id = (ImmutableDescriptor) d;
244 names = id.names;
245 if (id.getClass() == ImmutableDescriptor.class
246 && names.length > biggestImmutable.names.length)
247 biggestImmutable = id;
248 } else
249 names = d.getFieldNames();
250 for (String n : names) {
251 Object v = d.getFieldValue(n);
252 Object old = map.put(n, v);
253 if (old != null) {
254 boolean equal;
255 if (old.getClass().isArray()) {
256 equal = Arrays.deepEquals(new Object[] {old},
257 new Object[] {v});
258 } else
259 equal = old.equals(v);
260 if (!equal) {
261 final String msg =
262 "Inconsistent values for descriptor field " +
263 n + ": " + old + " :: " + v;
264 throw new IllegalArgumentException(msg);
265 }
266 }
267 }
268 }
269 }
270 if (biggestImmutable.names.length == map.size())
271 return biggestImmutable;
272 return new ImmutableDescriptor(map);
273 }
274
275 private static boolean isEmpty(Descriptor d) {
276 if (d == null)
277 return true;
278 else if (d instanceof ImmutableDescriptor)
279 return ((ImmutableDescriptor) d).names.length == 0;
280 else
281 return (d.getFieldNames().length == 0);
282 }
283
284 private static int findNonEmpty(Descriptor[] ds, int start) {
285 for (int i = start; i < ds.length; i++) {
286 if (!isEmpty(ds[i]))
287 return i;
288 }
289 return -1;
290 }
291
292 private int fieldIndex(String name) {
293 return Arrays.binarySearch(names, name, String.CASE_INSENSITIVE_ORDER);
294 }
295
296 public final Object getFieldValue(String fieldName) {
297 checkIllegalFieldName(fieldName);
298 int i = fieldIndex(fieldName);
299 if (i < 0)
300 return null;
301 Object v = values[i];
302 if (v == null || !v.getClass().isArray())
303 return v;
304 if (v instanceof Object[])
305 return ((Object[]) v).clone();
306 // clone the primitive array, could use an 8-way if/else here
307 int len = Array.getLength(v);
308 Object a = Array.newInstance(v.getClass().getComponentType(), len);
309 System.arraycopy(v, 0, a, 0, len);
310 return a;
311 }
312
313 public final String[] getFields() {
314 String[] result = new String[names.length];
315 for (int i = 0; i < result.length; i++) {
316 Object value = values[i];
317 if (value == null)
318 value = "";
319 else if (!(value instanceof String))
320 value = "(" + value + ")";
321 result[i] = names[i] + "=" + value;
322 }
323 return result;
324 }
325
326 public final Object[] getFieldValues(String... fieldNames) {
327 if (fieldNames == null)
328 return values.clone();
329 Object[] result = new Object[fieldNames.length];
330 for (int i = 0; i < fieldNames.length; i++) {
331 String name = fieldNames[i];
332 if (name != null && !name.equals(""))
333 result[i] = getFieldValue(name);
334 }
335 return result;
336 }
337
338 public final String[] getFieldNames() {
339 return names.clone();
340 }
341
342 /**
343 * Compares this descriptor to the given object. The objects are equal if
344 * the given object is also a Descriptor, and if the two Descriptors have
345 * the same field names (possibly differing in case) and the same
346 * associated values. The respective values for a field in the two
347 * Descriptors are equal if the following conditions hold:</p>
348 *
349 * <ul>
350 * <li>If one value is null then the other must be too.</li>
351 * <li>If one value is a primitive array then the other must be a primitive
352 * array of the same type with the same elements.</li>
353 * <li>If one value is an object array then the other must be too and
354 * {@link Arrays#deepEquals(Object[],Object[])} must return true.</li>
355 * <li>Otherwise {@link Object#equals(Object)} must return true.</li>
356 * </ul>
357 *
358 * @param o the object to compare with.
359 *
360 * @return {@code true} if the objects are the same; {@code false}
361 * otherwise.
362 *
363 */
364 // Note: this Javadoc is copied from javax.management.Descriptor
365 // due to 6369229.
366 @Override
367 public boolean equals(Object o) {
368 if (o == this)
369 return true;
370 if (!(o instanceof Descriptor))
371 return false;
372 String[] onames;
373 if (o instanceof ImmutableDescriptor) {
374 onames = ((ImmutableDescriptor) o).names;
375 } else {
376 onames = ((Descriptor) o).getFieldNames();
377 Arrays.sort(onames, String.CASE_INSENSITIVE_ORDER);
378 }
379 if (names.length != onames.length)
380 return false;
381 for (int i = 0; i < names.length; i++) {
382 if (!names[i].equalsIgnoreCase(onames[i]))
383 return false;
384 }
385 Object[] ovalues;
386 if (o instanceof ImmutableDescriptor)
387 ovalues = ((ImmutableDescriptor) o).values;
388 else
389 ovalues = ((Descriptor) o).getFieldValues(onames);
390 return Arrays.deepEquals(values, ovalues);
391 }
392
393 /**
394 * <p>Returns the hash code value for this descriptor. The hash
395 * code is computed as the sum of the hash codes for each field in
396 * the descriptor. The hash code of a field with name {@code n}
397 * and value {@code v} is {@code n.toLowerCase().hashCode() ^ h}.
398 * Here {@code h} is the hash code of {@code v}, computed as
399 * follows:</p>
400 *
401 * <ul>
402 * <li>If {@code v} is null then {@code h} is 0.</li>
403 * <li>If {@code v} is a primitive array then {@code h} is computed using
404 * the appropriate overloading of {@code java.util.Arrays.hashCode}.</li>
405 * <li>If {@code v} is an object array then {@code h} is computed using
406 * {@link Arrays#deepHashCode(Object[])}.</li>
407 * <li>Otherwise {@code h} is {@code v.hashCode()}.</li>
408 * </ul>
409 *
410 * @return A hash code value for this object.
411 *
412 */
413 // Note: this Javadoc is copied from javax.management.Descriptor
414 // due to 6369229.
415 @Override
416 public int hashCode() {
417 if (hashCode == -1) {
418 hashCode = Util.hashCode(names, values);
419 }
420 return hashCode;
421 }
422
423 @Override
424 public String toString() {
425 StringBuilder sb = new StringBuilder("{");
426 for (int i = 0; i < names.length; i++) {
427 if (i > 0)
428 sb.append(", ");
429 sb.append(names[i]).append("=");
430 Object v = values[i];
431 if (v != null && v.getClass().isArray()) {
432 String s = Arrays.deepToString(new Object[] {v});
433 s = s.substring(1, s.length() - 1); // remove [...]
434 v = s;
435 }
436 sb.append(String.valueOf(v));
437 }
438 return sb.append("}").toString();
439 }
440
441 /**
442 * Returns true if all of the fields have legal values given their
443 * names. This method always returns true, but a subclass can
444 * override it to return false when appropriate.
445 *
446 * @return true if the values are legal.
447 *
448 * @exception RuntimeOperationsException if the validity checking fails.
449 * The method returns false if the descriptor is not valid, but throws
450 * this exception if the attempt to determine validity fails.
451 */
452 public boolean isValid() {
453 return true;
454 }
455
456 /**
457 * <p>Returns a descriptor which is equal to this descriptor.
458 * Changes to the returned descriptor will have no effect on this
459 * descriptor, and vice versa.</p>
460 *
461 * <p>This method returns the object on which it is called.
462 * A subclass can override it
463 * to return another object provided the contract is respected.
464 *
465 * @exception RuntimeOperationsException for illegal value for field Names
466 * or field Values.
467 * If the descriptor construction fails for any reason, this exception will
468 * be thrown.
469 */
470 @Override
471 public Descriptor clone() {
472 return this;
473 }
474
475 /**
476 * This operation is unsupported since this class is immutable. If
477 * this call would change a mutable descriptor with the same contents,
478 * then a {@link RuntimeOperationsException} wrapping an
479 * {@link UnsupportedOperationException} is thrown. Otherwise,
480 * the behavior is the same as it would be for a mutable descriptor:
481 * either an exception is thrown because of illegal parameters, or
482 * there is no effect.
483 */
484 public final void setFields(String[] fieldNames, Object[] fieldValues)
485 throws RuntimeOperationsException {
486 if (fieldNames == null || fieldValues == null)
487 illegal("Null argument");
488 if (fieldNames.length != fieldValues.length)
489 illegal("Different array sizes");
490 for (int i = 0; i < fieldNames.length; i++)
491 checkIllegalFieldName(fieldNames[i]);
492 for (int i = 0; i < fieldNames.length; i++)
493 setField(fieldNames[i], fieldValues[i]);
494 }
495
496 /**
497 * This operation is unsupported since this class is immutable. If
498 * this call would change a mutable descriptor with the same contents,
499 * then a {@link RuntimeOperationsException} wrapping an
500 * {@link UnsupportedOperationException} is thrown. Otherwise,
501 * the behavior is the same as it would be for a mutable descriptor:
502 * either an exception is thrown because of illegal parameters, or
503 * there is no effect.
504 */
505 public final void setField(String fieldName, Object fieldValue)
506 throws RuntimeOperationsException {
507 checkIllegalFieldName(fieldName);
508 int i = fieldIndex(fieldName);
509 if (i < 0)
510 unsupported();
511 Object value = values[i];
512 if ((value == null) ?
513 (fieldValue != null) :
514 !value.equals(fieldValue))
515 unsupported();
516 }
517
518 /**
519 * Removes a field from the descriptor.
520 *
521 * @param fieldName String name of the field to be removed.
522 * If the field name is illegal or the field is not found,
523 * no exception is thrown.
524 *
525 * @exception RuntimeOperationsException if a field of the given name
526 * exists and the descriptor is immutable. The wrapped exception will
527 * be an {@link UnsupportedOperationException}.
528 */
529 public final void removeField(String fieldName) {
530 if (fieldName != null && fieldIndex(fieldName) >= 0)
531 unsupported();
532 }
533
534 static Descriptor nonNullDescriptor(Descriptor d) {
535 if (d == null)
536 return EMPTY_DESCRIPTOR;
537 else
538 return d;
539 }
540
541 private static void checkIllegalFieldName(String name) {
542 if (name == null || name.equals(""))
543 illegal("Null or empty field name");
544 }
545
546 private static void unsupported() {
547 UnsupportedOperationException uoe =
548 new UnsupportedOperationException("Descriptor is read-only");
549 throw new RuntimeOperationsException(uoe);
550 }
551
552 private static void illegal(String message) {
553 IllegalArgumentException iae = new IllegalArgumentException(message);
554 throw new RuntimeOperationsException(iae);
555 }
556}