Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormatImpl.java
41153 views
1
/*
2
* Copyright (c) 2000, 2020, 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.imageio.metadata;
27
28
import java.util.ArrayList;
29
import java.util.Collection;
30
import java.util.HashMap;
31
import java.util.Iterator;
32
import java.util.List;
33
import java.util.Locale;
34
import java.util.Map;
35
import java.util.MissingResourceException;
36
import java.util.ResourceBundle;
37
import javax.imageio.ImageTypeSpecifier;
38
import com.sun.imageio.plugins.common.StandardMetadataFormat;
39
40
/**
41
* A concrete class providing a reusable implementation of the
42
* {@code IIOMetadataFormat} interface. In addition, a static
43
* instance representing the standard, plug-in neutral
44
* {@code javax_imageio_1.0} format is provided by the
45
* {@code getStandardFormatInstance} method.
46
*
47
* <p> In order to supply localized descriptions of elements and
48
* attributes, a {@code ResourceBundle} with a base name of
49
* {@code this.getClass().getName() + "Resources"} should be
50
* supplied via the usual mechanism used by
51
* {@code ResourceBundle.getBundle}. Briefly, the subclasser
52
* supplies one or more additional classes according to a naming
53
* convention (by default, the fully-qualified name of the subclass
54
* extending {@code IIMetadataFormatImpl}, plus the string
55
* "Resources", plus the country, language, and variant codes
56
* separated by underscores). At run time, calls to
57
* {@code getElementDescription} or
58
* {@code getAttributeDescription} will attempt to load such
59
* classes dynamically according to the supplied locale, and will use
60
* either the element name, or the element name followed by a '/'
61
* character followed by the attribute name as a key. This key will
62
* be supplied to the {@code ResourceBundle}'s
63
* {@code getString} method, and the resulting localized
64
* description of the node or attribute is returned.
65
*
66
* <p> The subclass may supply a different base name for the resource
67
* bundles using the {@code setResourceBaseName} method.
68
*
69
* <p> A subclass may choose its own localization mechanism, if so
70
* desired, by overriding the supplied implementations of
71
* {@code getElementDescription} and
72
* {@code getAttributeDescription}.
73
*
74
* @see ResourceBundle#getBundle(String,Locale)
75
*
76
*/
77
public abstract class IIOMetadataFormatImpl implements IIOMetadataFormat {
78
79
/**
80
* A {@code String} constant containing the standard format
81
* name, {@code "javax_imageio_1.0"}.
82
*/
83
public static final String standardMetadataFormatName =
84
"javax_imageio_1.0";
85
86
private static IIOMetadataFormat standardFormat = null;
87
88
private String resourceBaseName = this.getClass().getName() + "Resources";
89
90
private String rootName;
91
92
// Element name (String) -> Element
93
private HashMap<String, Element> elementMap = new HashMap<>();
94
95
class Element {
96
String elementName;
97
98
int childPolicy;
99
int minChildren = 0;
100
int maxChildren = 0;
101
102
// Child names (Strings)
103
List<String> childList = new ArrayList<>();
104
105
// Parent names (Strings)
106
List<String> parentList = new ArrayList<>();
107
108
// List of attribute names in the order they were added
109
List<String> attrList = new ArrayList<>();
110
// Attr name (String) -> Attribute
111
Map<String, Attribute> attrMap = new HashMap<>();
112
113
ObjectValue<?> objectValue;
114
}
115
116
class Attribute {
117
String attrName;
118
119
int valueType = VALUE_ARBITRARY;
120
int dataType;
121
boolean required;
122
String defaultValue = null;
123
124
// enumeration
125
List<String> enumeratedValues;
126
127
// range
128
String minValue;
129
String maxValue;
130
131
// list
132
int listMinLength;
133
int listMaxLength;
134
}
135
136
class ObjectValue<T> {
137
int valueType = VALUE_NONE;
138
// ? extends T So that ObjectValue<Object> can take Class<?>
139
Class<? extends T> classType = null;
140
T defaultValue = null;
141
142
// Meaningful only if valueType == VALUE_ENUMERATION
143
List<? extends T> enumeratedValues = null;
144
145
// Meaningful only if valueType == VALUE_RANGE
146
Comparable<? super T> minValue = null;
147
Comparable<? super T> maxValue = null;
148
149
// Meaningful only if valueType == VALUE_LIST
150
int arrayMinLength = 0;
151
int arrayMaxLength = 0;
152
}
153
154
/**
155
* Constructs a blank {@code IIOMetadataFormatImpl} instance,
156
* with a given root element name and child policy (other than
157
* {@code CHILD_POLICY_REPEAT}). Additional elements, and
158
* their attributes and {@code Object} reference information
159
* may be added using the various {@code add} methods.
160
*
161
* @param rootName the name of the root element.
162
* @param childPolicy one of the {@code CHILD_POLICY_*} constants,
163
* other than {@code CHILD_POLICY_REPEAT}.
164
*
165
* @exception IllegalArgumentException if {@code rootName} is
166
* {@code null}.
167
* @exception IllegalArgumentException if {@code childPolicy} is
168
* not one of the predefined constants.
169
*/
170
public IIOMetadataFormatImpl(String rootName,
171
int childPolicy) {
172
if (rootName == null) {
173
throw new IllegalArgumentException("rootName == null!");
174
}
175
if (childPolicy < CHILD_POLICY_EMPTY ||
176
childPolicy > CHILD_POLICY_MAX ||
177
childPolicy == CHILD_POLICY_REPEAT) {
178
throw new IllegalArgumentException("Invalid value for childPolicy!");
179
}
180
181
this.rootName = rootName;
182
183
Element root = new Element();
184
root.elementName = rootName;
185
root.childPolicy = childPolicy;
186
187
elementMap.put(rootName, root);
188
}
189
190
/**
191
* Constructs a blank {@code IIOMetadataFormatImpl} instance,
192
* with a given root element name and a child policy of
193
* {@code CHILD_POLICY_REPEAT}. Additional elements, and
194
* their attributes and {@code Object} reference information
195
* may be added using the various {@code add} methods.
196
*
197
* @param rootName the name of the root element.
198
* @param minChildren the minimum number of children of the node.
199
* @param maxChildren the maximum number of children of the node.
200
*
201
* @exception IllegalArgumentException if {@code rootName} is
202
* {@code null}.
203
* @exception IllegalArgumentException if {@code minChildren}
204
* is negative or larger than {@code maxChildren}.
205
*/
206
public IIOMetadataFormatImpl(String rootName,
207
int minChildren,
208
int maxChildren) {
209
if (rootName == null) {
210
throw new IllegalArgumentException("rootName == null!");
211
}
212
if (minChildren < 0) {
213
throw new IllegalArgumentException("minChildren < 0!");
214
}
215
if (minChildren > maxChildren) {
216
throw new IllegalArgumentException("minChildren > maxChildren!");
217
}
218
219
Element root = new Element();
220
root.elementName = rootName;
221
root.childPolicy = CHILD_POLICY_REPEAT;
222
root.minChildren = minChildren;
223
root.maxChildren = maxChildren;
224
225
this.rootName = rootName;
226
elementMap.put(rootName, root);
227
}
228
229
/**
230
* Sets a new base name for locating {@code ResourceBundle}s
231
* containing descriptions of elements and attributes for this
232
* format.
233
*
234
* <p> Prior to the first time this method is called, the base
235
* name will be equal to
236
* {@code this.getClass().getName() + "Resources"}.
237
*
238
* @param resourceBaseName a {@code String} containing the new
239
* base name.
240
*
241
* @exception IllegalArgumentException if
242
* {@code resourceBaseName} is {@code null}.
243
*
244
* @see #getResourceBaseName
245
*/
246
protected void setResourceBaseName(String resourceBaseName) {
247
if (resourceBaseName == null) {
248
throw new IllegalArgumentException("resourceBaseName == null!");
249
}
250
this.resourceBaseName = resourceBaseName;
251
}
252
253
/**
254
* Returns the currently set base name for locating
255
* {@code ResourceBundle}s.
256
*
257
* @return a {@code String} containing the base name.
258
*
259
* @see #setResourceBaseName
260
*/
261
protected String getResourceBaseName() {
262
return resourceBaseName;
263
}
264
265
/**
266
* Utility method for locating an element.
267
*
268
* @param mustAppear if {@code true}, throw an
269
* {@code IllegalArgumentException} if no such node exists;
270
* if {@code false}, just return null.
271
*/
272
private Element getElement(String elementName, boolean mustAppear) {
273
if (mustAppear && (elementName == null)) {
274
throw new IllegalArgumentException("element name is null!");
275
}
276
Element element = elementMap.get(elementName);
277
if (mustAppear && (element == null)) {
278
throw new IllegalArgumentException("No such element: " +
279
elementName);
280
}
281
return element;
282
}
283
284
private Element getElement(String elementName) {
285
return getElement(elementName, true);
286
}
287
288
// Utility method for locating an attribute
289
private Attribute getAttribute(String elementName, String attrName) {
290
Element element = getElement(elementName);
291
Attribute attr = element.attrMap.get(attrName);
292
if (attr == null) {
293
throw new IllegalArgumentException("No such attribute \"" +
294
attrName + "\"!");
295
}
296
return attr;
297
}
298
299
// Setup
300
301
/**
302
* Adds a new element type to this metadata document format with a
303
* child policy other than {@code CHILD_POLICY_REPEAT}.
304
*
305
* @param elementName the name of the new element.
306
* @param parentName the name of the element that will be the
307
* parent of the new element.
308
* @param childPolicy one of the {@code CHILD_POLICY_*}
309
* constants, other than {@code CHILD_POLICY_REPEAT},
310
* indicating the child policy of the new element.
311
*
312
* @exception IllegalArgumentException if {@code parentName}
313
* is {@code null}, or is not a legal element name for this
314
* format.
315
* @exception IllegalArgumentException if {@code childPolicy}
316
* is not one of the predefined constants.
317
*/
318
protected void addElement(String elementName,
319
String parentName,
320
int childPolicy) {
321
Element parent = getElement(parentName);
322
if (childPolicy < CHILD_POLICY_EMPTY ||
323
childPolicy > CHILD_POLICY_MAX ||
324
childPolicy == CHILD_POLICY_REPEAT) {
325
throw new IllegalArgumentException
326
("Invalid value for childPolicy!");
327
}
328
329
Element element = new Element();
330
element.elementName = elementName;
331
element.childPolicy = childPolicy;
332
333
parent.childList.add(elementName);
334
element.parentList.add(parentName);
335
336
elementMap.put(elementName, element);
337
}
338
339
/**
340
* Adds a new element type to this metadata document format with a
341
* child policy of {@code CHILD_POLICY_REPEAT}.
342
*
343
* @param elementName the name of the new element.
344
* @param parentName the name of the element that will be the
345
* parent of the new element.
346
* @param minChildren the minimum number of children of the node.
347
* @param maxChildren the maximum number of children of the node.
348
*
349
* @exception IllegalArgumentException if {@code parentName}
350
* is {@code null}, or is not a legal element name for this
351
* format.
352
* @exception IllegalArgumentException if {@code minChildren}
353
* is negative or larger than {@code maxChildren}.
354
*/
355
protected void addElement(String elementName,
356
String parentName,
357
int minChildren,
358
int maxChildren) {
359
Element parent = getElement(parentName);
360
if (minChildren < 0) {
361
throw new IllegalArgumentException("minChildren < 0!");
362
}
363
if (minChildren > maxChildren) {
364
throw new IllegalArgumentException("minChildren > maxChildren!");
365
}
366
367
Element element = new Element();
368
element.elementName = elementName;
369
element.childPolicy = CHILD_POLICY_REPEAT;
370
element.minChildren = minChildren;
371
element.maxChildren = maxChildren;
372
373
parent.childList.add(elementName);
374
element.parentList.add(parentName);
375
376
elementMap.put(elementName, element);
377
}
378
379
/**
380
* Adds an existing element to the list of legal children for a
381
* given parent node type.
382
*
383
* @param parentName the name of the element that will be the
384
* new parent of the element.
385
* @param elementName the name of the element to be added as a
386
* child.
387
*
388
* @exception IllegalArgumentException if {@code elementName}
389
* is {@code null}, or is not a legal element name for this
390
* format.
391
* @exception IllegalArgumentException if {@code parentName}
392
* is {@code null}, or is not a legal element name for this
393
* format.
394
*/
395
protected void addChildElement(String elementName, String parentName) {
396
Element parent = getElement(parentName);
397
Element element = getElement(elementName);
398
parent.childList.add(elementName);
399
element.parentList.add(parentName);
400
}
401
402
/**
403
* Removes an element from the format. If no element with the
404
* given name was present, nothing happens and no exception is
405
* thrown.
406
*
407
* @param elementName the name of the element to be removed.
408
*/
409
protected void removeElement(String elementName) {
410
Element element = getElement(elementName, false);
411
if (element != null) {
412
for (String parentName : element.parentList) {
413
Element parent = getElement(parentName, false);
414
if (parent != null) {
415
parent.childList.remove(elementName);
416
}
417
}
418
elementMap.remove(elementName);
419
}
420
}
421
422
/**
423
* Adds a new attribute to a previously defined element that may
424
* be set to an arbitrary value.
425
*
426
* @param elementName the name of the element.
427
* @param attrName the name of the attribute being added.
428
* @param dataType the data type (string format) of the attribute,
429
* one of the {@code DATATYPE_*} constants.
430
* @param required {@code true} if the attribute must be present.
431
* @param defaultValue the default value for the attribute, or
432
* {@code null}.
433
*
434
* @exception IllegalArgumentException if {@code elementName}
435
* is {@code null}, or is not a legal element name for this
436
* format.
437
* @exception IllegalArgumentException if {@code attrName} is
438
* {@code null}.
439
* @exception IllegalArgumentException if {@code dataType} is
440
* not one of the predefined constants.
441
*/
442
protected void addAttribute(String elementName,
443
String attrName,
444
int dataType,
445
boolean required,
446
String defaultValue) {
447
Element element = getElement(elementName);
448
if (attrName == null) {
449
throw new IllegalArgumentException("attrName == null!");
450
}
451
if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
452
throw new IllegalArgumentException("Invalid value for dataType!");
453
}
454
455
Attribute attr = new Attribute();
456
attr.attrName = attrName;
457
attr.valueType = VALUE_ARBITRARY;
458
attr.dataType = dataType;
459
attr.required = required;
460
attr.defaultValue = defaultValue;
461
462
element.attrList.add(attrName);
463
element.attrMap.put(attrName, attr);
464
}
465
466
/**
467
* Adds a new attribute to a previously defined element that will
468
* be defined by a set of enumerated values.
469
*
470
* @param elementName the name of the element.
471
* @param attrName the name of the attribute being added.
472
* @param dataType the data type (string format) of the attribute,
473
* one of the {@code DATATYPE_*} constants.
474
* @param required {@code true} if the attribute must be present.
475
* @param defaultValue the default value for the attribute, or
476
* {@code null}.
477
* @param enumeratedValues a {@code List} of
478
* {@code String}s containing the legal values for the
479
* attribute.
480
*
481
* @exception IllegalArgumentException if {@code elementName}
482
* is {@code null}, or is not a legal element name for this
483
* format.
484
* @exception IllegalArgumentException if {@code attrName} is
485
* {@code null}.
486
* @exception IllegalArgumentException if {@code dataType} is
487
* not one of the predefined constants.
488
* @exception IllegalArgumentException if
489
* {@code enumeratedValues} is {@code null}.
490
* @exception IllegalArgumentException if
491
* {@code enumeratedValues} does not contain at least one
492
* entry.
493
* @exception IllegalArgumentException if
494
* {@code enumeratedValues} contains an element that is not a
495
* {@code String} or is {@code null}.
496
*/
497
protected void addAttribute(String elementName,
498
String attrName,
499
int dataType,
500
boolean required,
501
String defaultValue,
502
List<String> enumeratedValues) {
503
Element element = getElement(elementName);
504
if (attrName == null) {
505
throw new IllegalArgumentException("attrName == null!");
506
}
507
if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
508
throw new IllegalArgumentException("Invalid value for dataType!");
509
}
510
if (enumeratedValues == null) {
511
throw new IllegalArgumentException("enumeratedValues == null!");
512
}
513
if (enumeratedValues.size() == 0) {
514
throw new IllegalArgumentException("enumeratedValues is empty!");
515
}
516
for (Object o : enumeratedValues) {
517
if (o == null) {
518
throw new IllegalArgumentException
519
("enumeratedValues contains a null!");
520
}
521
if (!(o instanceof String)) {
522
throw new IllegalArgumentException
523
("enumeratedValues contains a non-String value!");
524
}
525
}
526
527
Attribute attr = new Attribute();
528
attr.attrName = attrName;
529
attr.valueType = VALUE_ENUMERATION;
530
attr.dataType = dataType;
531
attr.required = required;
532
attr.defaultValue = defaultValue;
533
attr.enumeratedValues = enumeratedValues;
534
535
element.attrList.add(attrName);
536
element.attrMap.put(attrName, attr);
537
}
538
539
/**
540
* Adds a new attribute to a previously defined element that will
541
* be defined by a range of values.
542
*
543
* @param elementName the name of the element.
544
* @param attrName the name of the attribute being added.
545
* @param dataType the data type (string format) of the attribute,
546
* one of the {@code DATATYPE_*} constants.
547
* @param required {@code true} if the attribute must be present.
548
* @param defaultValue the default value for the attribute, or
549
* {@code null}.
550
* @param minValue the smallest (inclusive or exclusive depending
551
* on the value of {@code minInclusive}) legal value for the
552
* attribute, as a {@code String}.
553
* @param maxValue the largest (inclusive or exclusive depending
554
* on the value of {@code minInclusive}) legal value for the
555
* attribute, as a {@code String}.
556
* @param minInclusive {@code true} if {@code minValue}
557
* is inclusive.
558
* @param maxInclusive {@code true} if {@code maxValue}
559
* is inclusive.
560
*
561
* @exception IllegalArgumentException if {@code elementName}
562
* is {@code null}, or is not a legal element name for this
563
* format.
564
* @exception IllegalArgumentException if {@code attrName} is
565
* {@code null}.
566
* @exception IllegalArgumentException if {@code dataType} is
567
* not one of the predefined constants.
568
*/
569
protected void addAttribute(String elementName,
570
String attrName,
571
int dataType,
572
boolean required,
573
String defaultValue,
574
String minValue,
575
String maxValue,
576
boolean minInclusive,
577
boolean maxInclusive) {
578
Element element = getElement(elementName);
579
if (attrName == null) {
580
throw new IllegalArgumentException("attrName == null!");
581
}
582
if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
583
throw new IllegalArgumentException("Invalid value for dataType!");
584
}
585
586
Attribute attr = new Attribute();
587
attr.attrName = attrName;
588
attr.valueType = VALUE_RANGE;
589
if (minInclusive) {
590
attr.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
591
}
592
if (maxInclusive) {
593
attr.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
594
}
595
attr.dataType = dataType;
596
attr.required = required;
597
attr.defaultValue = defaultValue;
598
attr.minValue = minValue;
599
attr.maxValue = maxValue;
600
601
element.attrList.add(attrName);
602
element.attrMap.put(attrName, attr);
603
}
604
605
/**
606
* Adds a new attribute to a previously defined element that will
607
* be defined by a list of values.
608
*
609
* @param elementName the name of the element.
610
* @param attrName the name of the attribute being added.
611
* @param dataType the data type (string format) of the attribute,
612
* one of the {@code DATATYPE_*} constants.
613
* @param required {@code true} if the attribute must be present.
614
* @param listMinLength the smallest legal number of list items.
615
* @param listMaxLength the largest legal number of list items.
616
*
617
* @exception IllegalArgumentException if {@code elementName}
618
* is {@code null}, or is not a legal element name for this
619
* format.
620
* @exception IllegalArgumentException if {@code attrName} is
621
* {@code null}.
622
* @exception IllegalArgumentException if {@code dataType} is
623
* not one of the predefined constants.
624
* @exception IllegalArgumentException if
625
* {@code listMinLength} is negative or larger than
626
* {@code listMaxLength}.
627
*/
628
protected void addAttribute(String elementName,
629
String attrName,
630
int dataType,
631
boolean required,
632
int listMinLength,
633
int listMaxLength) {
634
Element element = getElement(elementName);
635
if (attrName == null) {
636
throw new IllegalArgumentException("attrName == null!");
637
}
638
if (dataType < DATATYPE_STRING || dataType > DATATYPE_DOUBLE) {
639
throw new IllegalArgumentException("Invalid value for dataType!");
640
}
641
if (listMinLength < 0 || listMinLength > listMaxLength) {
642
throw new IllegalArgumentException("Invalid list bounds!");
643
}
644
645
Attribute attr = new Attribute();
646
attr.attrName = attrName;
647
attr.valueType = VALUE_LIST;
648
attr.dataType = dataType;
649
attr.required = required;
650
attr.listMinLength = listMinLength;
651
attr.listMaxLength = listMaxLength;
652
653
element.attrList.add(attrName);
654
element.attrMap.put(attrName, attr);
655
}
656
657
/**
658
* Adds a new attribute to a previously defined element that will
659
* be defined by the enumerated values {@code TRUE} and
660
* {@code FALSE}, with a datatype of
661
* {@code DATATYPE_BOOLEAN}.
662
*
663
* @param elementName the name of the element.
664
* @param attrName the name of the attribute being added.
665
* @param hasDefaultValue {@code true} if a default value
666
* should be present.
667
* @param defaultValue the default value for the attribute as a
668
* {@code boolean}, ignored if {@code hasDefaultValue}
669
* is {@code false}.
670
*
671
* @exception IllegalArgumentException if {@code elementName}
672
* is {@code null}, or is not a legal element name for this
673
* format.
674
* @exception IllegalArgumentException if {@code attrName} is
675
* {@code null}.
676
*/
677
protected void addBooleanAttribute(String elementName,
678
String attrName,
679
boolean hasDefaultValue,
680
boolean defaultValue) {
681
List<String> values = new ArrayList<>();
682
values.add("TRUE");
683
values.add("FALSE");
684
685
String dval = null;
686
if (hasDefaultValue) {
687
dval = defaultValue ? "TRUE" : "FALSE";
688
}
689
addAttribute(elementName,
690
attrName,
691
DATATYPE_BOOLEAN,
692
true,
693
dval,
694
values);
695
}
696
697
/**
698
* Removes an attribute from a previously defined element. If no
699
* attribute with the given name was present in the given element,
700
* nothing happens and no exception is thrown.
701
*
702
* @param elementName the name of the element.
703
* @param attrName the name of the attribute being removed.
704
*
705
* @exception IllegalArgumentException if {@code elementName}
706
* is {@code null}, or is not a legal element name for this format.
707
*/
708
protected void removeAttribute(String elementName, String attrName) {
709
Element element = getElement(elementName);
710
element.attrList.remove(attrName);
711
element.attrMap.remove(attrName);
712
}
713
714
/**
715
* Allows an {@code Object} reference of a given class type
716
* to be stored in nodes implementing the named element. The
717
* value of the {@code Object} is unconstrained other than by
718
* its class type.
719
*
720
* <p> If an {@code Object} reference was previously allowed,
721
* the previous settings are overwritten.
722
*
723
* @param elementName the name of the element.
724
* @param classType a {@code Class} variable indicating the
725
* legal class type for the object value.
726
* @param required {@code true} if an object value must be present.
727
* @param defaultValue the default value for the
728
* {@code Object} reference, or {@code null}.
729
* @param <T> the type of the object.
730
*
731
* @exception IllegalArgumentException if {@code elementName}
732
* is {@code null}, or is not a legal element name for this format.
733
*/
734
protected <T> void addObjectValue(String elementName,
735
Class<T> classType,
736
boolean required,
737
T defaultValue)
738
{
739
Element element = getElement(elementName);
740
ObjectValue<T> obj = new ObjectValue<>();
741
obj.valueType = VALUE_ARBITRARY;
742
obj.classType = classType;
743
obj.defaultValue = defaultValue;
744
745
element.objectValue = obj;
746
}
747
748
/**
749
* Allows an {@code Object} reference of a given class type
750
* to be stored in nodes implementing the named element. The
751
* value of the {@code Object} must be one of the values
752
* given by {@code enumeratedValues}.
753
*
754
* <p> If an {@code Object} reference was previously allowed,
755
* the previous settings are overwritten.
756
*
757
* @param elementName the name of the element.
758
* @param classType a {@code Class} variable indicating the
759
* legal class type for the object value.
760
* @param required {@code true} if an object value must be present.
761
* @param defaultValue the default value for the
762
* {@code Object} reference, or {@code null}.
763
* @param enumeratedValues a {@code List} of
764
* {@code Object}s containing the legal values for the
765
* object reference.
766
* @param <T> the type of the object.
767
*
768
* @exception IllegalArgumentException if {@code elementName}
769
* is {@code null}, or is not a legal element name for this format.
770
* @exception IllegalArgumentException if
771
* {@code enumeratedValues} is {@code null}.
772
* @exception IllegalArgumentException if
773
* {@code enumeratedValues} does not contain at least one
774
* entry.
775
* @exception IllegalArgumentException if
776
* {@code enumeratedValues} contains an element that is not
777
* an instance of the class type denoted by {@code classType}
778
* or is {@code null}.
779
*/
780
protected <T> void addObjectValue(String elementName,
781
Class<T> classType,
782
boolean required,
783
T defaultValue,
784
List<? extends T> enumeratedValues)
785
{
786
Element element = getElement(elementName);
787
if (enumeratedValues == null) {
788
throw new IllegalArgumentException("enumeratedValues == null!");
789
}
790
if (enumeratedValues.size() == 0) {
791
throw new IllegalArgumentException("enumeratedValues is empty!");
792
}
793
for (Object o : enumeratedValues) {
794
if (o == null) {
795
throw new IllegalArgumentException("enumeratedValues contains a null!");
796
}
797
if (!classType.isInstance(o)) {
798
throw new IllegalArgumentException("enumeratedValues contains a value not of class classType!");
799
}
800
}
801
802
ObjectValue<T> obj = new ObjectValue<>();
803
obj.valueType = VALUE_ENUMERATION;
804
obj.classType = classType;
805
obj.defaultValue = defaultValue;
806
obj.enumeratedValues = enumeratedValues;
807
808
element.objectValue = obj;
809
}
810
811
/**
812
* Allows an {@code Object} reference of a given class type
813
* to be stored in nodes implementing the named element. The
814
* value of the {@code Object} must be within the range given
815
* by {@code minValue} and {@code maxValue}.
816
* Furthermore, the class type must implement the
817
* {@code Comparable} interface.
818
*
819
* <p> If an {@code Object} reference was previously allowed,
820
* the previous settings are overwritten.
821
*
822
* @param elementName the name of the element.
823
* @param classType a {@code Class} variable indicating the
824
* legal class type for the object value.
825
* @param defaultValue the default value for the
826
* @param minValue the smallest (inclusive or exclusive depending
827
* on the value of {@code minInclusive}) legal value for the
828
* object value, as a {@code String}.
829
* @param maxValue the largest (inclusive or exclusive depending
830
* on the value of {@code minInclusive}) legal value for the
831
* object value, as a {@code String}.
832
* @param minInclusive {@code true} if {@code minValue}
833
* is inclusive.
834
* @param maxInclusive {@code true} if {@code maxValue}
835
* is inclusive.
836
* @param <T> the type of the object.
837
*
838
* @exception IllegalArgumentException if {@code elementName}
839
* is {@code null}, or is not a legal element name for this
840
* format.
841
*/
842
protected <T extends Object & Comparable<? super T>> void
843
addObjectValue(String elementName,
844
Class<T> classType,
845
T defaultValue,
846
Comparable<? super T> minValue,
847
Comparable<? super T> maxValue,
848
boolean minInclusive,
849
boolean maxInclusive)
850
{
851
Element element = getElement(elementName);
852
ObjectValue<T> obj = new ObjectValue<>();
853
obj.valueType = VALUE_RANGE;
854
if (minInclusive) {
855
obj.valueType |= VALUE_RANGE_MIN_INCLUSIVE_MASK;
856
}
857
if (maxInclusive) {
858
obj.valueType |= VALUE_RANGE_MAX_INCLUSIVE_MASK;
859
}
860
obj.classType = classType;
861
obj.defaultValue = defaultValue;
862
obj.minValue = minValue;
863
obj.maxValue = maxValue;
864
865
element.objectValue = obj;
866
}
867
868
/**
869
* Allows an {@code Object} reference of a given class type
870
* to be stored in nodes implementing the named element. The
871
* value of the {@code Object} must an array of objects of
872
* class type given by {@code classType}, with at least
873
* {@code arrayMinLength} and at most
874
* {@code arrayMaxLength} elements.
875
*
876
* <p> If an {@code Object} reference was previously allowed,
877
* the previous settings are overwritten.
878
*
879
* @param elementName the name of the element.
880
* @param classType a {@code Class} variable indicating the
881
* legal class type for the object value.
882
* @param arrayMinLength the smallest legal length for the array.
883
* @param arrayMaxLength the largest legal length for the array.
884
*
885
* @exception IllegalArgumentException if {@code elementName} is
886
* not a legal element name for this format.
887
*/
888
protected void addObjectValue(String elementName,
889
Class<?> classType,
890
int arrayMinLength,
891
int arrayMaxLength) {
892
Element element = getElement(elementName);
893
ObjectValue<Object> obj = new ObjectValue<>();
894
obj.valueType = VALUE_LIST;
895
obj.classType = classType;
896
obj.arrayMinLength = arrayMinLength;
897
obj.arrayMaxLength = arrayMaxLength;
898
899
element.objectValue = obj;
900
}
901
902
/**
903
* Disallows an {@code Object} reference from being stored in
904
* nodes implementing the named element.
905
*
906
* @param elementName the name of the element.
907
*
908
* @exception IllegalArgumentException if {@code elementName} is
909
* not a legal element name for this format.
910
*/
911
protected void removeObjectValue(String elementName) {
912
Element element = getElement(elementName);
913
element.objectValue = null;
914
}
915
916
// Utility method
917
918
// Methods from IIOMetadataFormat
919
920
// Root
921
922
public String getRootName() {
923
return rootName;
924
}
925
926
// Multiplicity
927
928
public abstract boolean canNodeAppear(String elementName,
929
ImageTypeSpecifier imageType);
930
931
public int getElementMinChildren(String elementName) {
932
Element element = getElement(elementName);
933
if (element.childPolicy != CHILD_POLICY_REPEAT) {
934
throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
935
}
936
return element.minChildren;
937
}
938
939
public int getElementMaxChildren(String elementName) {
940
Element element = getElement(elementName);
941
if (element.childPolicy != CHILD_POLICY_REPEAT) {
942
throw new IllegalArgumentException("Child policy not CHILD_POLICY_REPEAT!");
943
}
944
return element.maxChildren;
945
}
946
947
private String getResource(String key, Locale locale) {
948
if (locale == null) {
949
locale = Locale.getDefault();
950
}
951
952
/**
953
* Per the class documentation, resource bundles, including localized ones
954
* are intended to be delivered by the subclasser - ie supplier of the
955
* metadataformat. For the standard format and all standard plugins that
956
* is the JDK. For 3rd party plugins that they will supply their own.
957
* This includes plugins bundled with applets/applications.
958
* In all cases this means it is sufficient to search for those resource
959
* in the module that is providing the MetadataFormatImpl subclass.
960
*/
961
try {
962
ResourceBundle bundle = ResourceBundle.getBundle(resourceBaseName, locale,
963
this.getClass().getModule());
964
return bundle.getString(key);
965
} catch (MissingResourceException e) {
966
return null;
967
}
968
}
969
970
/**
971
* Returns a {@code String} containing a description of the
972
* named element, or {@code null}. The description will be
973
* localized for the supplied {@code Locale} if possible.
974
*
975
* <p> The default implementation will first locate a
976
* {@code ResourceBundle} using the current resource base
977
* name set by {@code setResourceBaseName} and the supplied
978
* {@code Locale}, using the fallback mechanism described in
979
* the comments for {@code ResourceBundle.getBundle}. If a
980
* {@code ResourceBundle} is found, the element name will be
981
* used as a key to its {@code getString} method, and the
982
* result returned. If no {@code ResourceBundle} is found,
983
* or no such key is present, {@code null} will be returned.
984
*
985
* <p> If {@code locale} is {@code null}, the current
986
* default {@code Locale} returned by {@code Locale.getLocale}
987
* will be used.
988
*
989
* @param elementName the name of the element.
990
* @param locale the {@code Locale} for which localization
991
* will be attempted.
992
*
993
* @return the element description.
994
*
995
* @exception IllegalArgumentException if {@code elementName}
996
* is {@code null}, or is not a legal element name for this format.
997
*
998
* @see #setResourceBaseName
999
*/
1000
public String getElementDescription(String elementName,
1001
Locale locale) {
1002
Element element = getElement(elementName);
1003
return getResource(elementName, locale);
1004
}
1005
1006
// Children
1007
1008
public int getChildPolicy(String elementName) {
1009
Element element = getElement(elementName);
1010
return element.childPolicy;
1011
}
1012
1013
public String[] getChildNames(String elementName) {
1014
Element element = getElement(elementName);
1015
if (element.childPolicy == CHILD_POLICY_EMPTY) {
1016
return null;
1017
}
1018
return element.childList.toArray(new String[0]);
1019
}
1020
1021
// Attributes
1022
1023
public String[] getAttributeNames(String elementName) {
1024
Element element = getElement(elementName);
1025
List<String> names = element.attrList;
1026
1027
String[] result = new String[names.size()];
1028
return names.toArray(result);
1029
}
1030
1031
public int getAttributeValueType(String elementName, String attrName) {
1032
Attribute attr = getAttribute(elementName, attrName);
1033
return attr.valueType;
1034
}
1035
1036
public int getAttributeDataType(String elementName, String attrName) {
1037
Attribute attr = getAttribute(elementName, attrName);
1038
return attr.dataType;
1039
}
1040
1041
public boolean isAttributeRequired(String elementName, String attrName) {
1042
Attribute attr = getAttribute(elementName, attrName);
1043
return attr.required;
1044
}
1045
1046
public String getAttributeDefaultValue(String elementName,
1047
String attrName) {
1048
Attribute attr = getAttribute(elementName, attrName);
1049
return attr.defaultValue;
1050
}
1051
1052
public String[] getAttributeEnumerations(String elementName,
1053
String attrName) {
1054
Attribute attr = getAttribute(elementName, attrName);
1055
if (attr.valueType != VALUE_ENUMERATION) {
1056
throw new IllegalArgumentException
1057
("Attribute not an enumeration!");
1058
}
1059
1060
List<String> values = attr.enumeratedValues;
1061
String[] result = new String[values.size()];
1062
return values.toArray(result);
1063
}
1064
1065
public String getAttributeMinValue(String elementName, String attrName) {
1066
Attribute attr = getAttribute(elementName, attrName);
1067
if (attr.valueType != VALUE_RANGE &&
1068
attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
1069
attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
1070
attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1071
throw new IllegalArgumentException("Attribute not a range!");
1072
}
1073
1074
return attr.minValue;
1075
}
1076
1077
public String getAttributeMaxValue(String elementName, String attrName) {
1078
Attribute attr = getAttribute(elementName, attrName);
1079
if (attr.valueType != VALUE_RANGE &&
1080
attr.valueType != VALUE_RANGE_MIN_INCLUSIVE &&
1081
attr.valueType != VALUE_RANGE_MAX_INCLUSIVE &&
1082
attr.valueType != VALUE_RANGE_MIN_MAX_INCLUSIVE) {
1083
throw new IllegalArgumentException("Attribute not a range!");
1084
}
1085
1086
return attr.maxValue;
1087
}
1088
1089
public int getAttributeListMinLength(String elementName, String attrName) {
1090
Attribute attr = getAttribute(elementName, attrName);
1091
if (attr.valueType != VALUE_LIST) {
1092
throw new IllegalArgumentException("Attribute not a list!");
1093
}
1094
1095
return attr.listMinLength;
1096
}
1097
1098
public int getAttributeListMaxLength(String elementName, String attrName) {
1099
Attribute attr = getAttribute(elementName, attrName);
1100
if (attr.valueType != VALUE_LIST) {
1101
throw new IllegalArgumentException("Attribute not a list!");
1102
}
1103
1104
return attr.listMaxLength;
1105
}
1106
1107
/**
1108
* Returns a {@code String} containing a description of the
1109
* named attribute, or {@code null}. The description will be
1110
* localized for the supplied {@code Locale} if possible.
1111
*
1112
* <p> The default implementation will first locate a
1113
* {@code ResourceBundle} using the current resource base
1114
* name set by {@code setResourceBaseName} and the supplied
1115
* {@code Locale}, using the fallback mechanism described in
1116
* the comments for {@code ResourceBundle.getBundle}. If a
1117
* {@code ResourceBundle} is found, the element name followed
1118
* by a "/" character followed by the attribute name
1119
* ({@code elementName + "/" + attrName}) will be used as a
1120
* key to its {@code getString} method, and the result
1121
* returned. If no {@code ResourceBundle} is found, or no
1122
* such key is present, {@code null} will be returned.
1123
*
1124
* <p> If {@code locale} is {@code null}, the current
1125
* default {@code Locale} returned by {@code Locale.getLocale}
1126
* will be used.
1127
*
1128
* @param elementName the name of the element.
1129
* @param attrName the name of the attribute.
1130
* @param locale the {@code Locale} for which localization
1131
* will be attempted, or {@code null}.
1132
*
1133
* @return the attribute description.
1134
*
1135
* @exception IllegalArgumentException if {@code elementName}
1136
* is {@code null}, or is not a legal element name for this format.
1137
* @exception IllegalArgumentException if {@code attrName} is
1138
* {@code null} or is not a legal attribute name for this
1139
* element.
1140
*
1141
* @see #setResourceBaseName
1142
*/
1143
public String getAttributeDescription(String elementName,
1144
String attrName,
1145
Locale locale) {
1146
Element element = getElement(elementName);
1147
if (attrName == null) {
1148
throw new IllegalArgumentException("attrName == null!");
1149
}
1150
Attribute attr = element.attrMap.get(attrName);
1151
if (attr == null) {
1152
throw new IllegalArgumentException("No such attribute!");
1153
}
1154
1155
String key = elementName + "/" + attrName;
1156
return getResource(key, locale);
1157
}
1158
1159
private ObjectValue<?> getObjectValue(String elementName) {
1160
Element element = getElement(elementName);
1161
ObjectValue<?> objv = element.objectValue;
1162
if (objv == null) {
1163
throw new IllegalArgumentException("No object within element " +
1164
elementName + "!");
1165
}
1166
return objv;
1167
}
1168
1169
public int getObjectValueType(String elementName) {
1170
Element element = getElement(elementName);
1171
ObjectValue<?> objv = element.objectValue;
1172
if (objv == null) {
1173
return VALUE_NONE;
1174
}
1175
return objv.valueType;
1176
}
1177
1178
public Class<?> getObjectClass(String elementName) {
1179
ObjectValue<?> objv = getObjectValue(elementName);
1180
return objv.classType;
1181
}
1182
1183
public Object getObjectDefaultValue(String elementName) {
1184
ObjectValue<?> objv = getObjectValue(elementName);
1185
return objv.defaultValue;
1186
}
1187
1188
public Object[] getObjectEnumerations(String elementName) {
1189
ObjectValue<?> objv = getObjectValue(elementName);
1190
if (objv.valueType != VALUE_ENUMERATION) {
1191
throw new IllegalArgumentException("Not an enumeration!");
1192
}
1193
List<?> vlist = objv.enumeratedValues;
1194
Object[] values = new Object[vlist.size()];
1195
return vlist.toArray(values);
1196
}
1197
1198
public Comparable<?> getObjectMinValue(String elementName) {
1199
ObjectValue<?> objv = getObjectValue(elementName);
1200
if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1201
throw new IllegalArgumentException("Not a range!");
1202
}
1203
return objv.minValue;
1204
}
1205
1206
public Comparable<?> getObjectMaxValue(String elementName) {
1207
ObjectValue<?> objv = getObjectValue(elementName);
1208
if ((objv.valueType & VALUE_RANGE) != VALUE_RANGE) {
1209
throw new IllegalArgumentException("Not a range!");
1210
}
1211
return objv.maxValue;
1212
}
1213
1214
public int getObjectArrayMinLength(String elementName) {
1215
ObjectValue<?> objv = getObjectValue(elementName);
1216
if (objv.valueType != VALUE_LIST) {
1217
throw new IllegalArgumentException("Not a list!");
1218
}
1219
return objv.arrayMinLength;
1220
}
1221
1222
public int getObjectArrayMaxLength(String elementName) {
1223
ObjectValue<?> objv = getObjectValue(elementName);
1224
if (objv.valueType != VALUE_LIST) {
1225
throw new IllegalArgumentException("Not a list!");
1226
}
1227
return objv.arrayMaxLength;
1228
}
1229
1230
// Standard format descriptor
1231
1232
private static synchronized void createStandardFormat() {
1233
if (standardFormat == null) {
1234
standardFormat = new StandardMetadataFormat();
1235
}
1236
}
1237
1238
/**
1239
* Returns an {@code IIOMetadataFormat} object describing the
1240
* standard, plug-in neutral {@code javax.imageio_1.0}
1241
* metadata document format described in the comment of the
1242
* {@code javax.imageio.metadata} package.
1243
*
1244
* @return a predefined {@code IIOMetadataFormat} instance.
1245
*/
1246
public static IIOMetadataFormat getStandardFormatInstance() {
1247
createStandardFormat();
1248
return standardFormat;
1249
}
1250
}
1251
1252