Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/management/ImmutableDescriptor.java
38829 views
1
/*
2
* Copyright (c) 2004, 2013, 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:
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
}
557
558