1 /* 2 * Copyright (c) 2000, 2014, 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 */ 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 */ 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 */ 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 */ 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 */ 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 283 private Element getElement(String elementName) { 284 return getElement(elementName, true); 285 } 286 287 // Utility method for locating an attribute 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 927 public String getRootName() { 928 return rootName; 929 } 930 931 // Multiplicity 932 933 public abstract boolean canNodeAppear(String elementName, 934 ImageTypeSpecifier imageType); 935 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 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 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 */ 1022 public String getElementDescription(String elementName, 1023 Locale locale) { 1024 Element element = getElement(elementName); 1025 return getResource(elementName, locale); 1026 } 1027 1028 // Children 1029 1030 public int getChildPolicy(String elementName) { 1031 Element element = getElement(elementName); 1032 return element.childPolicy; 1033 } 1034 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 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 1053 public int getAttributeValueType(String elementName, String attrName) { 1054 Attribute attr = getAttribute(elementName, attrName); 1055 return attr.valueType; 1056 } 1057 1058 public int getAttributeDataType(String elementName, String attrName) { 1059 Attribute attr = getAttribute(elementName, attrName); 1060 return attr.dataType; 1061 } 1062 1063 public boolean isAttributeRequired(String elementName, String attrName) { 1064 Attribute attr = getAttribute(elementName, attrName); 1065 return attr.required; 1066 } 1067 1068 public String getAttributeDefaultValue(String elementName, 1069 String attrName) { 1070 Attribute attr = getAttribute(elementName, attrName); 1071 return attr.defaultValue; 1072 } 1073 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 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 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 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 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 */ 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 1182 private ObjectValue getObjectValue(String elementName) { 1183 Element element = getElement(elementName); 1184 ObjectValue objv = element.objectValue; 1185 if (objv == null) { 1186 throw new IllegalArgumentException("No object within element " + 1187 elementName + "!"); 1188 } 1189 return objv; 1190 } 1191 1192 public int getObjectValueType(String elementName) { 1193 Element element = getElement(elementName); 1194 ObjectValue objv = element.objectValue; 1195 if (objv == null) { 1196 return VALUE_NONE; 1197 } 1198 return objv.valueType; 1199 } 1200 1201 public Class<?> getObjectClass(String elementName) { 1202 ObjectValue objv = getObjectValue(elementName); 1203 return objv.classType; 1204 } 1205 1206 public Object getObjectDefaultValue(String elementName) { 1207 ObjectValue objv = getObjectValue(elementName); 1208 return objv.defaultValue; 1209 } 1210 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 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 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 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 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 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 */ 1269 public static IIOMetadataFormat getStandardFormatInstance() { 1270 createStandardFormat(); 1271 return standardFormat; 1272 } 1273 }