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