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+
26+package javax.management;
27+
28+import com.sun.jmx.mbeanserver.Util;
29+import java.io.InvalidObjectException;
30+import java.lang.reflect.Array;
31+import java.util.Arrays;
32+import java.util.Comparator;
33+import java.util.Map;
34+import java.util.SortedMap;
35+import java.util.TreeMap;
36+
37+/**
38+ * An immutable descriptor.
39+ * @since 1.6
40+ */
41+public 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+}