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